44

I've been trying to achieve a crossfade transition between 2 video clips using ffmpeg but have failed so far. I'm new to ffmpeg and am mostly relying on tweaking what I can find in the documentation and existing examples online. From what I read so far, using either the blend or overlay filter should help in achieving what I'm after but I can't figure out the command line details to get it to work.

The fade and concat filters are great for fade-out of video 1, fade-in to video 2 and concat the 2 into 1 clip type transitions but I'd appreciate help in getting a command to transition from video 1 to video 2 without any going to black in between. I couldn't find any examples for exactly this problem anywhere, maybe I'm looking for the wrong keywords...?

More speficially, my videos are mp4s (h264 video, no sound, in case that matters), each is 5 seconds long and I'm after a transition from approx. 4.5s of video 1 to 0.5s of video 2.

Similar to what this tutorial does using MLT and frames (see 2:25 for an example fade), though I'm looking for a way to do this just in ffmpeg without calling any other progs. http://www.youtube.com/watch?v=3PRZ9L_KLdI

Any pointers or maybe a command line to get a fade like this would be much appreciated, thanks very much!

  • related http://superuser.com/questions/223678/how-to-convert-single-images-into-a-video-with-blending-transition | http://stackoverflow.com/questions/7565962/ffmpeg-fade-effects-between-frames – Ciro Santilli OurBigBook.com May 27 '16 at 11:02

7 Answers7

37

I suggest to do that way:

  • Create black background with the same duration and resolution as output video should be
  • Add alpha channel to each video
  • Add fade to alpha effect to each video
  • Use overlay on each video with black background

So the command for adding crossfade to 2 video (5 sec) each should be:

ffmpeg -i 1.mp4 -i 2.mp4 -f lavfi -i color=black -filter_complex \
"[0:v]format=pix_fmts=yuva420p,fade=t=out:st=4:d=1:alpha=1,setpts=PTS-STARTPTS[va0];\
[1:v]format=pix_fmts=yuva420p,fade=t=in:st=0:d=1:alpha=1,setpts=PTS-STARTPTS+4/TB[va1];\
[2:v]scale=960x720,trim=duration=9[over];\
[over][va0]overlay[over1];\
[over1][va1]overlay=format=yuv420[outv]" \
-vcodec libx264 -map [outv] out.mp4

This will fade out first video to alpha at the 4th second (st=4) with a duration of 1 second (d=1), fade in the second one at the 0th second (st=0) with a duration of 1 second (d=1), and move its display time forward to 4 sec (+4/TB). Then we just cut 9 second of black color, scale it to output video size and overlay the stuff.

Hope it helps.

Hashim Aziz
  • 11,898
  • 35
  • 98
  • 166
