Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

FFmpeg - How to trim with high precision?

Tags:

ffmpeg

I am pretty stucked with the way that FFmpeg trims videos, so I would be really grateful if someone could help me. Basically, what I am trying to do is: I have a video sequence and I want it to stop in a certain second, freeze the image some seconds (like a pause), and then reproduce again from the same exact frame. I could say that I am dealing with three videos: A.mp4 (first part of the video sequence), B.mp4 (frozen image) and C.mp4 (second part of the video sequence). Besides, I also perform an overlay with filter_complex.

The part of generating a video from a static image is not a problem, my main concern is to find the way to trim and concatenate videos accurately. From other posts and sources, I discovered that FFmpeg trims from those Frames that are Keyframes, which can be forced. However, the result I obtain is not the appropriate one, because my video A ends in a frame different from the one in the beginning of C.

The commands I am using are the following ones:

ffmpeg -y -i VideoSequence.mp4 -c:v libx264 -pix_fmt yuv420p \
-force_key_frames "expr: gte(t,n_forced * 15)" -t 30 VideoOut.mp4

[Note that everything inside Filter Complex is about an Overlay, which works fine]

As far as I know, the resulting video should have a Keyframe every 15 seconds. Now, I want to cut the video into 2 parts ("before second 15" and "after second 15"):

ffmpeg -y -ss 00:00:01 -i VideoOut.mp4 -t 14 -c copy A.mp4

ffmpeg -y -ss 00:00:15 -i VideoOut.mp4 -t 5 -c copy C.mp4 

As mentioned, I expect the end of A.mp4 to "match" with the beginning of C.mp4 (at a frame precision), but the result I obtain is far from being perfect.

Thank you very much, any kind of help will be appreciated!

like image 869
DSalenga Avatar asked Oct 11 '16 14:10

DSalenga


1 Answers

Assuming your end goal is I want it to stop in a certain second, freeze the image some seconds (like a pause), and then reproduce again from the same exact frame., the current method, with multiple video generations, is needlessly wasteful.

Here's the way to do it in one command, assuming the video is 25 FPS.

ffmpeg -i original.mp4 -filter_complex
       "[0]split[a][b];
        [a]trim=1:15,loop=75:1:349,setpts=N/FRAME_RATE/TB[pre];
        [b]trim=15,setpts=N/FRAME_RATE/TB[post];
        [0]atrim=1:15,asetpts=N/SR/TB[apre];
        [0]atrim=15,asetpts=N/SR/TB[apost];
        [pre][apre][post][apost]concat=a=1[v][a]"
       -map "[v]" -map "[a]" paused.mp4

Here's what is happening in the filtergraph:

First, the video is split to two identical streams. Then the first stream is trimmed from start of 2nd to end of 15th second. In the loop filter, 1 frame starting at (the last frame) #349 is looped for 75 frames. Then the timestamps are regularized since the trim or loop filters won't do so. The 2nd split stream is trimmed to start from the 16th second, its timestamps reset and the two streams joint using concat. If you need to overlay something on top of the joint stream, insert an overlay filter after the concat.

like image 173
Gyan Avatar answered Nov 15 '22 07:11

Gyan