Convert Pixel Formats with libswscale in FFmpeg

This article provides a step-by-step technical guide on how to convert the pixel format and rescale a decoded video frame using the libswscale library in FFmpeg. You will learn how to initialize the scaling context, allocate target buffers, perform the conversion efficiently in C/C++, and properly free the allocated resources.

To convert the pixel format of a decoded AVFrame (for example, from AV_PIX_FMT_YUV420P to AV_PIX_FMT_RGB24), you must use FFmpeg’s libswscale library. The process involves four main steps: creating a scaling context, preparing the destination frame, performing the conversion, and freeing the resources.

1. Initialize the Scaling Context

First, allocate and configure a SwsContext using sws_getContext. This context stores the configuration for the source and destination dimensions, pixel formats, and the interpolation algorithm.

#include <libswscale/swscale.h>

struct SwsContext* sws_ctx = sws_getContext(
    src_width,          // Source frame width
    src_height,         // Source frame height
    src_pix_fmt,        // Source pixel format (e.g., AV_PIX_FMT_YUV420P)
    dst_width,          // Target frame width
    dst_height,         // Target frame height
    dst_pix_fmt,        // Target pixel format (e.g., AV_PIX_FMT_RGB24)
    SWS_BILINEAR,       // Rescaling algorithm (e.g., SWS_BICUBIC, SWS_BILINEAR)
    NULL, NULL, NULL    // Optional filters and parameters
);

if (!sws_ctx) {
    // Handle error: context creation failed
}

2. Allocate the Destination Frame

Create a destination AVFrame to hold the converted pixel data and allocate its internal buffers.

#include <libavutil/frame.h>

AVFrame* dst_frame = av_frame_alloc();
if (!dst_frame) {
    // Handle allocation error
}

dst_frame->format = dst_pix_fmt;
dst_frame->width  = dst_width;
dst_frame->height = dst_height;

// Allocate buffer memory for the target frame format
int ret = av_frame_get_buffer(dst_frame, 0);
if (ret < 0) {
    // Handle buffer allocation error
}

3. Perform the Conversion

Use the sws_scale function to perform the actual pixel format conversion. You must pass the pointers to the data planes and the linesizes for both the source and destination frames.

int output_height = sws_scale(
    sws_ctx,
    (const uint8_t *const *)src_frame->data, // Source data pointers
    src_frame->linesize,                     // Source line sizes
    0,                                       // Start slice Y coordinate
    src_frame->height,                       // Source slice height
    dst_frame->data,                         // Destination data pointers
    dst_frame->linesize                      // Destination line sizes
);

if (output_height < 0) {
    // Handle scaling error
}

4. Clean Up Resources

Once you are done with the conversion process, free the SwsContext and the destination frame to prevent memory leaks.

sws_freeContext(sws_ctx);
av_frame_free(&dst_frame);