api.video
SearchCtrl K

Features

Developers

A very Brady video: Using FFMPEG to recreate the Brady Bunch introduction

September 14, 2021 - Doug in ffmpeg

🎶🎶🎶 Now here's the story... 🎶🎶🎶

As a child, the Brady Bunch was a regular part of my afternoon. It wasn't a new show, but every day after school there were cartoons and the Brady Bunch reruns on TV.

For those not from the USA (it was interesting that at api.video, it was just the Americans who knew the show), the story revolves around a single dad with 3 boys who marries a single mom with 3 girls. They hired a wacky housekeeper, and together the nine characters were the Brady Bunch. Years of laugh track hijinks followed their adventures.

The opening

The image above is a short GIF, but each character is looking around and smiling at the other members of the Brady clan. It's an iconic US television look, and I thought it would be fun to re-create a "Brady Bunch" video using FFMPEG to interlace 9 different videos into that familiar 9-grid pattern.

A huge Thank You! to Anne-Sophie and Cedric for recording all of our colleagues (and of course to my api.video colleagues for going along with this fun project).

Starting my FFMPEG command

After pitching this idea to the team, I woke up a few days later to a number of videos shared in a Slack channel. Woo hoo! Here we go!

When composing an FFMPEG command, the first step is to input all of the files you will be using. We'll do a -filter_complex to resize and build the video layout, and then output the video in done.mp4.

ffmpeg -i sorin.mp4 -i olia.mp4	-i maria.mp4  -i josephine.mp4 
-i yohann.mp4 -i anne-sophie.mp4 -i brady_erikka.mov 
-i cedric.mov -i pierre.mov  -i T3.mov  -i fancy.mov 
-i romain.mov	-i jb.mov 
-filter_complex '<we'll get here in a second>' 
done.mp4

For the filter complex, we'll generate a 4K video. 4K is 3840x2160, and is super convenient as it allows us to place 9 1280x720 videos into the "Brady grid":

the Brady Grid of 9 720p videos

So, each of the videos needs to be 1280x720. And here is where we came across our first complication. Half of the videos were recorded in landscape mode:

Cedric

..but half were recorded in portrait mode....

The landsacpe videos are all the right 16:9 proportion - but are 1080p - so will have to be rescaled to 1280x720.

The portrait videos are 1080x1920, so we'll have to do a crop,and then slightly upscale them. But we're using FFMPEG, so this will be a snap!

anne-sophie-brady-bunch

Building the palette

We want our video to be 4k, so we first input a video and resize it to 4K. It doesn't matter which video, since we will draw on top of it. I'm using the center video - the 5th video (index 4) as the background. It is resized to 4K, and named main.

[4]scale=3840:2160[main]

Cropping inside the filter_complex

For each video, we can build something like this:

[0:v]crop=in_w:in_w*9/16:0:in_h*0.2,scale=1280:720[0];

Let's walk through what this command does:

[0:v] - grab the video from input 0 (thats sorin.mp4).

crop=in_w:in_w*9/16:0:in_h*0.2 - the crop command, which has 4 parameters video width: video height:x start: y start

  • video width: in_w - take the width of the input video
  • video height: in_w*9/16 - take the width, and multiply by 9/16 to keep the video in the 9:16 format. (this will do nothing to the landscape videos, but will crop the portrait ones).
  • x_start: 0. Grab from the far left of the video
  • y_start: in_h*0.2 Get the height of the video, and begin the crop 20% down. The percentage can be modified slightly on each video - as some of our colleagues are taller than others.

Now we've cropped each video. The landscape videos will be mostly unchanged, but the portrait videos will be cropped into a landscape view around the face.

scale=1280:720[0] Resize the 16:9 video to 720p.

[0]. Rename this modified video 0. This is a variable that we can call later for placement.

We have to do this for each video, so our -filter_complex begins growing:

[4]scale=3840:2160[main];
[0:v]crop=in_w:in_w*9/16:0:in_h*0.2,scale=1280:720[0];
[1:v]crop=in_w:in_w*9/16:0:in_h*0.1,scale=1280:720[1];
[2:v]crop=in_w:in_w*9/16:0:in_h*0.2,scale=1280:720[2];
[3:v]crop=in_w:in_w*9/16:0:in_h*0.2,scale=1280:720[3];
[4:v]crop=in_w:in_w*9/16:0:in_h*0.1,scale=1280:720[4];
[5:v]crop=in_w:in_w*9/16:0:in_h*0.2,scale=1280:720[5];
[6:v]crop=in_w:in_w*9/16:0:in_h*0.2,scale=1280:720[6];
[7:v]crop=in_w:in_w*9/16:0:in_h*0.1,scale=1280:720[7];
[8:v]crop=in_w:in_w*9/16:0:in_h*0.1,scale=1280:720[8];
[9:v]crop=in_w:in_w*9/16:0:in_h*0.1,scale=1280:720[9];
[10:v]crop=in_w:in_w*9/16:0:in_h*0.1,scale=1280:720[10];
[11:v]crop=in_w:in_w*9/16:0:in_h*0.1,scale=1280:720[11];
[12:v]crop=in_w:in_w*9/16:0:in_h*0.1,scale=1280:720[12];
[13:v]crop=in_w:in_w*9/16:0:in_h*0.1,scale=1280:720[13];
[14:v]crop=in_w:in_w*9/16:0:in_h*0.2,scale=1280:720[14];
[15:v]crop=in_w:in_w*9/16:0:in_h*0.2,scale=1280:720[15]

