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.mp4

How the Coordinates Work

  • 0_0: Places input0.mp4 at the top-left corner (x=0, y=0).
  • w0_0: Places input1.mp4 directly to the right of the first video (x=width of input0, y=0).
  • w0_h1: Places input2.mp4 directly below input1.mp4 at (x=width of input0, y=height of input1).
  • fill=black: Fills any unused canvas area with black. This is necessary if the total height of input1 + input2 does not match the height of input0.

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.mp4

How It Works

  1. Each video stream is scaled to a standard 960x540 resolution.
  2. The xstack layout 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.mp4

This ensures both inputs occupy an exact 960x1080 space side by side without distortion, even if their source resolutions and aspect ratios differ significantly.