Progressive Decoding Mechanisms in libavif

This article explains how the libavif library handles the progressive decoding of AVIF images. It covers the core mechanisms of the library, including its integration with AV1 spatial scalability, the avifProgressiveState API flags, and how developers can utilize the avifDecoder structure to incrementally render images from lower to higher quality.

AV1 Spatial Scalability and Multi-Layer Bitstreams

The primary mechanism enabling progressive decoding in AVIF is AV1’s native support for spatial scalability and multi-layer bitstreams. An AVIF image can be encoded with multiple layers within a single item, where the base layer contains a low-resolution or low-quality version of the image, and subsequent enhancement layers contain the data necessary to reconstruct higher resolutions or finer details.

libavif detects these layers during the container parsing phase. Instead of treating the file as a single static frame, the library identifies the presence of these enhancement layers and prepares the underlying AV1 decoder (such as dav1d or aom) to decode them sequentially.

The avifProgressiveState API

To manage progressive rendering, libavif exposes the avifProgressiveState enumeration through its decoder API. This state informs the application of the progressive capabilities of the target file:

By querying decoder->progressiveState, an application can dynamically adjust its rendering pipeline based on whether the incoming image support progressive refinement.

Implementing Progressive Decoding via avifDecoder

To decode an image progressively using libavif, developers interact with the avifDecoder structure and its frame-stepping functions. The workflow operates as follows:

  1. Enable Progressive Decoding: The developer sets the decoder to allow progressive decoding before parsing the file.
  2. Determine Layer Count: Once parsed, if decoder->progressiveState is active, decoder->imageCount represents the total number of progressive layers available rather than individual animation frames.
  3. Sequential Decoding Loop: The application calls avifDecoderNextImage() in a loop. Each call decodes the next available layer of the image.
  4. Incremental Rendering: After each successful call to avifDecoderNextImage(), the buffer pointed to by decoder->image contains the image at the current layer’s quality. The application can immediately draw this buffer to the screen, providing a visual transition from a blurry or blocky preview to the final high-resolution image.

Performance and Overhead

While progressive decoding improves the perceived loading time for users on slow network connections, it does introduce computational overhead. libavif must invoke the underlying AV1 decoder for each layer, which requires additional CPU cycles compared to decoding only the final, highest-quality layer directly. To optimize this, libavif relies on efficient memory management within the codec wrappers to reuse buffers between layer passes whenever possible.