Create a Grid of Videos with FFmpeg's Mosaic Filter
Update Sept. 2022
Check out this stack overflow answer where user llogan
provides a better way to accomplish this using hstack, vstack and xstack filters.
You’re probably familiar with the Brady Bunch grid of 9 faces that appears at the start and end of the show. It’s pretty iconic and even if you haven’t seen the show you’ll probably recognize it as being from the Brady Bunch.
I was bored one day and wanted to see if I could get this grid effect from FFmpeg. I’m using Ubuntu Linux 18.04, but FFmpeg runs on pretty much every operating system, so you should be OK with whatever.
I did some googling and found out that it’s technically called a “mosaic” and it can be done with FFmpeg’s filter_complex. However, the example in that link only has a 2x2 grid, and everyone knows that the Brady Bunch grid is 3x3. Unfortunately, this meant I actually had to read through the documentation and untangle that horrid mess of a command to make it fit my application. For reference, it looks like this:
ffmpeg
-i 1.avi -i 2.avi -i 3.avi -i 4.avi
-filter_complex "
nullsrc=size=640x480 [base];
[0:v] setpts=PTS-STARTPTS, scale=320x240 [upperleft];
[1:v] setpts=PTS-STARTPTS, scale=320x240 [upperright];
[2:v] setpts=PTS-STARTPTS, scale=320x240 [lowerleft];
[3:v] setpts=PTS-STARTPTS, scale=320x240 [lowerright];
[base][upperleft] overlay=shortest=1 [tmp1];
[tmp1][upperright] overlay=shortest=1:x=320 [tmp2];
[tmp2][lowerleft] overlay=shortest=1:y=240 [tmp3];
[tmp3][lowerright] overlay=shortest=1:x=320:y=240
"
-c:v libx264 output.mkv
Now, you can read the linked page if you want every detail, but the gist of it is, you pass in 1.avi, 2.avi, 3.avi, and 4.avi, and it gives them each a 320x240 section of a 640x480 window.
The setpts
filter sets the Presentation TimeStamp. In this case, we’re starting all videos from the same timestamp, PTS-STARTPTS
which refers to the start of the video.
If we want to do a Brady Bunch grid we’re going to need to pass in nine videos and divide the square into thirds. I wanted a final resolution of 1920x1080.
Here’s what my command ended up looking like:
ffmpeg
-i 1.avi -i 2.avi -i 3.avi -i 4.avi -i 5.avi -i 6.avi -i 7.avi
-i 8.avi -i 9.avi
-filter_complex "
nullsrc=size=1920x1080 [base];
[0:v] setpts=PTS-STARTPTS, scale=640x360 [upperleft];
[1:v] setpts=PTS-STARTPTS, scale=640x360 [uppercenter];
[2:v] setpts=PTS-STARTPTS, scale=640x360 [upperright];
[3:v] setpts=PTS-STARTPTS, scale=640x360 [centerleft];
[4:v] setpts=PTS-STARTPTS, scale=640x360 [centercenter];
[5:v] setpts=PTS-STARTPTS, scale=640x360 [centerright];
[6:v] setpts=PTS-STARTPTS, scale=640x360 [lowerleft];
[7:v] setpts=PTS-STARTPTS, scale=640x360 [lowercenter];
[8:v] setpts=PTS-STARTPTS, scale=640x360 [lowerright];
[base][upperleft] overlay=shortest=1 [tmp1];
[tmp1][uppercenter] overlay=shortest=1:x=640 [tmp2];
[tmp2][upperright] overlay=shortest=1:x=1280 [tmp3];
[tmp3][centerleft] overlay=shortest=1:y=360 [tmp4];
[tmp4][centercenter] overlay=shortest=1:x=640:y=360 [tmp5];
[tmp5][centerright] overlay=shortest=1:x=1280:y=360 [tmp6];
[tmp6][lowerleft] overlay=shortest=1:y=720 [tmp7];
[tmp7][lowercenter] overlay=shortest=1:x=640:y=720 [tmp8];
[tmp8][lowerright] overlay=shortest=1:x=1280:y=720
"
-c:v libx264 output.mkv
To actually get it to run you’ll need to format it on one line:
ffmpeg -i 1.avi -i 2.avi -i 3.avi -i 4.avi -i 5.avi -i 6.avi -i 7.avi -i 8.avi -i 9.avi -filter_complex "nullsrc=size=1920x1080 [base]; [0:v] setpts=PTS-STARTPTS, scale=640x360 [upperleft]; [1:v] setpts=PTS-STARTPTS, scale=640x360 [uppercenter]; [2:v] setpts=PTS-STARTPTS, scale=640x360 [upperright]; [3:v] setpts=PTS-STARTPTS, scale=640x360 [centerleft]; [4:v] setpts=PTS-STARTPTS, scale=640x360 [centercenter]; [5:v] setpts=PTS-STARTPTS, scale=640x360 [centerright]; [6:v] setpts=PTS-STARTPTS, scale=640x360 [lowerleft]; [7:v] setpts=PTS-STARTPTS, scale=640x360 [lowercenter]; [8:v] setpts=PTS-STARTPTS, scale=640x360 [lowerright]; [base][upperleft] overlay=shortest=1 [tmp1]; [tmp1][uppercenter] overlay=shortest=1:x=640 [tmp2]; [tmp2][upperright] overlay=shortest=1:x=1280 [tmp3]; [tmp3][centerleft] overlay=shortest=1:y=360 [tmp4]; [tmp4][centercenter] overlay=shortest=1:x=640:y=360 [tmp5]; [tmp5][centerright] overlay=shortest=1:x=1280:y=360 [tmp6]; [tmp6][lowerleft] overlay=shortest=1:y=720 [tmp7]; [tmp7][lowercenter] overlay=shortest=1:x=640:y=720 [tmp8]; [tmp8][lowerright] overlay=shortest=1:x=1280:y=720" -c:v libx264 output.mk
Maybe in a future blog we’ll wrap it in a script to make it easier to pass in a desired resolution and a desired grid dimension (if 3x3 isn’t what you’re looking for). But what are you waiting for, grab some videos and go make a mosaic with FFmpeg! Do it!
Update Oct. 2020
I was looking at backlinks to this blog, and found a stack overflow answer where user Jao
included a python snippet to automate the creation of the command above. Awesome!