ptQa
  • 2,079
  • 15
  • 14
  • Hi @ptQa, thanks very much, the crossfade effect works like a charm. Though what happens in the output video is that it only shows a top left section of the original videos and the rest of the frame is missing. I looked at the resolution, input is 960 x 720 and output is 320 x 240 but it doesn't scale down the whole video, instead cuts out a 320 x 240 section in the top left and shows that in the output video, rest of the input videos is missing. Is there an addition to your command line that prevents this? –  Jul 10 '14 at 00:42
  • Oh, I got it, you should also scale black color to resolution that you want to get. See updated answer. I've added scale to filter graph. – ptQa Jul 10 '14 at 08:39
  • That solved it, thanks. I uploaded a sample video showing the crossfading in case anyone wants to see it in action, http://youtu.be/JqorgXAjjTo –  Jul 11 '14 at 00:42
  • Same problem; I have a variant of this working on some of my own video -- thanks. Question: How would this be modified to also cross-fade the videos' audio tracks, following the same pattern as the video fade? – Jim Miller Sep 17 '14 at 05:06
  • 1
    BTW, I think there's an error in the answer's sample command -- it should be `-i 1.mp4 -i 2.mp4...`, right? – Jim Miller Sep 17 '14 at 05:07
  • I had an issue of the video freezing in transition using this command. FFmpeg v2.6. I found that using frames instead of seconds for the frame filters would fix the issue, e.g. fade=out:120:30 for a 30fps video. Is this a bug with FFmpeg? @ptQa – Jon G May 20 '15 at 16:27
  • It sounds like you have a video container without PTS/DTS or you do not have container at all. Try to convert video to mpeg2ts and see if it helps. – ptQa May 20 '15 at 20:27
  • What's with the squeezed frame when i use 2 full HD videos and set the resolution of the black input to 1920x1080. It's squeezing the original videos and putting them between black bars on each side but the overall dimensions of the video are full HD. – Emery King Mar 22 '16 at 23:00
  • How would you be able to do this for a variable number of videos like 5? How would the filter_complex be structured? – jrkt Sep 26 '16 at 20:05
  • The `scale` filter sets the SAR/DAR, so you can get a squeezed/stretched video (same problem as @MarshallHouse ?). Using `color=size=960x720` as source (and removing the filter) solves this. – Remko Aug 02 '17 at 07:55
  • 1
    What's with the extraneous scale (resize) filter? That confuses things. A bare bones example would be great. – r_alex_hall Jan 01 '18 at 00:43
  • I used a similar script to this answer, however I did not need the black background as input. Instead, you can overlay the second input onto the first directly. ie. `[0:v]format=pix_fmts=yuva420p[va0];[1:v]formatformat=pix_fmts=yuva420p,fade=t=in:st=0:d=1:alpha=1[va1];[va0][va1]overlay=format=yuv420[outv]` – Nightfirecat Jul 14 '21 at 05:54
  • @r_alex_hall See my answer for a barebones solution. – Hashim Aziz Oct 18 '21 at 02:57
18

FFmpeg now has a crossfade filter, released in version 4.3.

ffmpeg -i 1.mp4 -i 2.mp4 -filter_complex "xfade=offset=4.5:duration=1" output.mp4

And similarly, there's an audio version.

ffmpeg -i 1.mp4 -i 2.mp4 -filter_complex "xfade=offset=4.5:duration=1;acrossfade=duration=1" output.mp4
OrangeDog
  • 1,191
  • 12
  • 25
11

ffmpeg-concat is the easiest way to accomplish what you want and allows you to use a bunch of sexy OpenGL transitions, with the default being crossfade.

ffmpeg-gl-transition is a custom ffmpeg filter which allows you to use GLSL to smoothly transition between two video streams. This filter is significantly easier to use and customize than the alternatives listed here.

This filter supports a large list of transition types, with the default being crossfade.

./ffmpeg -i 0.mp4 -i 1.mp4 -filter_complex "gltransition=duration=4:offset=1.5" out.mp4
fisch2
  • 219
  • 2
  • 4
5

This is how I did :

  • ffmpeg version N-77197-gdf2ce13
  • 2 videos of 4 seconds each.
  • Need to join it with fade between them.
  • videos are 25 fps.

1) Add fade out (light to dark) at the end of the 1st and fade in (dark to light) at the beggining of the 2nd:

ffmpeg -i 1.mp4 -y -vf fade=out:76:24 1f.mp4

ffmpeg -i 2.mp4 -y -vf fade=in:0:25 2f.mp4

76:24 mean the fade out will start frame 76 and will finish 24 frames later = 1s fade out.

0:25 mean the fade in will start frame 0 and will finish 25 frames later.

2) Merge the 2 videos

Convert all to TS

ffmpeg -i 1f.mp4 -c copy -bsf:v h264_mp4toannexb -f mpegts 1f.ts

ffmpeg -i 2f.mp4 -c copy -bsf:v h264_mp4toannexb -f mpegts 2f.ts

Merge

ffmpeg -i "concat:1f.ts|2f.ts" -bsf:a aac_adtstoasc -c copy output.mp4

Thanks to:

http://www.bogotobogo.com/FFMpeg/ffmpeg_fade_in_fade_out_transitions_effects_filters.php

