crossfade between 2 videos using ffmpeg
Solution 1
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.
Solution 2
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
Solution 3
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
Solution 4
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
Solution 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
Related videos on Youtube
Mugba
Updated on September 18, 2022Comments
-
Mugba almost 2 years
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!
-
Ciro Santilli Путлер Капут 六四事 about 8 years
-
-
Mugba almost 10 yearsHi @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?
-
ptQa almost 10 yearsOh, 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.
-
Mugba almost 10 yearsThat solved it, thanks. I uploaded a sample video showing the crossfading in case anyone wants to see it in action, youtu.be/JqorgXAjjTo
-
Jim Miller almost 10 yearsSame 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 almost 10 yearsBTW, I think there's an error in the answer's sample command -- it should be
-i 1.mp4 -i 2.mp4...
, right? -
Jon G about 9 yearsI 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
-
ptQa about 9 yearsIt 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.
-
Gyan over 8 yearsThis is not a crossfade.
-
Emery King over 8 yearsWhat'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.
-
intuited about 8 yearsWhat @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.
-
jrkt over 7 yearsHow would you be able to do this for a variable number of videos like 5? How would the filter_complex be structured?
-
Remko almost 7 yearsThe
scale
filter sets the SAR/DAR, so you can get a squeezed/stretched video (same problem as @MarshallHouse ?). Usingcolor=size=960x720
as source (and removing the filter) solves this. -
Elisa Cha Cha over 6 yearsHave you considered submitting a patch to FFmpeg?
-
fisch2 over 6 yearsI definitely will, I just need to fix some small issues and test more first. Good call!
-
r_alex_hall over 6 yearsWhat's with the extraneous scale (resize) filter? That confuses things. A bare bones example would be great.
-
kraftydevil over 5 yearsis this in the official FFmpeg? I can't seem to locate it
-
OrangeDog about 4 yearsand the question already describes this method
-
fakedad almost 4 yearsIt appears to have been added in 4.3. For anyone on Ubuntu 20.04 like myself, this means that you will need to somehow download a newer version or build it yourself. (And it's well worth it!)
-
OrangeDog over 3 yearsThis just duplicates my answer...
-
Hauke P. over 3 years@OrangeDog: I see that you've expanded your answer quite a lot in the last 7 hours. Thanks for doing that. I'm sure this will help someone else in the future.
-
durka42 over 3 yearsIt's available on Homebrew!
-
durka42 over 3 yearsUnfortunately this doesn't work. It copies the audio but produces a completely blank output video.
-
OrangeDog over 3 years@durka42 it works for me. You may need additional stream selectors depending on your inputs.
-
Nightfirecat almost 3 yearsI 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]
-
Hashim Aziz over 2 years@r_alex_hall See my answer for a barebones solution.