sil
sil

Reputation: 2110

Combining three or more videos with ffmpeg and the xfade filter

As of 2020 ffmpeg has the xfade filter which can combine videos with a transition. Combining two videos is easy enough:

ffmpeg -i vid1.mp4 -i vid2.mp4 \
  -filter_complex [0][1]xfade=transition=pixelize:duration=1:offset=4,format=yuv420p" \
  out.mp4

But I don't understand how to combine three videos (so that v1 fades into v2 and v2 then fades into v3. I tried something like this:

ffmpeg -i vid1.mp4 -i vid2.mp4 -i vid3.mp4 \
  -filter_complex [0][1]xfade=transition=pixelize:duration=1:offset=4,format=yuv420p[0n1];[0n1][2]xfade=transition=pixelize:duration=1:offset=9,format=yuv420p" \
  out.mp4

but that doesn't work. My idea was that 0 and 1, or vid1 and vid2, would be combined into a [0n1] stream with a transition by xfade, and then that 0n1 stream could be combined with vid3 with another filter. As far as I can tell, this includes the first two videos but not the third. What this of course means is that I don't understand how to specify a filtergraph correctly!

How should I use xfade to combine 3 or more videos with transitions between them?

A full example is as follows. Here I'll use three images (because then issues with combining videos at different frame rates are avoided), and smash them all to 500x500 for ease (in the final version they would be letterboxed to keep resolution and so on).

ffmpeg \
  -loop 1 -t 5 -i tests/p1.jpg \
  -loop 1 -t 5 -i tests/p2.jpg \
  -loop 1 -t 5 -i tests/p3.jpg \
  -filter_complex "[0]scale=500:500[s0];[1]scale=500:500[s1];[2]scale=500:500[s2];[s0][s1]xfade=transition=pixelize:duration=1:offset=4,format=yuv420p[s01];[s01][s2]xfade=transition=pixelize:duration=1:offset=9,format=yuv420p" out.mp4

I would expect this to create a video which was:

but what I actually get is

and then the video ends. p3 is not included at all.

The output is as follows:

ffmpeg version N-53546-g5eb4405fc5-static https://johnvansickle.com/ffmpeg/  Copyright (c) 2000-2020 the FFmpeg developers
  built with gcc 8 (Debian 8.3.0-6)
  configuration: --enable-gpl --enable-version3 --enable-static --disable-debug --disable-ffplay --disable-indev=sndio --disable-outdev=sndio --cc=gcc --enable-fontconfig --enable-frei0r --enable-gnutls --enable-gmp --enable-libgme --enable-gray --enable-libaom --enable-libfribidi --enable-libass --enable-libvmaf --enable-libfreetype --enable-libmp3lame --enable-libopencore-amrnb --enable-libopencore-amrwb --enable-libopenjpeg --enable-librubberband --enable-libsoxr --enable-libspeex --enable-libsrt --enable-libvorbis --enable-libopus --enable-libtheora --enable-libvidstab --enable-libvo-amrwbenc --enable-libvpx --enable-libwebp --enable-libx264 --enable-libx265 --enable-libxml2 --enable-libdav1d --enable-libxvid --enable-libzvbi --enable-libzimg
  libavutil      56. 56.100 / 56. 56.100
  libavcodec     58. 97.100 / 58. 97.100
  libavformat    58. 49.100 / 58. 49.100
  libavdevice    58. 11.101 / 58. 11.101
  libavfilter     7. 87.100 /  7. 87.100
  libswscale      5.  8.100 /  5.  8.100
  libswresample   3.  8.100 /  3.  8.100
  libpostproc    55.  8.100 / 55.  8.100
Input #0, image2, from 'tests/p1.jpg':
  Duration: 00:00:00.04, start: 0.000000, bitrate: 44845 kb/s
    Stream #0:0: Video: mjpeg (Baseline), yuvj444p(pc, bt470bg/unknown/unknown), 820x1270 [SAR 150:150 DAR 82:127], 25 fps, 25 tbr, 25 tbn, 25 tbc
