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