Erwan
  • 91
  • 1
  • 1
  • 7
    This is not a crossfade. – Gyan Jan 27 '16 at 15:33
  • 4
    What @Mulvya said. A crossfade fades one clip out at the same time as another is fading in. The first clip has its transparency steadily increased over the duration of the crossfade; the second has its transparency steadily decreased. – intuited May 23 '16 at 06:44
  • and the question already describes this method – OrangeDog Jun 11 '20 at 20:17
5

OrangeDog is right that native crossfades were recently introduced to FFmpeg (version 4.3.2, released on 8th June 2020) via the xfade filter. However, his answer is incomplete and doesn't explain how the filter works or take into account how complex it can be.


Using the xfade filter

The offset parameter is the point at the first input (in seconds) that you want the transition to start, and the duration parameter is how long (in seconds) you want the transition to take before it has finished transitioning to the second input.

The most common use of a crossfade is to fade from the very end of your first input to the beginning of your second one. However, this is trickier to do with xfade than it first seems: if the offset parameter is set too low it will fade too early and take a part of the end of the first input with it.

If the offset is set too high (more than VIDEO 1 DURATION + FADE DURATION) the crossfade will fail to be applied at all because the xfade filter considers this invalid - I'm guessing because it disregards the second input entirely in its calculations.

This means that the best that can be done with the xfade filter is to truncate as little as possible from the end of the first input, which typically means "only" a lost second or two of video at the end. This might be fine for your needs, in which case the xfade filter will be perfect for you.

If it isn't, then skip ahead to the end to use the older crossfade solution which - despite being a little more complicated to set up - does include the second input in its calculations, resulting in a crossfade that completely preserves the video streams of both inputs.

To begin, get the duration in seconds of your first input:

ffprobe -v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 input1.mp4

Then get an upper limit value by doing VIDEO1 DURATION - FADE DURATION.

This upper limit is the highest offset value you can set - any higher and the xfade filter will fail to apply the crossfade. I recommend starting with it as the initial value and gradually lowering it until you're satisfied with the end result.

For example, where the duration of your first input is 170.500 seconds, and you want the crossfade to last for 2 seconds, the upper limit value will be 168.500:

ffmpeg -i input1.mp4 -i input2.mp4 -filter_complex \
"[0:v][1:v]xfade=duration=2:offset=168.500[outv];
 [0:a][1:a]acrossfade=duration=2[outa]" -map [outv] -map [outa] Output.mp4

If the result of that command is a crossfade in which the first input stream abruptly ends before the crossfade's transition does, then gradually lower the offset value by a second each time until the transition looks as smooth as possible.

When running these experimental runs, you can add the -preset ultrafast option to speed up encoding time - just remember to run the final encode without it to maximise compression for your final output file.

Using the transition parameter, xfade also allows you to make use of crossfade transitions other than the default fade. This example uses the pixelise transition:

ffmpeg -i input1.mp4 -i input2.mp4 -filter_complex \
"[v0][v1]xfade=transition=pixelise:duration=2:offset=168.721[outv];
 [0:a][1:a]acrossfade=duration=2[outa]" -map [outv] -map [outa] Output.mp4

The full list of available transitions can be found on the FFmpeg wiki.


If your sources don't have the same timebases and frame rates

Note that any of the above commands may fail with one of the following errors:

First input link main timebase (1/1500) do not match the corresponding second input link xfade timebase (1/30000)

First input link main frame rate (30/1) do not match the corresponding second input link xfade frame rate (30000/1001)

These errors mean your inputs have differing timebases and framerates respectively, which is highly likely when dealing with inputs from different sources. In these cases you will need the command below instead.

For reasons that are still confusing to me since everything is being re-encoded anyway, the xfade filter requires that the TBN and frame rate of each input be the same, similar to how the concat demuxer requires the TBN, resolution, codec and other attributes be the same.

If this is the case for you and you get one of the above errors, you'll need to set a common TBN and frame rate for each of your video streams before applying the xfade filter to them:

