FFmpeg xstack: Stack Videos of Different Sizes
The xstack filter in FFmpeg allows you to arrange
multiple video streams into custom 2D grids, mosaics, or side-by-side
layouts using coordinate-based placement. While simpler filters like
hstack and vstack require inputs to share
identical heights or widths, xstack supports inputs with
varying resolutions. This guide explains how to properly define
coordinate offsets, scale inputs for aligned grids, and handle uneven
dimensions without visual glitches.
Understanding the
layout Parameter
The xstack filter requires two primary parameters:
inputs (the number of video streams) and
layout (the coordinates where each video’s top-left corner
is positioned).
Coordinates are written as x_y pairs separated by a pipe
(|). You can use absolute pixel values or reference
preceding inputs using predefined variables:
w0,h0: Width and height of the first input.w1,h1: Width and height of the second input, and so forth.
Method 1: Stacking Different Sizes Using Coordinate Offsets
If you want to keep the original resolutions of each video, calculate the placement using coordinate references.
For example, to stack three videos of different sizes in an asymmetrical 2-column layout (one large video on the left, two smaller stacked videos on the right):
ffmpeg -i input0.mp4 -i input1.mp4 -i input2.mp4 -filter_complex \
"[0:v][1:v][2:v]xstack=inputs=3:layout=0_0|w0_0|w0_h1:fill=black[v]" \
-map "[v]" -c:v libx264 output.mp4How the Coordinates Work
0_0: Placesinput0.mp4at the top-left corner(x=0, y=0).w0_0: Placesinput1.mp4directly to the right of the first video(x=width of input0, y=0).w0_h1: Placesinput2.mp4directly belowinput1.mp4at(x=width of input0, y=height of input1).fill=black: Fills any unused canvas area with black. This is necessary if the total height ofinput1 + input2does not match the height ofinput0.
Method 2: Pre-scaling Inputs for a Balanced Grid
When creating a standard 2x2 grid from sources with mismatched
resolutions, placing them directly can cause misaligned edges or empty
gaps. The standard solution is to scale each video to uniform cell
dimensions within filter_complex before passing them to
xstack.
ffmpeg -i input0.mp4 -i input1.mp4 -i input2.mp4 -i input3.mp4 -filter_complex \
"[0:v]scale=960:540[v0]; \
[1:v]scale=960:540[v1]; \
[2:v]scale=960:540[v2]; \
[3:v]scale=960:540[v3]; \
[v0][v1][v2][v3]xstack=inputs=4:layout=0_0|w0_0|0_h0|w0_h0[v]" \
-map "[v]" -c:v libx264 output.mp4How It Works
- Each video stream is scaled to a standard 960x540 resolution.
- The
xstacklayout places them into a 1920x1080 canvas:0_0: Top-left.w0_0: Top-right.0_h0: Bottom-left.w0_h0: Bottom-right.
Method 3: Scaling While Preserving Aspect Ratio (Padding)
If your inputs have different aspect ratios (such as mixing 16:9 landscape and 9:16 vertical video) and you want to prevent stretching, scale the video to fit inside a target box and pad the remaining area before stacking.
ffmpeg -i input0.mp4 -i input1.mp4 -filter_complex \
"[0:v]scale=960:1080:force_original_aspect_ratio=decrease,pad=960:1080:(ow-iw)/2:(oh-ih)/2:black[v0]; \
[1:v]scale=960:1080:force_original_aspect_ratio=decrease,pad=960:1080:(ow-iw)/2:(oh-ih)/2:black[v1]; \
[v0][v1]xstack=inputs=2:layout=0_0|w0_0[v]" \
-map "[v]" -c:v libx264 output.mp4This ensures both inputs occupy an exact 960x1080 space side by side without distortion, even if their source resolutions and aspect ratios differ significantly.