22

I'm trying to use FFmpeg to split video based on some start_time and end_time inputs, and replace those with other video files. For instance, for input 10 15 I will need to replace EXACT seconds 10 to 15 with another video.

OK. What comes into my mind is this:

  1. First split the video (in this case, 0-10, 10-15, and 15-end)
  2. Replace the 10-15 with the other video
  3. Merge all sub-videos back

First question: Is this the best and fastest solution? Do we have an easier command or other tools to replace a specific period of time in video with another video?

Second question: Assuming FFmpeg is the viable solution, when I simply use the command ffmpeg -I input.video -ss 10 -t 5 -c copy output.mp4, my video processing code can not read and process the file. I used mediainfo tool to compare the two files and noticed differences in the header sizes while all video codec related stuff was the same. I noticed the HEADER SIZE is different which is apparently causing the problem. Is there any ways to force the header size also (aside from codecs) be the same?

UPDATE: Here is the original .mp4 file which works fine in the emulator.

Giacomo1968
  • 53,069
  • 19
  • 162
  • 212
Mary
  • 407
  • 1
  • 3
  • 12
  • First we'd have to deal with the problem you mention in your second question. Please show the exact command you're using and the full, uncut command line output that you get from ffmpeg. Also show any actual errors you get when reading the file – it'd be good to know what your "processing code" is, exactly. – slhck Jul 14 '17 at 16:37
  • @slhck the video processing code is a huge code base. I can not share it. It is just that for some reasons the original file is read but not the re-generated one. I can however share the original `.mp4` file if needed. – Mary Jul 14 '17 at 18:10
  • I used mediainfo tool to compare the two files and notice differences in the header sizes and stuff – Mary Jul 14 '17 at 18:10
  • Having the original would help, but we won't be able to troubleshoot why it can't be read, I'm afraid, if that is some internal code. – slhck Jul 14 '17 at 18:30
  • @slhck I uploaded the file. My question is updated – Mary Jul 14 '17 at 19:43

1 Answers1

29

Using trim and concat – source files with different codecs

For this, make sure that your individual files have the same resolution, and ideally same framerate and chroma subsampling etc. This will prevent errors or glitches during concatenation.

You can do everything in one go without splitting the file, using the trim and concat filters:

ffmpeg -i edv_g24.mp4 -i short-video.mp4 -filter_complex "\
[0:v]trim=0:10,setpts=PTS-STARTPTS[v0]; \
[1:v]trim=0:5,setpts=PTS-STARTPTS[v1]; \
[0:v]trim=15:30,setpts=PTS-STARTPTS[v2]; \
[v0][v1][v2]concat=n=3:v=1:a=0[out]" \
-map "[out]" output.mp4

Here, trim is used to specify the individual portions of the input video streams (0:v, 1:v) that you later want to concatenate. These parts are named v0 through v2. (The setpts filter resets the timestamps of these individual parts to 0, which is required for concatenation). Later, we concatenate the three parts.

If you want to trim from a certain timestamp to the end, use trim=start=15 instead of specifying the range.

If your files have audio, you have to trim those streams separately:

ffmpeg -i edv_g24_2.mp4 -i short-video.mp4 -filter_complex "\
[0:v]trim=0:10,setpts=PTS-STARTPTS[v0]; \
[0:a]atrim=0:10,asetpts=PTS-STARTPTS[a0]; \
[1:v]trim=0:5,setpts=PTS-STARTPTS[v1]; \
[1:a]atrim=0:5,asetpts=PTS-STARTPTS[a1]; \
[0:v]trim=15:30,setpts=PTS-STARTPTS[v2]; \
[0:a]atrim=15:30,asetpts=PTS-STARTPTS[a2]; \
[v0][a0][v1][a1][v2][a2]concat=n=3:v=1:a=1[outv][outa]" \
-map "[outv]" -map "[outa]" output.mp4

Note that video and audio will be re-encoded in this case – make sure to specify appropriate output quality targets (e.g. -crf for x264, x265 or libvpx-vp9). You can read more about this on the FFmpeg Wiki, e.g. for VP9 or H.264.

Using individual segments

If you want to split the clips and later reassemble them:

ffmpeg -i edv_g24.mp4 -ss 0 -to 10 -c copy part1.mp4
ffmpeg -i edv_g24.mp4 -ss 10 -to 15 -c copy part2.mp4
ffmpeg -i edv_g24.mp4 -ss 15 -c copy part3.mp4

ffmpeg -i part1.mp4 -i short-video.mp4 -i part3.mp4 -filter_complex \
"[0:v][1:v][2:v]concat=n=3:v=1:a=0[outv]" \
-map "[outv]" -t 30 output.mp4

If the files have audio, use the same approach as above:

ffmpeg -i part1.mp4 -i short-video.mp4 -i part3.mp4 -filter_complex \
"[0:v][0:a][1:v][1:a][2:v][2:a]concat=n=3:v=1:a=1[outv][outa]" \
-map "[outv]" -map "[outa]" -t 30 output.mp4

This will again re-encode the video stream. It's a little more straightforward but otherwise should be equivalent to the above method.

Using concat demuxer