It took a few tests to lay out each person the way I wanted - mostly changing the in_h multiplier to properly center the face.

Layout of the videos

The next bit of the filter_complex lays out each of the videos into the grid.

[main][0]overlay=0:0[main0]

This tells FFMPEG to take the "main" 4K video, and overlay video "0" at (0,0). Rename this intermediate video "main0". We now repeat this for each video, updating the variables and the (x,y) coordinates for each video.

Only one video will remain at the center (index 4) - the video of our CEO Cedric.

[main][0]overlay=0:0[main0];
[main0][1]overlay=1280:0[main1];
[main1][2]overlay=2560:0[main2];
[main2][3]overlay=0:720[main3];
[main3][4]overlay=1280:720[main4];
[main4][5]overlay=2560:720[main5];
[main5][6]overlay=0:1440[main6];
[main6][7]overlay=1280:1440[main7];
[main7][8]overlay=2560:1440[main8];
[main8][9]overlay=0:0[main9];
[main9][10]overlay=1280:0[main10];
[main10][11]overlay=2560:0[main11];
[main11][12]overlay=0:720[main12];
[main12][13]overlay=2560:720[main13];
[main13][14]overlay=0:1440[main14];
[main14][15]overlay=1280:1440[main15];
[main15][16]overlay=2560:1440

Now, those of you with keen eyes will notice that video 9 will sit on top of video 0 (10 on top of 1, etc.). I have more videos to add to the grid of 9 than places for them. We'll fix that in our next step.

Video timings

Each video has a slightly different length. When a video reaches an end, the video freezes, while the rest continue playing. I'd like all the videos to move for the entire clip. Looking at all of them, the shortest video is 9 seconds long. (Thanks to Pierre for defining our video's length 🤣).

We want each video to be 9 seconds long. We can fix this by changing the input parameter for each video:

-t 9 -i doug.mp4

-t 9 says, "grab only the first 9 seconds of doug.mp4." Adding this to each video - now the overall video is exactly 9 seconds long!

More video timing - desynchronizing the movements

Having watched these videos a lot, everyone moved their heads in the same pattern of motions, at approximately the same rate. When they are all combined into one video, the results were eerily robotic. Usually, you want all your videos in sync, but in this case, we want to actually artificially de-synchronize our videos a bit.

To do this, we can grab 9 seconds of each video - but from a different start point for each video:

 -ss 5 -t 9 -i olia.mp4

-ss 4 start offset of 4 seconds -t 9 9 seconds of video

This command tells FFMpeg: "input 9 seconds of Olia.mp4, starting at 5 seconds into the video,". As long as we are careful and the SS + t values are not longer than the video's length, we'll be fine (I created a spreadsheet). So we input different -ss values for each video.

Timing part 3: More than 9 videos

As noted, we have more than 9 videos. We could have just stopped at 9, but I wanted to include everyone. The easiest way to do that is to have two rounds of people in the 9 grid. We'll put Cedric in the middle for 18 seconds (his video was over 20s, so we are ok), and array 2 sets of 8 people around him.

 -ss 5 -t 9 -i erikka.mp4
 
 -itsoffset 9 -t 9 -i romain.mov

Erikka's video will be 9 seconds long (starting at time 5 from her video), and will be inserted into the final video at t=0. Romain's video will be 9 seconds long as well, but the -itsoffset 9 parameter says "Begin adding this video 9 seconds into the video."

So the result is Erikka is on the video for time 0-9s, and at t=9, Romain's video replaces her for t=9-18.

At this point, I'm super happy with the video. but it is just missing something... and that is (of course) a text overlay.

The api.video Bunch

There is a free Brady Bunch font. I downloaded it, and placed it in the same directory as the videos. When you add text, you specify the location, the size and the font. I'll have 3 lines of text. The comma seperate each line, but for readability, I'll add a few lines between each one:

drawtext=enable:fontfile=BradBunR.ttf:fontsize=400:borderw=10:bordercolor=white:text='The':x=(3840)/2-200:y=(2160)/2-600,

drawtext=enable:fontfile=BradBunR.ttf:fontsize=400:borderw=10:bordercolor=white:text='api.video':x=(3840)/2-400:y=(2160)/2-400,

drawtext=enable:fontfile=BradBunR.ttf:fontsize=400:borderw=10:bordercolor=white:text='Bunch':x=(3840)/2-200:y=(2160)/2+200
  • drawtext=enable Turn on the drawing (I guess)
  • fontfile=BradBunR.ttf Load in the Brady Bunch font!
  • fontsize=400 It is a 4K video, so we can go pretty large.
  • borderw=10:bordercolor=white The default font color is black, but we want a nice retro white border around it.
  • text='api.video' The text to output.
  • x=(3840)/2-400:y=(2160)/2-400 Finally the (x,y) coordinates to start the text. This took some trial and error to get the locations in the right spot, but I'm pretty happy with the result:

