How can I remove multiple segments from a video using FFmpeg?
Well, you still can use the trim
filter for that. Here is an example, lets assume that you want to cut out segments 30-40 sec (10 sec) and 50-80 sec (30 sec):
ffmpeg -i in.ts -filter_complex \
"[0:v]trim=duration=30[a]; \
[0:v]trim=start=40:end=50,setpts=PTS-STARTPTS[b]; \
[a][b]concat[c]; \
[0:v]trim=start=80,setpts=PTS-STARTPTS[d]; \
[c][d]concat[out1]" -map [out1] out.ts
What I did here? I trimmed first 30 sec, 40-50 sec and 80 sec to end, and then combined them into stream out1
with the concat
filter.
About setpts: we need this because trim does not modify picture display time, and when we cut out 10 sec decoder counter does not see any frames for this 10 sec.
If you want to have audio too, You have to do the same for audio streams. So the command should be:
ffmpeg -i utv.ts -filter_complex \
"[0:v]trim=duration=30[av];[0:a]atrim=duration=30[aa];\
[0:v]trim=start=40:end=50,setpts=PTS-STARTPTS[bv];\
[0:a]atrim=start=40:end=50,asetpts=PTS-STARTPTS[ba];\
[av][bv]concat[cv];[aa][ba]concat=v=0:a=1[ca];\
[0:v]trim=start=80,setpts=PTS-STARTPTS[dv];\
[0:a]atrim=start=80,asetpts=PTS-STARTPTS[da];\
[cv][dv]concat[outv];[ca][da]concat=v=0:a=1[outa]" -map [outv] -map [outa] out.ts
I can never get ptQa's solution to work, mostly because I can never figure out what the errors from the filters mean or how to fix them. My solution seems a little clunkier because it can leave behind a mess, but if you're throwing it into a script, the clean up can be automated. I also like this approach because if something goes wrong on step 4, you end up with completed steps 1-3 so recovering from errors is a little more efficient.
The basic strategy is using -t
and -ss
to get videos of each segment you want, then join together all the parts for your final version.
Say you have 6 segments ABCDEF each 5 seconds long and you want A (0-5 seconds), C (10-15 seconds) and E (20-25 seconds) you'd do this:
ffmpeg -i abcdef.tvshow -t 5 a.tvshow -ss 10 -t 5 c.tvshow -ss 20 -t 5 e.tvshow
or
ffmpeg -i abcdef.tvshow -t 0:00:05 a.tvshow -ss 0:00:10 -t 0:00:05 c.tvshow -ss 0:00:20 -t 0:00:05 e.tvshow
That will make files a.tvshow, c.tvshow and e.tvshow. The -t
says how long each clip is, so if c is 30 seconds long you could pass in 30 or 0:00:30. The -ss
option says how far to skip into the source video, so it's always relative to the start of the file.
Then once you have a bunch of video files I make a file ace-files.txt
like this:
file 'a.tvshow'
file 'c.tvshow'
file 'e.tvshow'
Note the "file" at the beginning and the escaped file name after that.
Then the command:
ffmpeg -f concat -i ace-files.txt -c copy ace.tvshow
That concats all the files in abe-files.txt
together, copying their audio and video codecs and makes a file ace.tvshow
which should just be sections a, c and e. Then just remember to delete ace-files.txt
, a.tvshow
, c.tvshow
and e.tvshow
.
Disclaimer: I have no idea how (in)efficient this is compared to the other approaches in terms of ffmpeg
but for my purposes it works better. Hope it helps someone.
For those having trouble following ptQa's approach, there's a slightly more streamlined way to go about it. Rather than concat each step of the way, just do them all at the end.
For each input, define a A/V pair:
//Input1:
[0:v]trim=start=10:end=20,setpts=PTS-STARTPTS,format=yuv420p[0v];
[0:a]atrim=start=10:end=20,asetpts=PTS-STARTPTS[0a];
//Input2:
[0:v]trim=start=30:end=40,setpts=PTS-STARTPTS,format=yuv420p[1v];
[0:a]atrim=start=30:end=40,asetpts=PTS-STARTPTS[1a];
//Input3:
[0:v]trim=start=30:end=40,setpts=PTS-STARTPTS,format=yuv420p[2v];
[0:a]atrim=start=30:end=40,asetpts=PTS-STARTPTS[2a];
Define as many pairs as you need, then concat them all in one pass, where n=total input count.
[0v][0a][1v][1a][2v][2a]concat=n=3:v=1:a=1[outv][outa] -map [outv] -map [outa] out.mp4
This can easily be constructed in a loop.
A complete command that uses 2 inputs might look like this:
-y -i in.mp4 -filter_complex
[0:v]trim=start=10.0:end=15.0,setpts=PTS-STARTPTS,format=yuv420p[0v];
[0:a]atrim=start=10.0:end=15.0,asetpts=PTS-STARTPTS[0a];
[0:v]trim=start=65.0:end=70.0,setpts=PTS-STARTPTS,format=yuv420p[1v];
[0:a]atrim=start=65.0:end=70.0,asetpts=PTS-STARTPTS[1a];[0v][0a][1v]
[1a]concat=n=2:v=1:a=1[outv][outa] -map [outv] -map [outa] out.mp4