Audio Resampling with FFmpeg libswresample

This guide demonstrates how to programmatically resample, convert channel layouts, and change sample formats of audio data using FFmpeg's libswresample library in C or C++. You will learn how to allocate and configure an SwrContext, calculate the necessary output buffer sizes, execute the conversion loop, flush remaining buffered samples, and safely release allocated resources.

1. Prerequisites and Key Concepts

libswresample performs three core operations on PCM audio data:

  • Sample rate conversion: Adjusting the frequency (e.g., 44.1 kHz to 48 kHz).
  • Format conversion: Converting bit depths and packing styles (e.g., 16-bit signed integer interleaved to 32-bit float planar).
  • Channel rematrixing: Changing channel counts and layouts (e.g., 5.1 surround sound to stereo).

Modern versions of FFmpeg (version 5.1 and newer) use the AVChannelLayout structure instead of legacy uint64 channel bitmasks, and use swr_alloc_set_opts2() for initialization.

2. Step-by-Step Workflow

Step 1: Initialize the SwrContext

Allocate and configure the resampler context by defining your input and output audio properties:

#include <libswresample/swresample.h>
#include <libavutil/opt.h>
#include <libavutil/channel_layout.h>

struct SwrContext *swr_ctx = NULL;

// Define input parameters (e.g., Stereo, 44100Hz, S16 interleaved)
AVChannelLayout in_ch_layout = AV_CHANNEL_LAYOUT_STEREO;
enum AVSampleFormat in_sample_fmt = AV_SAMPLE_FMT_S16;
int in_sample_rate = 44100;

// Define output parameters (e.g., 5.1 Surround, 48000Hz, Float planar)
AVChannelLayout out_ch_layout = AV_CHANNEL_LAYOUT_5POINT1;
enum AVSampleFormat out_sample_fmt = AV_SAMPLE_FMT_FLTP;
int out_sample_rate = 48000;

// Allocate and set options
int ret = swr_alloc_set_opts2(&swr_ctx,
                              &out_ch_layout, out_sample_fmt, out_sample_rate,
                              &in_ch_layout, in_sample_fmt, in_sample_rate,
                              0, NULL);
if (ret < 0 || !swr_ctx) {
    // Handle error
}

// Initialize the resampling context
if (swr_init(swr_ctx) < 0) {
    swr_free(&swr_ctx);
    // Handle error
}

Step 2: Allocate Output Buffers

Because resampling changes sample rates, input and output sample counts will differ. You must determine the maximum number of output samples that can be generated using swr_get_out_samples() to prevent buffer overflows:

int in_nb_samples = 1024; // Number of input samples per channel
uint8_t **out_data = NULL;
int out_linesize;

// Compute upper-bound output sample count
int max_out_samples = swr_get_out_samples(swr_ctx, in_nb_samples);

// Allocate output audio buffers
ret = av_samples_alloc_array_and_samples(&out_data, &out_linesize,
                                         out_ch_layout.nb_channels,
                                         max_out_samples,
                                         out_sample_fmt, 0);
if (ret < 0) {
    // Handle allocation error
}

Step 3: Perform the Conversion

Pass your input buffer to swr_convert(). For planar formats, each pointer in the array points to a distinct channel plane. For interleaved formats, all data is in data[0].

// Perform resampling
int converted_samples = swr_convert(swr_ctx,
                                    out_data, max_out_samples,
                                    (const uint8_t **)in_data, in_nb_samples);

if (converted_samples < 0) {
    // Handle conversion error
}

// Write or process converted_samples from out_data...

Step 4: Flush Cached Samples

Resampling algorithms use internal filters that introduce algorithmic delay, keeping some samples inside the resampler. When you reach the end of the input audio stream, flush the remaining samples by passing NULL for the input pointers and 0 for input sample count:

int flushed_samples;
do {
    flushed_samples = swr_convert(swr_ctx, out_data, max_out_samples, NULL, 0);
    if (flushed_samples > 0) {
        // Write or process flushed_samples from out_data...
    }
} while (flushed_samples > 0);

Step 5: Clean Up

Always free your allocated buffers and context once processing is complete:

if (out_data) {
    av_freep(&out_data[0]);
    av_freep(&out_data);
}
swr_free(&swr_ctx);

Summary Checklist

  1. Call swr_alloc_set_opts2() with target and source configurations.
  2. Initialize with swr_init().
  3. Query needed buffer size with swr_get_out_samples().
  4. Run swr_convert() on incoming audio chunks.
  5. Pass NULL to swr_convert() at EOF to drain buffered data.
  6. Deallocate memory with av_freep() and swr_free().