crossfade between 2 videos using ffmpeg

50,496

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
Share:
50,496

Related videos on Youtube

Mugba
Author by

Mugba

Updated on September 18, 2022

Comments

  • Mugba
    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!

  • Mugba
    Mugba almost 10 years
    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?
  • ptQa
    ptQa almost 10 years
    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.
  • Mugba
    Mugba almost 10 years
    That 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
    Jim Miller almost 10 years
    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
    Jim Miller almost 10 years
    BTW, I think there's an error in the answer's sample command -- it should be -i 1.mp4 -i 2.mp4..., right?
  • Jon G
    Jon G about 9 years
    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
  • ptQa
    ptQa about 9 years
    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.
  • Gyan
    Gyan over 8 years
    This is not a crossfade.
  • Emery King
    Emery King over 8 years
    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.
  • intuited
    intuited about 8 years
    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.
  • jrkt
    jrkt over 7 years
    How would you be able to do this for a variable number of videos like 5? How would the filter_complex be structured?
  • Remko
    Remko almost 7 years
    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.
  • Elisa Cha Cha
    Elisa Cha Cha over 6 years
    Have you considered submitting a patch to FFmpeg?
  • fisch2
    fisch2 over 6 years
    I definitely will, I just need to fix some small issues and test more first. Good call!
  • r_alex_hall
    r_alex_hall over 6 years
    What's with the extraneous scale (resize) filter? That confuses things. A bare bones example would be great.
  • kraftydevil
    kraftydevil over 5 years
    is this in the official FFmpeg? I can't seem to locate it
  • OrangeDog
    OrangeDog about 4 years
    and the question already describes this method
  • fakedad
    fakedad almost 4 years
    It 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
    OrangeDog over 3 years
    This just duplicates my answer...
  • Hauke P.
    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
    durka42 over 3 years
    It's available on Homebrew!
  • durka42
    durka42 over 3 years
    Unfortunately this doesn't work. It copies the audio but produces a completely blank output video.
  • OrangeDog
    OrangeDog over 3 years
    @durka42 it works for me. You may need additional stream selectors depending on your inputs.
  • Nightfirecat
    Nightfirecat almost 3 years
    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_fmt‌​s=yuva420p,fade=t=in‌​:st=0:d=1:alpha=1[va‌​1];[va0][va1]overlay‌​=format=yuv420[outv]
  • Hashim Aziz
    Hashim Aziz over 2 years
    @r_alex_hall See my answer for a barebones solution.