The full FFMPEG command

We've walked through all the steps used to create the command, but we cannot avoid the inevitable: we have to show the full FFMPEG command. It follows the basic premise above:

  1. Input all the videos (with time = 9, start offsets and playback offsets)
  2. filter all the videos
  3. resize and or crop all the videos
  4. overlay them all on a 4K background
  5. add the text
  6. output the video
ffmpeg  -ss 10 -t 9 -i  sorin.mp4  -ss 5 -t 9 -i olia.mp4	-ss 4 -t 9 -i maria.mp4  -ss 5 -t 9 -i josephine.mp4 -ss 2 -t 18 -i cedric.mov -ss 1 -t 9 -i yohann.mp4 -ss 6 -t 9 -i anne-sophie.mp4 -ss 2 -t 9 -i brady_erikka.mov -ss 0 -t 9  -i pierre.mov  -itsoffset 9 -ss 3 -t 9 -i   T3.mov -itsoffset 9 -ss 6 -t 9  -i fancy.mov -ss 0 -itsoffset 9 -t 9 -i romain.mov	-itsoffset 9 -ss 11 -t 9 -i jb.mov -itsoffset 9 -ss 0 -t 9 -i doug.mp4 -itsoffset 9 -ss 0 -t 9 -i stephanie.mov -ss 10 -itsoffset 9 -t 9 -i  sorin.mp4  -ss 5 -itsoffset 9 -t 9 -i olia.mp4 -filter_complex '[4]scale=3840:2160[main];[0:v]crop=in_w:in_w*9/16:0:in_h*0.2,scale=1280:720[0];[1:v]crop=in_w:in_w*9/16:0:in_h*0.1,scale=1280:720[1];[2:v]crop=in_w:in_w*9/16:0:in_h*0.2,scale=1280:720[2];[3:v]crop=in_w:in_w*9/16:0:in_h*0.2,scale=1280:720[3];[4:v]crop=in_w:in_w*9/16:0:in_h*0.1,scale=1280:720[4];[5:v]crop=in_w:in_w*9/16:0:in_h*0.2,scale=1280:720[5];[6:v]crop=in_w:in_w*9/16:0:in_h*0.2,scale=1280:720[6];[7:v]crop=in_w:in_w*9/16:0:in_h*0.1,scale=1280:720[7];[8:v]crop=in_w:in_w*9/16:0:in_h*0.1,scale=1280:720[8];[9:v]crop=in_w:in_w*9/16:0:in_h*0.1,scale=1280:720[9];[10:v]crop=in_w:in_w*9/16:0:in_h*0.1,scale=1280:720[10];[11:v]crop=in_w:in_w*9/16:0:in_h*0.1,scale=1280:720[11];[12:v]crop=in_w:in_w*9/16:0:in_h*0.1,scale=1280:720[12];[13:v]crop=in_w:in_w*9/16:0:in_h*0.1,scale=1280:720[13];[14:v]crop=in_w:in_w*9/16:0:in_h*0.2,scale=1280:720[14];[15:v]crop=in_w:in_w*9/16:0:in_h*0.2,scale=1280:720[15];[main][0]overlay=0:0[main0];[main0][1]overlay=1280:0[main1];[main1][2]overlay=2560:0[main2];[main2][3]overlay=0:720[main3];[main3][4]overlay=1280:720[main4];[main4][5]overlay=2560:720[main5];[main5][6]overlay=0:1440[main6];[main6][7]overlay=1280:1440[main7];[main7][8]overlay=2560:1440[main8];[main8][9]overlay=0:0[main9];[main9][10]overlay=1280:0[main10];[main10][11]overlay=2560:0[main11];[main11][12]overlay=0:720[main12];[main12][13]overlay=2560:720[main13];[main13][14]overlay=0:1440[main14];[main14][15]overlay=1280:1440[main15];[main15][16]overlay=2560:1440[finalnotext];[finalnotext]drawtext=enable:fontfile=BradBunR.ttf:fontsize=400:borderw=10:bordercolor=white:text='The':x=(3840)/2-200:y=(2160)/2-600,drawtext=enable:fontfile=BradBunR.ttf:fontsize=400:borderw=10:bordercolor=white:text='api.video':x=(3840)/2-400:y=(2160)/2-400,drawtext=enable:fontfile=BradBunR.ttf:fontsize=400:borderw=10:bordercolor=white:text='Bunch':x=(3840)/2-200:y=(2160)/2+200'   apibunch.mp4

Yeah, it's a long one. But the results speak for themselves - a fun video showing off the api.video team!

Conclusion

Install FFMPEG! Get 9 friends to film a video - and create your own "The Bunch." We hope you'll upload it to api.video and share the results with us!

Doug

Head of Developer Relations

Connect your users with videos

Create a free account today