I received an anonymous comment on a past post FFMpeg Blending specifically asking how two video files could be concatenated with a blending effect transition.
Note: anonymous commenter/reader, if you find yourself returning and find this post useful please drop a quick comment so I know the effort in addressing your question wasn't a lost. Thanks!
In past posts I feel we've covered most of the necessary filtering examples as well as concatenation of videos, it's a not-so-simple matter of putting them all together. Let's start with constraints/complications that can bite you.
Constraints/Complications
Input Files
If your input files come from a uniform source (e.g. your phone, video camera, snippets from a larger video file) you may not have to worry about these factors. For this example however, we will grab a couple videos from YouTube from different sources so we will have to address some issues.
In general, your input files need to be similarly formatted. Similar frame rates, video resolutions and audio tracks. I've wasted more hours than I care to mention forgetting to take care in this step, let that be a word of caution to emphasize take a few minutes to check this beforehand to avoid making the mistakes I've made. Best-case, if your formats don't match you get slapped in the face with an error from FFmpeg, worse case it succeeds in delivering a video that looks like garbage leaving you with an Easter Egg Hunt in finding the issue. When in doubt, enforce some standard format prior to doing anything more sophisticated.
Let's start with video resolution; your input files (for concatenation and blends) need to be identically sized, not kinda similarly sized, but identically sized. Source files that have different aspect ratios are particularly problematic because resizing them may not give you the required sizing leaving you to crop/pad accordingly.
A consistent frame rate (FPS) is necessary for time-based filters, like the blending, make sure your frame rate (FPS) is identical for both input videos, otherwise you'll encounter unexpected results that may resemble this;
A consistent auto track existence; just shy of a bazillion times I've attempted to concatenate two videos only to find my audio tracks absent/stalling. For example, applying an image snippet before/after a video results in stalling or absent audio until I relearn that I need to apply an empty audio track to the image video, otherwise concatenation doesn't know how to properly handle concatenation of one video with audio and another without. All your source media for concatenation should all have an audio track or none have an audio tracks. Mixing and matching will only give you headaches in the end.
Example
Let's move on to our example.
Let's snag two videos from YouTube
$ youtube-dl -f mp4 -o $@ https://www.youtube.com/watch?v=_oHpWw7L3d4
$ ffprobe -i yt-stingray.mp4ffprobe version 4.2.2 Copyright (c) 2007-2019 the FFmpeg developersbuilt with gcc 5.4.0 (Ubuntu 5.4.0-6ubuntu1~16.04.12) 20160609configuration: --enable-libx264 --enable-nonfree --enable-gpl --enable-libfreetype --enable-libmp3lame --enable-libzmqlibavutil 56. 31.100 / 56. 31.100libavcodec 58. 54.100 / 58. 54.100libavformat 58. 29.100 / 58. 29.100libavdevice 58. 8.100 / 58. 8.100libavfilter 7. 57.100 / 7. 57.100libswscale 5. 5.100 / 5. 5.100libswresample 3. 5.100 / 3. 5.100libpostproc 55. 5.100 / 55. 5.100Input #0, mov,mp4,m4a,3gp,3g2,mj2, from 'yt-stingray.mp4':Metadata:major_brand : mp42minor_version : 0compatible_brands: isommp42creation_time : 2019-05-22T15:56:25.000000ZDuration: 00:47:09.44, start: 0.000000, bitrate: 397 kb/sStream #0:0(und): Video: h264 (Constrained Baseline) (avc1 / 0x31637661), yuv420p(tv, smpte170m), 480x360 [SAR 1:1 DAR 4:3], 299 kb/s, 23.98 fps, 23.98 tbr, 24k tbn, 47.95 tbc (default)Metadata:creation_time : 2019-05-22T15:56:25.000000Zhandler_name : ISO Media file produced by Google Inc. Created on: 05/22/2019.Stream #0:1(eng): Audio: aac (LC) (mp4a / 0x6134706D), 44100 Hz, stereo, fltp, 95 kb/s (default)Metadata:creation_time : 2019-05-22T15:56:25.000000Zhandler_name : ISO Media file produced by Google Inc. Created on: 05/22/2019.$ ffprobe -i yt-hardcastle01.mp4ffprobe version 4.2.2 Copyright (c) 2007-2019 the FFmpeg developersbuilt with gcc 5.4.0 (Ubuntu 5.4.0-6ubuntu1~16.04.12) 20160609configuration: --enable-libx264 --enable-nonfree --enable-gpl --enable-libfreetype --enable-libmp3lame --enable-libzmqlibavutil 56. 31.100 / 56. 31.100libavcodec 58. 54.100 / 58. 54.100libavformat 58. 29.100 / 58. 29.100libavdevice 58. 8.100 / 58. 8.100libavfilter 7. 57.100 / 7. 57.100libswscale 5. 5.100 / 5. 5.100libswresample 3. 5.100 / 3. 5.100libpostproc 55. 5.100 / 55. 5.100Input #0, mov,mp4,m4a,3gp,3g2,mj2, from 'yt-hardcastle01.mp4':Metadata:major_brand : mp42minor_version : 0compatible_brands: isommp42encoder : GoogleDuration: 00:01:48.62, start: 0.000000, bitrate: 1362 kb/sStream #0:0(und): Video: h264 (High) (avc1 / 0x31637661), yuv420p(tv, bt709), 1280x720 [SAR 1:1 DAR 16:9], 1232 kb/s, 29.97 fps, 29.97 tbr, 30k tbn, 59.94 tbc (default)Metadata:handler_name : ISO Media file produced by Google Inc.Stream #0:1(und): Audio: aac (LC) (mp4a / 0x6134706D), 44100 Hz, stereo, fltp, 127 kb/s (default)Metadata:handler_name : ISO Media file produced by Google Inc.
Concatenation
$ cat videoXX.mp4.txtfile 'yt-stingray.mp4'file 'yt-hardcastle01.mp4'$ ffmpeg -y -f concat -i videoXX.mp4.txt videoXX.mp4
Blending
[Parsed_amerge_0 @ 0x2e8b1c0] No channel layout for input 1[Parsed_amerge_0 @ 0x2e8b1c0] Input channel layouts overlap: output layout will be determined by the number of distinct input channels[Parsed_blend_2 @ 0x2dc7c80] First input link top parameters (size 480x360) do not match the corresponding second input link bottom parameters (size 1280x720)[Parsed_blend_2 @ 0x2dc7c80] Failed to configure output pad on Parsed_blend_2Error reinitializing filters!Failed to inject frame into filter network: Invalid argumentError while processing the decoded data for stream #1:0Conversion failed!
$ ffmpeg -i yt-stingray-scaled.mp4 -ss 4 -t 10 -strict -2 clip01-scaled.mp4$ ffmpeg -i yt-hardcastle-scaled.mp4 -ss 4 -t 10 -strict -2 clip02-scaled.mp4
- segment1.mp4 : first 8 seconds of clip01-scaled.mp4
- segment2.mp4 : last 2 seconds of clip01-scaled.mp4
- segment3.mp4 : first 2 seconds of clip02-scaled.mp4
- segment4.mp4 : last 8 seconds of clip02-scaled.mp4
$ ffmpeg -i clip01-scaled.mp4 -t 8.032000 -target ntsc-dvd -strict -2 segment1.mp4$ ffmpeg -i clip01-scaled.mp4 -ss 8.032000 -target ntsc-dvd -strict -2 segment2.mp4$ ffmpeg -i clip02-scaled.mp4 -ss 0 -t 2 -target ntsc-dvd -strict -2 segment3.mp4$ ffmpeg -i clip02-scaled.mp4 -ss 2 -target ntsc-dvd -strict -2 segment4.mp4
$ ffmpeg -i segment2.mp4 -i segment3.mp4 -filter_complex "[0:v]setpts=PTS-STARTPTS[v0],[1:v]setpts=PTS-STARTPTS[v1],[v0][v1]blend=all_expr='A*(1-(T/2)) B*(T/2)'" -filter_complex "amerge=inputs=2" -ac 2 -shortest -target ntsc-dvd segment2a.mp4
Reassembling Video Clips
$ cat video.mp4.txtfile 'segment1.mp4'file 'segment2a.mp4'file 'segment4.mp4'$ ffmpeg -y -f concat -i video.mp4.txt video.mp4
$ cat -n Makefile1 TransitionDuration=22 all: video.mp434 segment1.mp4 : clip01-scaled.mp45 ${SH} ffmpeg -i $< -t $(shell echo $(shell ffprobe -loglevel quiet -show_format $< -show_entries format=duration | grep duration | cut -f 2 -d '=') -${TransitionDuration} | bc) -target ntsc-dvd -strict -2 $@67 segment2.mp4 : clip01-scaled.mp48 ${SH} ffmpeg -i $< -ss $(shell echo $(shell ffprobe -loglevel quiet -show_format $< -show_entries format=duration | grep duration | cut -f 2 -d '=') -${TransitionDuration} | bc) -target ntsc-dvd -strict -2 $@910 segment3.mp4 : clip02-scaled.mp411 ${SH} ffmpeg -i $< -ss 0 -t $(shell echo ${TransitionDuration} -1 | bc) -target ntsc-dvd -strict -2 $@1213 segment4.mp4 : clip02-scaled.mp414 ${SH} ffmpeg -i $< -ss ${TransitionDuration} -target ntsc-dvd -strict -2 $@1516 segment2a.mp4 : segment2.mp4 segment3.mp417 ${SH} ffmpeg -i $(shell echo $^ | cut -f 1 -d ' ') -i $(shell echo $^ | cut -f 2 -d ' ') -filter_complex "[0:v]setpts=PTS-STARTPTS[v0],[1:v]setpts=PTS-STARTPTS[v1],[v0][v1]blend=all_expr='A*(1-(T/${TransitionDuration})) B*(T/${TransitionDuration})'" -filter_complex "amerge=inputs=2" -ac 2 -shortest -target ntsc-dvd $@1819 video.mp4: segment1.mp4 segment2a.mp4 segment4.mp420 ${SH} rm [email protected] | true21 ${SH} for f in $^; do echo "file '$$f'" >> [email protected]; done22 ${SH} ffmpeg -y -f concat -i [email protected] $@23 ${SH} mplayer $@2425 clip01.mp4: yt-stingray.mp426 ${SH} ffmpeg -i $< -ss 4 -t 10 -strict -2 $@2728 clip02.mp4: yt-hardcastle01.mp429 ${SH} ffmpeg -i $< -ss 9 -t 10 -strict -2 $@3031 yt-stingray.mp4:32 ${SH} youtube-dl -f mp4 -o $@ https://www.youtube.com/watch?v=aXTk9VPZ4Gg3334 yt-hardcastle01.mp4:35 ${SH} youtube-dl -f mp4 -o $@ https://www.youtube.com/watch?v=_oHpWw7L3d43637 %-scaled.mp4: %.mp438 ${SH} ffmpeg -i $< -vf "scale=-1:720,pad=1280:ih:(ow-iw)/2" -strict -2 $@394041 clean:42 ${RM} *.mp4