Input #1, image2, from 'tests/p2.jpg':
  Duration: 00:00:00.04, start: 0.000000, bitrate: 22325 kb/s
    Stream #1:0: Video: mjpeg (Baseline), yuvj420p(pc, bt470bg/unknown/unknown), 960x600 [SAR 1:1 DAR 8:5], 25 fps, 25 tbr, 25 tbn, 25 tbc
Input #2, image2, from 'tests/p3.jpg':
  Duration: 00:00:00.04, start: 0.000000, bitrate: 15266 kb/s
    Stream #2:0: Video: mjpeg (Baseline), yuvj420p(pc, bt470bg/unknown/unknown), 728x669 [SAR 96:96 DAR 728:669], 25 fps, 25 tbr, 25 tbn, 25 tbc
File 'out.mp4' already exists. Overwrite? [y/N] y
Stream mapping:
  Stream #0:0 (mjpeg) -> scale
  Stream #1:0 (mjpeg) -> scale
  Stream #2:0 (mjpeg) -> scale
  format -> Stream #0:0 (libx264)
Press [q] to stop, [?] for help
[swscaler @ 0x8228040] deprecated pixel format used, make sure you did set range correctly
[swscaler @ 0x8258640] deprecated pixel format used, make sure you did set range correctly
[swscaler @ 0x827df40] deprecated pixel format used, make sure you did set range correctly
[swscaler @ 0x829f800] deprecated pixel format used, make sure you did set range correctly
[swscaler @ 0x82c13c0] deprecated pixel format used, make sure you did set range correctly
[swscaler @ 0x82e8340] deprecated pixel format used, make sure you did set range correctly
[libx264 @ 0x77b7600] using SAR=82/127
[libx264 @ 0x77b7600] using cpu capabilities: MMX2 SSE2Fast SSSE3 SSE4.2 AVX FMA3 BMI2 AVX2
[libx264 @ 0x77b7600] profile High, level 3.0, 4:2:0, 8-bit
[libx264 @ 0x77b7600] 264 - core 161 r3018 db0d417 - H.264/MPEG-4 AVC codec - Copyleft 2003-2020 - http://www.videolan.org/x264.html - options: cabac=1 ref=3 deblock=1:0:0 analyse=0x3:0x113 me=hex subme=7 psy=1 psy_rd=1.00:0.00 mixed_ref=1 me_range=16 chroma_me=1 trellis=1 8x8dct=1 cqm=0 deadzone=21,11 fast_pskip=1 chroma_qp_offset=-2 threads=6 lookahead_threads=1 sliced_threads=0 nr=0 decimate=1 interlaced=0 bluray_compat=0 constrained_intra=0 bframes=3 b_pyramid=2 b_adapt=1 b_bias=0 direct=1 weightb=1 open_gop=0 weightp=2 keyint=250 keyint_min=25 scenecut=40 intra_refresh=0 rc_lookahead=40 rc=crf mbtree=1 crf=23.0 qcomp=0.60 qpmin=0 qpmax=69 qpstep=4 ip_ratio=1.40 aq=1:1.00
Output #0, mp4, to 'out.mp4':
  Metadata:
    encoder         : Lavf58.49.100
    Stream #0:0: Video: h264 (libx264) (avc1 / 0x31637661), yuv420p, 500x500 [SAR 82:127 DAR 82:127], q=-1--1, 25 fps, 12800 tbn, 25 tbc (default)
    Metadata:
      encoder         : Lavc58.97.100 libx264
    Side data:
      cpb: bitrate max/min/avg: 0/0/0 buffer size: 0 vbv_delay: N/A
frame=  101 fps=100 q=28.0 size=       0kB time=00:00:01.92 bitrate=   0.2kbits/s speed=[swscaler @ 0x8291d80] deprecated pixel format used, make sure you did set range correctly
    Last message repeated 2 times
[swscaler @ 0x82b3000] deprecated pixel format used, make sure you did set range correctly
    Last message repeated 2 times