ffmpeg -i upscaled.mp4 -i outro.mp4 -filter_complex \
"[0:v]settb=AVTB,fps=30/1[v0];
 [1:v]settb=AVTB,fps=30/1[v1];
 [v0][v1]xfade=transition=fade:duration=2:offset=168.721[outv];
 [0:a][1:a]acrossfade=duration=3[outa]" -map [outv] -map [outa] Output.mp4

As far as I'm aware there's no equivalent to the AVTB constant for frame rates, so replace 30/1 above with an output frame rate that is suitable to you. 30/1 (which is FFmpeg notation for 30 FPS) is probably a safe bet but I'm no expert on frame rates and their implications.


Using a "traditional" crossfade

If, like me, you weren't completely happy with the results of the xfade filter, you can do a crossfade the old way instead.

This is a modified version of Gyan's answer here, and essentially works similarly to ptQa's answer except that it's less complicated and doesn't contain any filters that are irrelevant to the actual crossfade. It can essentially be thought of as a middle ground between the simplicity of the xfade filter and the complexity of ptQa's solution.

ffmpeg -i input1.mp4 -i input2.mp4 -filter_complex \
"color=black:WXH:d=VIDEO1+VIDEO2-FADE[base]; \
[0:v]setpts=PTS-STARTPTS[v0]; \
[1:v]format=yuva420p,fade=in:st=0:d=FADEDURATION:alpha=1,
    setpts=PTS-STARTPTS+((VIDEO1LENGTH-FADEDURATION)/TB)[v1]; \
[base][v0]overlay[tmp]; \
[tmp][v1]overlay,format=yuv420p[fv]; \
[0:a][1:a]acrossfade=d=AUDIOFADEDURATION[fa]" \
-map [fv] -map [fa] \
output.mp4

Replace WXH with your video's resolution.

Replace VIDEO1+VIDEO2-FADE with the result of the sum - what you get by adding the first input's length in seconds to the second input's length in seconds, minus the fade duration. Use ffprobe -v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 input.mp4 to get the duration of each input.

Finally, replace each instance of VIDEO1LENGTH and VIDEO2LENGTH with their values, and FADEDURATION and AUDIOFADEDURATION with what you'd like them to be.

So if I wanted to crossfade between two videos that are 1920x1080, with the first input having a length of 170.300 seconds and the second a length of 9 seconds, and I want a crossfade of 2 seconds between them with an audio crossfade of 4 seconds, my command would look like this:

ffmpeg -i input1.mp4 -i input2.mp4 \
-filter_complex \
"color=black:1920x1080:d=177.300[base]; \
[0:v]setpts=PTS-STARTPTS[v0]; \
[1:v]format=yuva420p,fade=in:st=0:d=2:alpha=1,setpts=PTS-STARTPTS+((170.300-2)/TB)[v1]; \
[base][v0]overlay[tmp]; \
[tmp][v1]overlay,format=yuv420p[fv]; \
[0:a][1:a]acrossfade=d=4[fa]" \
-map [fv] -map [fa] Output.mp4
Hashim Aziz
  • 11,898
  • 35
  • 98
  • 166
1

No sure for how long this has already been working, but at least as of 2021, an easier solution is ffmpeg's own xfade filter:

ffmpeg -i first.mp4 -i second.mp4 \
       -filter_complex "xfade=transition=fade:offset=60:duration=1" \
       out.mp4

...does a crossfade from first.mp4 to second.mp4 where the transition starts at 60 seconds and lasts 1 second.

Hauke P.
  • 531
  • 1
  • 8
  • 23
  • 1
    This just duplicates my answer... – OrangeDog Jan 29 '21 at 12:33
  • 1
    @OrangeDog: I see that [you've expanded your answer quite a lot in the last 7 hours](https://superuser.com/posts/1559967/revisions). Thanks for doing that. I'm sure this will help someone else in the future. – Hauke P. Jan 29 '21 at 20:02
0

You can use editly for this:

npm i -g editly
editly --transition-name linearblur --transition-duration 1 in.mp4 in.mp4 --out out.mp4

--transition-name can be found here