Extract Frames from Animated AVIF with libavif

This article provides a quick guide on how to extract specific frames from an animated AVIF file using the official libavif library. You will learn how to achieve this both through the command line using the avifdec utility and programmatically using the libavif C API.

Method 1: Using the avifdec Command-Line Tool

The easiest way to extract a single frame from an animated AVIF is by using the avifdec command-line tool, which is compiled alongside libavif.

To extract a specific frame, use the --frame (or -f) option followed by the zero-based index of the frame you want to export.

avifdec --frame 5 input.avif output_frame_5.png

In this example, --frame 5 extracts the sixth frame of the animation (since indexing starts at 0) and saves it as a PNG file. You can output to other supported formats like PNG, JPEG, or Y4M depending on how your avifdec binary was compiled.

Method 2: Using the libavif C API

If you are developing an application, you can use the libavif C library to programmatically decode and extract specific frames. The key function for this process is avifDecoderNthImage().

Here is the step-by-step workflow to extract a specific frame:

  1. Initialize the Decoder: Create and initialize the decoder structure.
  2. Parse the File: Read the AVIF container to retrieve metadata and track information without decoding the entire pixel payload yet.
  3. Request the Specific Frame: Use avifDecoderNthImage() to jump directly to and decode your target frame index.

Below is a simplified C code example demonstrating this process:

#include "avif/avif.h"
#include <stdio.h>

int main() {
    const char * sourceFile = "input.avif";
    uint32_t targetFrameIndex = 3; // Extract the 4th frame

    avifDecoder * decoder = avifDecoderCreate();
    
    // Open and parse the file
    avifResult result = avifDecoderSetFile(decoder, sourceFile);
    if (result != AVIF_RESULT_OK) {
        fprintf(stderr, "Failed to open AVIF: %s\n", avifResultToString(result));
        avifDecoderDestroy(decoder);
        return 1;
    }

    result = avifDecoderParse(decoder);
    if (result != AVIF_RESULT_OK) {
        fprintf(stderr, "Failed to parse AVIF: %s\n", avifResultToString(result));
        avifDecoderDestroy(decoder);
        return 1;
    }

    // Verify the requested frame index exists
    if (targetFrameIndex >= (uint32_t)decoder->imageCount) {
        fprintf(stderr, "Frame index %u is out of bounds. Total frames: %d\n", targetFrameIndex, decoder->imageCount);
        avifDecoderDestroy(decoder);
        return 1;
    }

    // Decode the specific frame
    result = avifDecoderNthImage(decoder, targetFrameIndex);
    if (result != AVIF_RESULT_OK) {
        fprintf(stderr, "Failed to decode frame %u: %s\n", targetFrameIndex, avifResultToString(result));
        avifDecoderDestroy(decoder);
        return 1;
    }

    // The decoded frame is now available in decoder->image
    printf("Successfully decoded frame %u. Width: %u, Height: %u\n", 
           targetFrameIndex, decoder->image->width, decoder->image->height);

    // Clean up
    avifDecoderDestroy(decoder);
    return 0;
}

By utilizing avifDecoderNthImage(), the decoder efficiently seeks to the specified frame, handling any keyframe dependencies internally to construct the final image.