3

I recently had the following problem:

I wanted, using ffmpeg, to concatenate several videos (with audio), adding black clips (with null audio) between them , and lastly fade the videos from those black clips (fade-in) and to those black clips (fade-out).

Schematically, with 2 videos for instance:

black (1s) --> part_1 (60s) --> black (3s) --> part_2 (120s) --> black (1s)
            ^                ^              ^                 ^
       (2s-fade-in)     (2s-fade-out)  (2s-fade-in)      (2s-fade-out)

I was looking for a one-liner solution (to avoid re-encoding original videos more than once), but only found documentation or help for each part of the problem.

Having figured it out, I wanted to document my solution for those after me who will stumble on a similar problem :)

1 Answer 1

3

So here is what I came up with.

A not fully one-liner solution

1) Generate black clips

ffmpeg -f lavfi -i "color=black:s=1920x1080:r=30" \
 -f lavfi -i "anullsrc=r=48000:cl=stereo" \
 -c:v libx264 -c:a aac -t 3 empty_3s.mp4

Use lavfi filters to generate your empty clip. Of course, change the parameters according to your videos and what you want to achieve. Especially:

  • in first (video) source filter:
    • s (size) should match that of the videos you want to concatenate.
    • r (frame rate) should also match your videos (especially if your want a constant frame rate output)
  • in second (audio) source filter:
    • r (sample rate) should match that of the audio tracks from the videos you want to concatenate.
    • cl (channel layout) should also match the audio from your videos

Output parameters (codecs for instance) should match those of your videos. Use -t to set the length (in seconds) of the empty clip.

2) Concatenate and fade your videos using a complex filtergraph

ffmpeg -i empty_1s.mp4 -i part_1.mp4 -i empty_3s.mp4 -i part_2.mp4 -i empty_1s.mp4 \
 -filter_complex \
  "[1:v] fade=t=in:st=0:d=2, fade=t=out:st=58:d=2 [1v]; \
   [1:a] afade=t=in:st=0:d=2, afade=t=out:st=58:d=2 [1a]; \
   [3:v] fade=t=in:st=0:d=2, fade=t=out:st=118:d=2 [3v]; \
   [3:a] afade=t=in:st=0:d=1, afade=t=out:st=118:d=2 [3a]; \
   [0:v] [0:a] [1v] [1a] [2:v] [2:a] [3v] [3a] [4:v] [4:a] concat=n=5:v=1:a=1 [v] [a]" \
 -map "[v]" -map "[a]" -c:v libx264 -c:a aac output.mp4

Explanation:

  • we first specify the inputs (-i) that we will concatenate
  • we then set a complex filter (-filter_complex), that we use to:
    • first, fade in and out our videos. To that end we use the fade (for video) and afade (for audio) filters. We specify the start (st) and duration (d) for each fade. We output the results with outpad names ([1v], [1a]...).
    • then, concatenate the black clips and the previously faded videos, using the concat filter. We specify the number of input segments n (i.e. the number of video+audio sets, in our case 5) we want to concat, and the number of video (v=1) and audio (a=1) streams for the output.
  • we map the final output to the output streams from the concat filter
  • specify the encoding parameters to whatever you want :)

Important notes:

  • your videos (inputs) must have the same characteristics (pixel and sample format for video, sample rate and channel layout for audio). See concat documentation for more info.
  • in the fade filter, we specify start time and duration in seconds, but you can use frames or samples as well with s and n (video) or ss and ns (audio) options. See afade and fade filters documentation.
  • you must know the duration of your videos for the fade filter, especially for fade-outs. See this question for hints on how two fade-out clips without having to manually specify start time.

True one-liner

If you really want a one-liner (and not having to encode empty black clips), you can use:

ffmpeg \
 -f lavfi -t 1 -i "color=black:s=1920x1080:r=30" -f lavfi -t 1 -i "anullsrc=r=48000:cl=2" \
 -i part_1.mp4 \
 -f lavfi -t 3 -i "color=black:s=1920x1080:r=30" -f lavfi -t 3 -i "anullsrc=r=48000:cl=2" \
 -i part_2.mp4 \
 -f lavfi -t 1 -i "color=black:s=1920x1080:r=30" -f lavfi -t 1 -i "anullsrc=r=48000:cl=2" \
 -filter_complex \
  "[0:v] null [0v]; [1:a] anull [0a]; [3:v] null [2v]; [4:a] anull [2a]; \
   [6:v] null [4v]; [7:a] anull [4a]; \
   [2:v] fade=t=in:st=0:d=2, fade=t=out:st=58:d=2 [1v]; \
   [2:a] afade=t=in:st=0:d=2, afade=t=out:st=58:d=2 [1a]; \
   [5:v] fade=t=in:st=0:d=1, fade=t=out:st=118:d=2 [3v]; \
   [5:a] afade=t=in:st=0:d=1, afade=t=out:st=118:d=2 [3a]; \
   [0v] [0a] [1v] [1a] [2v] [2a] [3v] [3a] [4v] [4a] concat=n=5:v=1:a=1 [v] [a]" \
 -map "[v]" -map "[a]" -c:v libx264 -c:a aac output.mp4

In that case we must beware of the input indices, as black video clips and null audio clips are separated inputs (I didn't figure out how to put them as two streams in a single input, if you have an idea feel free to share it).

Here I applied null and anull filters to those inputs to give them outpad tags, in order to use consistent identifiers in the concat filter. But this is purely optional.

Hope this helps, feel free to improve my solution with your insights :)

1
  • You don't need to generate distinct source filter inputs for the null video and audio segments. They can be used multiple times in the filter_complex.
    – Gyan
    Dec 26, 2017 at 16:33

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.