user3310334
user3310334

Reputation:

Remove a section from the middle of a video without concat

How do I cut a section out of a video with ffmpeg?

Imagine I have a 60 second mp4 A.

I want to remove all the stuff from 0:15 to 0:45.

The result should be a 30-second mp4, which is composed of the first 15 seconds of A directly followed by the last 15 seconds of A.

How can I do this without using concat?

I know how I could do it by creating two intermediary files and then using ffmpeg to concat them. I don't want to have to perform so much manual work for this (simple?) operation.

I have also seen the trim filder used for removing multiple parts from a video. All the usages I've found show that it seems to be very verbose, and I haven't found an example for a case as simple as I would like (just a single section removed).

Do I have to use trim for this operation? Or are there other less verbose solutions?

The ideal would of course be something at least simple as -ss 0:15 -to 0:45 which removes the ends of a video (-cut 0:15-0:45 for example).

Upvotes: 34

Views: 16033

Answers (4)

lmat - Reinstate Monica
lmat - Reinstate Monica

Reputation: 7768

If you want to use the copy "codec", consider the following approach. Warning! This is lossy. I don't mean that the compression is losing fidelity, but it's actually losing frames because cuts are done on the nearest keyframes.

ffmpeg -i input.mp4 -t "$start_cut_section" -c copy part1.mp4&
ffmpeg -i input.mp4 -ss "$end_cut_section" -c copy part2.mp4&
echo "file 'part1.mp4'" > filelist;
echo "file 'part2.mp4'" >> filelist;
wait;

ffmpeg -f concat -i filelist -c copy output.mp4;
rm filelist;

This creates two files from before and after the cut, then combines them into a new trimmed final video. Obviously, this can be used to create as many cuts as you like. It may seem like a longer approach than the accepted answer, but it likely will execute much faster because of the use of the copy codec.

Upvotes: 13

sriramcu
sriramcu

Reputation: 57

I came across this requirement multiple times, when I have a long video and I only want to remove a small portion of it. I would have to transform the segments of the video to be removed into segments of the video to be preserved from the original video file. Then, I would either have to use the infile method, which requires converting these timestamps (MM:SS or HH:MM:SS) into seconds. Or I could use the trim filter on these transformed segments, which is as you mentioned, quite verbose.

This process can be quite tedious, especially if I'm removing 10-15 tiny snippets from a video. I automated this process using a Python script, wherein the user can directly mention the timestamps of unwanted segments of the video to be removed, without the need of the aforementioned transformation, or the requirement to convert any timestamps into seconds.

With this tool, the one-liner command would look like this:

python ffmpeg_batch_cut.py -i A -s 0:15-0:45 output.mp4

The editing of the video would take place without re-encoding (the tool internally uses the concat file method), which may lead to the loss of keyframes, depending on the input video file. For more information, refer to my GitHub repository.

Upvotes: 0

jaimet
jaimet

Reputation: 603

I started from https://stackoverflow.com/a/54192662/3499840 (currently the only answer to "FFmpeg remove 2 sec from middle of video and concat the parts. Single line solution").

Working from that example, the following works for me:

    # In order to keep <start-15s> and <45s-end>, you need to 
    # keep all the frames which are "not between 15s and 45s":

    ffmpeg -i input.mp4 \
      -vf  "select='not(between(t,15,45))',  setpts=N/FRAME_RATE/TB" \
      -af "aselect='not(between(t,15,45))', asetpts=N/SR/TB" \
      output.mp4

This is a one-line linux command, but I've used the bash line-continuation character ('\') so that I can vertically align the equals-signs as this helps me to understand what is going on.

I had never seen ffmpeg's not and between operators before, but I found their documentation here.

Regarding the usual ffmpeg "copy vs re-encode" dichotomy, I was hoping to be able to use ffmpeg's "copy" "codec" (yeah, I know that it's not really a codec) so that ffmpeg would not re-encode my video, but if I specify "copy", then ffmpeg starts and stops at the nearest keyframes which are not sufficiently close to my desired start and stop points. (I want to remove a piece of video that is approximately 20 seconds long, but my source video only has one keyframe every 45 seconds!). Hence I am obliged to re-encode. See https://trac.ffmpeg.org/wiki/Seeking#Seekingwhiledoingacodeccopy for more info.

The setpts/asetpts filters set the timestamps on each frame to the correct values so that your media player will play each frame at the correct time.

HTH.

Upvotes: 32

mel3kings
mel3kings

Reputation: 9405

For those looking to remove different parts of the video and not just one block, you can pass multiple 'queries' in the command:

ffmpeg -i in.mp4 \
-vf select='not(between(t\,0\,10))',select='not(between(t\,15\,30))',setpts=N/FRAME_RATE/TB \
-af aselect='not(between(t\,0\,10))',aselect='not(between(t\,15\,30))',asetpts=N/SR/TB \
out.mp4

Upvotes: 6

Related Questions