How libvpx Handles Aspect Ratio and Pixel Geometry

This article explains how the libvpx library—the reference software encoder for the VP8 and VP9 video formats—manages aspect ratio and pixel geometry metadata. It explores the differences in how VP8 and VP9 handle Sample Aspect Ratio (SAR) and Display Aspect Ratio (DAR) within their respective bitstreams, how developers can configure these settings using the libvpx API, and the role of container formats in ensuring correct video playback.

The Difference Between VP8 and VP9 Bitstreams

The libvpx library handles pixel geometry differently depending on whether you are encoding to the older VP8 codec or the newer VP9 codec.

VP8: Container-Dependent Metadata

The VP8 bitstream format does not natively support pixel geometry or Sample Aspect Ratio (SAR) metadata within its raw frame headers. VP8 assumes a default pixel aspect ratio of 1:1 (square pixels). If a video encoded with VP8 requires a non-square aspect ratio (for example, anamorphic widescreen), this metadata cannot be stored in the raw VP8 video stream. Instead, libvpx relies entirely on the multiplexer (muxer) to write this geometry data into the container format, such as WebM or Matroska (MKV).

VP9: Native Bitstream Signaling

VP9 introduced native support for signaling pixel geometry directly within the frame header. The VP9 bitstream includes fields for “Render Width” and “Render Height” alongside the coded “Frame Width” and “Frame Height.”

If the render dimensions differ from the coded dimensions, the decoder calculates the Sample Aspect Ratio (SAR) to stretch or shrink the pixels accordingly during playback. This allows VP9 to support non-square pixels natively without relying solely on container-level metadata.

Using the libvpx API to Set Aspect Ratio

When programmatically encoding video using the libvpx API, developers must pass the correct spatial dimensions to the encoder.

  1. Setting Coded Dimensions: The actual encoded pixel resolution is defined in the configuration structure (vpx_codec_enc_cfg_t) using the g_w (width) and g_h (height) parameters.
  2. Setting Render Dimensions (VP9 Only): To define a custom pixel aspect ratio in VP9, you must use the encoder control VP9E_SET_RENDER_SIZE. This control takes a pointer to a two-integer array containing the desired display width and height.
int render_size[2] = { display_width, display_height };
vpx_codec_control(&codec_context, VP9E_SET_RENDER_SIZE, render_size);

If this control is not set, libvpx defaults the render size to match the coded size, resulting in a 1:1 pixel aspect ratio.

The Role of Container Formats (WebM and MKV)

Because raw video bitstreams are rarely played back directly, the container format plays a critical role in handling pixel geometry. When encoding to WebM (the standard container for VP8 and VP9), libvpx works in tandem with the muxer (such as libwebm or FFmpeg’s muxer).

WebM and Matroska containers use specific Track elements to define display geometry: * PixelWidth / PixelHeight: The physical, coded dimensions of the video frames. * DisplayWidth / DisplayHeight: The dimensions at which the video should be rendered on screen.

When encoding VP8, the container’s DisplayWidth and DisplayHeight are the only way to signal a non-square aspect ratio to media players. For VP9, while the metadata exists in the bitstream, muxers typically copy the VP9 render size values into the container’s display headers to ensure maximum compatibility with hardware decoders and media players.