You can in principle also try to concatenate the bitstreams without re-encoding them, using the concat demuxer. Create a file called concat.txt and add the following entries (corresponding to the video clips created above):

file 'part1.mp4'
file 'short-video.mp4'
file 'part3.mp4'

Then concatenate these individual files:

ffmpeg -f concat -i concat.txt -c copy output.avi

This however requires your clips to have the same codec, resolution, framerate etc. – so it doesn't work with all kinds of heterogenous sources.

Using concat protocol

The above kind of file-level concatenation can be achieved using the concat protocol as well, with the same kind of constraints as above (same codec, resolution, etc.):

ffmpeg -i "concat:part1.avi|part2.avi|part3.avi" -c copy output.mp4

For more info on concatenation, read the respective FFmpeg Wiki page.

H.v.M.
  • 547
  • 2
  • 5
  • 20
slhck
  • 223,558
  • 70
  • 607
  • 592
  • This looks very complete! Thanks. I will try them! – Mary Jul 15 '17 at 15:07
  • Idk why using the `concat` method throws an error: `[concat @ 00000000025c2560] Line 1: unknown keyword 'part1.mp4 \\ text.txt: Invalid data found when processing input` – Mary Jul 17 '17 at 14:16
  • For "using individual segments", I see a halt and small cut out the output. Is there any ways to fix this? – Mary Jul 17 '17 at 14:27
  • Method 1 works perfectly. One thing: how can we specify "until end" without stating a specific number (instead of 30 s) – Mary Jul 17 '17 at 15:00
  • 1
    Sorry, made a mistake. For the `concat` demuxer, the syntax was different. Fixed now. To go until the end, use `trim=start=15`, see the trim filter documentation: http://ffmpeg.org/ffmpeg-filters.html#trim -- as for timing issues, this really depends on your source material. Haven't had issues concatenating though with the clip you uploaded. – slhck Jul 17 '17 at 15:01
  • Great! Now it works. Is there any ways just to put all text in command line instead of a text file? I need to do all these in my C code. I wanna avoid playing with files – Mary Jul 17 '17 at 15:15
  • I guess that you can create a file descriptor and read from that. ffmpeg doesn't care if it reads from a real file or something that looks like one, and this should be doable in C. This would be similar to what Bash can do with process substitution: http://trac.ffmpeg.org/wiki/Concatenate – slhck Jul 17 '17 at 15:18
  • Found it: `ffmpeg -i "concat:part1.avi|part2.avi|part3.avi" -c copy output.mp4` – Mary Jul 17 '17 at 15:19
  • That is something different. This uses the protocol demuxer, and it will definitely only work if the files have the same codec, resolution, and it won't work for all containers, especially not modern ones, or ones that aren't streamable. – slhck Jul 17 '17 at 15:20
  • Great answers. Thanks. Did you have any chance for the header problem? Shall I ask a separate question for that and mark this as done? – Mary Jul 17 '17 at 15:20
  • Yeah, I didn't understand what exactly you meant with "header", so please ask a new question and try to be precise. Thanks! – slhck Jul 17 '17 at 15:21
  • here is the new question: https://superuser.com/questions/1230643/ffmpeg-conserve-original-video-file-header – Mary Jul 17 '17 at 15:33
  • -f concat did not work for an h.264 MP4 of Elephant Dreams. The concatenated file hangs on playback where the files were concatenated. I have a hunch that setting GOP at 2s could fix it. Any thoughts? – SacWebDeveloper Mar 24 '19 at 03:28
  • @SacWebDeveloper The concat demuxer requires the files to have basically the same encoding settings and properties. If they're dissimilar, you may have playback problems, even though the ffmpeg command worked. In this case, use the concat filter instead. – slhck Mar 24 '19 at 17:05
  • Thanks, much better explained than FFMpeg documentation! – Al Hill Jun 17 '20 at 10:18
  • How to you specify to re-encode all parts to have same codecs so input files can differ a lot? With concat says `Media type mismatch`. – Smeterlink Jul 21 '21 at 23:14
  • 1
    @Smeterlink Please ask a new question and include the full command and command line output. – slhck Jul 22 '21 at 08:17
  • How can I with a single command cut into separate files where some of the output files have jump cuts, that is, they're concatenations of some of the cut segments? For example, I want to with a single command (so there's only one seeking through the input file) cut out segments a, b, c, and d and create output files where the first one is concatenation of a+b and the second, c+d? – Display Name May 22 '23 at 00:25
  • @DisplayName I don't think this can be *easily* achieved with one seek/command. Especially if you need multiple output files. I'd rather cut the segments first (i.e., create a, b, c and d, and remaining parts, using, e.g., using the `segment_times` option of the segment muxer) into lossless video (e.g. `.nut`), then create the new ones (a+b, c+d) based on a simple concat demuxer. Of course you could write multiple filter chains with `trim`. – slhck May 22 '23 at 08:24
  • @DisplayName Seeking itself is not the biggest performance impact. You could decode the video into a large complex filter chain (where you could use `trim` and `concat` to generate your outputs as needed), but it will likely have lower performance as it needs to decode the entire video. – slhck May 22 '23 at 08:25