[swscaler @ 0x82fc200] deprecated pixel format used, make sure you did set range correctly
    Last message repeated 2 times
frame=  153 fps=101 q=28.0 size=       0kB time=00:00:04.00 bitrate=   0.1kbits/s speed=[swscaler @ 0x82fc200] deprecated pixel format used, make sure you did set range correctly
    Last message repeated 2 times
frame=  225 fps=111 q=28.0 size=     256kB time=00:00:06.88 bitrate= 304.9kbits/s dup=0 frame=  225 fps= 95 q=-1.0 Lsize=     267kB time=00:00:08.88 bitrate= 245.9kbits/s dup=0 drop=125 speed=3.74x    
video:263kB audio:0kB subtitle:0kB other streams:0kB global headers:0kB muxing overhead: 1.293847%
[libx264 @ 0x77b7600] frame I:2     Avg QP:21.03  size: 19928
[libx264 @ 0x77b7600] frame P:66    Avg QP:21.18  size:  2453
[libx264 @ 0x77b7600] frame B:157   Avg QP:29.72  size:   427
[libx264 @ 0x77b7600] consecutive B-frames:  5.3%  4.4%  1.3% 88.9%
[libx264 @ 0x77b7600] mb I  I16..4: 29.0% 27.8% 43.2%
[libx264 @ 0x77b7600] mb P  I16..4: 10.8%  3.2%  7.5%  P16..4:  2.6%  0.9%  0.3%  0.0%  0.0%    skip:74.8%
[libx264 @ 0x77b7600] mb B  I16..4:  1.1%  0.5%  1.6%  B16..8:  1.0%  0.5%  0.1%  direct: 0.3%  skip:94.9%  L0:37.4% L1:43.5% BI:19.1%
[libx264 @ 0x77b7600] 8x8 transform intra:16.5% inter:55.3%
[libx264 @ 0x77b7600] coded y,uvDC,uvAC intra: 31.4% 71.7% 31.8% inter: 1.1% 1.4% 0.1%
[libx264 @ 0x77b7600] i16 v,h,dc,p: 45% 46%  9%  1%
[libx264 @ 0x77b7600] i8 v,h,dc,ddl,ddr,vr,hd,vl,hu: 25% 30% 27%  6%  1%  1%  2%  1%  7%
[libx264 @ 0x77b7600] i4 v,h,dc,ddl,ddr,vr,hd,vl,hu: 56% 31%  9%  1%  1%  1%  1%  1%  1%
[libx264 @ 0x77b7600] i8c dc,h,v,p: 46% 27% 23%  4%
[libx264 @ 0x77b7600] Weighted P-Frames: Y:10.6% UV:7.6%
[libx264 @ 0x77b7600] ref P L0: 61.9% 17.1% 13.9%  6.8%  0.3%
[libx264 @ 0x77b7600] ref B L0: 76.3% 22.8%  0.9%
[libx264 @ 0x77b7600] ref B L1: 97.6%  2.4%
[libx264 @ 0x77b7600] kb/s:238.88

Upvotes: 3

Views: 2467

Answers (1)

Gyan
Gyan

Reputation: 92928

The offset for the 2nd xfade is wrong. With 5 seconds clips, and 1 second transition at the tail of each clip, the 2nd clip starts at 4s and ends at 9s, so the offset for the 2nd xfade should be 8, not 9.

So,

ffmpeg \
  -loop 1 -t 5 -i tests/p1.jpg \
  -loop 1 -t 5 -i tests/p2.jpg \
  -loop 1 -t 5 -i tests/p3.jpg \
  -filter_complex "[0]scale=500:500[s0];[1]scale=500:500[s1];[2]scale=500:500[s2];[s0][s1]xfade=transition=pixelize:duration=1:offset=4,format=yuv420p[s01];[s01][s2]xfade=transition=pixelize:duration=1:offset=8,format=yuv420p" out.mp4

Upvotes: 2

Related Questions