1

I have a vídeo where I want to cut 2 parts and join them together but I want to put a part that happens at a later time first...

For example cut seconds 23-30 and 45-50 and put the part with seconds 45-50 first.

I used this:

ffmpeg -i input.mp4 -vf "select='between(t,45,50)+between(t,23,30)',scale=720:-1" "output.avi"

But even thoug I put the 45-50 part first in the command the output.avi plays the 23-30 part first in the output.avi video.

How would I join this 2 videos and have the later part played first, I also tried it with filter_complex but I also have to scale the video and it looks like filter_complex and vf can't be used together.

If possible I want to use only 1 reencoding to not loose much quality. Thanks in advance.

1

Here's the thing about modern video—most formats are actually designed presuming a varying frame rate. We like to think of them like digital filmstrips, but that model rarely works when we're streaming over a network or capturing from many kinds of devices. So, every frame has what's called a Presentation Time Stamp, or PTS.

The problem here is that, while you do indeed concatenate in that order, the PTS for each frame never changes. You can actually manually set the PTS of a frame with the setpts filter, which is definitely something to look into as panning video can get much more complicated than this, and it's deliciously flexible; but may require some reading to understand all of the parameters.

The fields you'll need to know are PTS, which is the presentation timestamp before the filter is run; STARTPTS, which is the presentation timestamp of the first frame rendered, and TB, which stands for Time Base and is basically the ratio between a PTS "tick" and a second; so if you know that your first clip is going to be five seconds long, and you want your second clip to start immediately afterward, you will want to add 5.0/TB to your new PTS.

So, I suggest (PTS-STARTPTS)+(5/TB) for your second clip; but this isn't the only way to determine it and feel free to adjust it if the result doesn't feel right to you.

We're going to use a complex filter here, as we're splitting our stream into two separate streams and concatenating them at the end. (Complex streams actually aren't all that bad, but do take a little getting used to.) One of the big differences with a complex filter is that you generally start each command by specifying a stream for it to use, and end each command specifying a new stream to write to, both in square brackets. When we begin with [0:v], that means to take the first input (input number zero from file input.mp4) and grab its video stream. What comes at the end can be used to identify it later when you want to filter it again.

Lastly, it helps to use the -map tag to tell FFmpeg which stream it is writing from. It's critical, as if FFmpeg wasn't aware of which one to use you could end up with literally any stream or combination thereof in your file; and an invocation can take a while to complete.

Your final command:

ffmpeg -i input.mp4 -filter_complex " \
    [0:v]select='between(t,45,50)'[v1], \
    [0:v]select='between(t,23,30)'[v2], \
    [v2]setpts='(PTS-STARTPTS)+(5/TB)'[v2], \
    [v1][v2]concat[outv], \
    [outv]scale=720:-1[outv]" \
    -map [outv] output.avi

Remember that if you want to use this for any other file, you will need to adjust the (5/TB) part to match the end of your first clip.

While I'm a very experienced programmer, I'm relatively new to FFmpeg in-depth; so it's possible that someone else here will have more details for you to consider.

8
  • Thank you very much. I tried this and I'm reporting my results: The output video starts with the first frame of the second video and gets stuck there until it reaches the original time of the second vídeo 45 sec, than it starts playing the second video and after that the first, so after 45 everything starts playing as intended but how I get rid of that 45 useless seconds in the beginning of the video where it shows the first frame of the second vídeo? Thanks. – Ricardo Bohner May 1 at 14:05
  • Something else, when I save the output video as gif than there are no 45 seconds with stuck frame in the beginning. But when I save it as avi or mp4 there is... – Ricardo Bohner May 1 at 14:07
  • There was also a gap of a couple of seconds from the second to the first video to start playing but then I remembered something from another topic here in this site and I changed setpts='(PTS-STARTPTS)+(5/TB)' to setpts='N/FRAME_RATE/TB and that fixed the gap... – Ricardo Bohner May 1 at 14:19
  • Yeah, we've gone a pretty long way from taping film together in a darkroom; it's all about those PTS timestamps now. Think of it as how far along the strip the frame is; whereas N is the frame number, and TB is closer to the speed of the film. If you bump into an issue with gaps between frames, and your video twiddling its thumbs, the problem is almost certainly in the PTS. You need to set it to the exact time for that frame. Admittedly, as capable as FFmpeg is, it's pretty advanced stuff and the parameter names tend to throw people. – Michael Macha May 1 at 16:10
  • @RicardoBohner Politely, if this does solve it for you, could you accept it as an answer? Others in the future are going to have the same problem, which is most of what Stack Exchange exists to address. Accepting the answer will make it easier to find this with a simple search, and will hopefully save them some time. – Michael Macha May 1 at 16:11
0

After playing around with @Michael Macha's answer I figured out a way to do what I wanted without having stuck frames and extra seconds on the beginning of the video:

ffmpeg -i "Input.mp4" -filter_complex \
"[0:v]select='between(t,45,50)'[v1], \
[0:v]select='between(t,23,30)'[v2], \
[v1][v2]concat[outv],[outv]scale=720:-1[outv], \
[outv]setpts=N/FRAME_RATE/TB[outv]" -map [outv] output.avi

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.