Remove a section from the middle of a video without concat

Viewed 1845

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).

2 Answers

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.

If you want to use the copy "codec", consider the following approach:

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 now 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 "codex".

Related