1. AVAudioFifo 概述
AVAudioFifo 是 FFmpeg 中专门为音频处理设计的先进先出(FIFO)缓冲区。它主要用于解决音频处理中常见的生产者-消费者速度不匹配问题。
主要应用场景:
- 解码和编码速度不一致时的缓冲
- 重采样前后的数据暂存
- 音频格式转换的中间缓冲
- 实时音频处理的流量控制
2. 核心数据结构
c
typedef struct AVAudioFifo {
AVFifoBuffer **buf; // FIFO 缓冲区数组(每个声道一个)
int nb_buffers; // 缓冲区数量(声道数)
int nb_samples; // 当前样本总数
int allocated_samples; // 已分配样本容量
int channels; // 声道数
enum AVSampleFormat sample_fmt; // 采样格式
int sample_size; // 单个样本字节大小
} AVAudioFifo;
3. API 函数详解
3.1 创建和销毁
cpp
// 创建 AVAudioFifo
AVAudioFifo* av_audio_fifo_alloc(enum AVSampleFormat sample_fmt,
int channels,
int nb_samples);
// 重新分配 FIFO 大小
int av_audio_fifo_realloc(AVAudioFifo* af, int nb_samples);
// 销毁 AVAudioFifo
void av_audio_fifo_free(AVAudioFifo* af);
参数说明:
sample_fmt: 采样格式(如AV_SAMPLE_FMT_FLTP)channels: 声道数nb_samples: 初始容量(样本数)
3.2 数据读写操作
cpp
// 写入数据到 FIFO
int av_audio_fifo_write(AVAudioFifo* af,
void** data,
int nb_samples);
// 从 FIFO 读取数据
int av_audio_fifo_read(AVAudioFifo* af,
void** data,
int nb_samples);
// 从 FIFO 读取数据(不移动读指针)
int av_audio_fifo_peek(AVAudioFifo* af,
void** data,
int nb_samples);
// 从指定位置读取数据
int av_audio_fifo_peek_at(AVAudioFifo* af,
void** data,
int nb_samples,
int offset);
3.3 状态查询
cpp
// 获取当前样本数量
int av_audio_fifo_size(AVAudioFifo* af);
// 获取剩余空间(可写入的样本数)
int av_audio_fifo_space(AVAudioFifo* af);
// 重置 FIFO(清空数据)
void av_audio_fifo_reset(AVAudioFifo* af);
4. 详细使用示例
4.1 基础使用模式
cpp
#include <libavutil/audio_fifo.h>
// 创建 FIFO:立体声,浮点平面格式,初始容量 1024 个样本
AVAudioFifo* fifo = av_audio_fifo_alloc(AV_SAMPLE_FMT_FLTP, 2, 1024);
if (!fifo) {
// 错误处理
}
// 准备写入数据
float* audio_data[2]; // 左右声道数据
int samples_to_write = 512;
// 写入数据
int written = av_audio_fifo_write(fifo, (void**)audio_data, samples_to_write);
if (written < samples_to_write) {
// FIFO 空间不足
}
// 读取数据
float* output_data[2];
int samples_to_read = 256;
int read = av_audio_fifo_read(fifo, (void**)output_data, samples_to_read);
// 清理
av_audio_fifo_free(fifo);
4.2 在音频转换中的完整应用
cpp
class AudioProcessor {
private:
AVAudioFifo* fifo;
AVCodecContext* decoder_ctx;
AVCodecContext* encoder_ctx;
SwrContext* resampler;
public:
bool init() {
// 创建 FIFO,容量为编码器帧大小的 4 倍
int fifo_size = encoder_ctx->frame_size * 4;
fifo = av_audio_fifo_alloc(encoder_ctx->sample_fmt,
encoder_ctx->ch_layout.nb_channels,
fifo_size);
return fifo != nullptr;
}
bool processFrame(AVFrame* decoded_frame) {
// 重采样解码后的帧
AVFrame* resampled_frame = resampleAudio(decoded_frame);
if (!resampled_frame) return false;
// 写入 FIFO
int written = av_audio_fifo_write(fifo,
(void**)resampled_frame->data,
resampled_frame->nb_samples);
av_frame_free(&resampled_frame);
if (written < resampled_frame->nb_samples) {
std::cerr << "FIFO 写入不完整" << std::endl;
return false;
}
// 如果 FIFO 中有足够数据,进行编码
return encodeFromFifo();
}
bool encodeFromFifo() {
int frame_size = encoder_ctx->frame_size;
while (av_audio_fifo_size(fifo) >= frame_size) {
AVFrame* frame = av_frame_alloc();
frame->format = encoder_ctx->sample_fmt;
frame->ch_layout = encoder_ctx->ch_layout;
frame->sample_rate = encoder_ctx->sample_rate;
frame->nb_samples = frame_size;
if (av_frame_get_buffer(frame, 0) < 0) {
av_frame_free(&frame);
return false;
}
// 从 FIFO 读取数据
int read = av_audio_fifo_read(fifo, (void**)frame->data, frame_size);
if (read < frame_size) {
av_frame_free(&frame);
return false;
}
// 编码帧
if (!encodeFrame(frame)) {
av_frame_free(&frame);
return false;
}
av_frame_free(&frame);
}
return true;
}
void flush() {
// 处理 FIFO 中剩余的数据
int remaining = av_audio_fifo_size(fifo);
if (remaining > 0) {
// 创建最后一帧
AVFrame* last_frame = av_frame_alloc();
last_frame->format = encoder_ctx->sample_fmt;
last_frame->ch_layout = encoder_ctx->ch_layout;
last_frame->sample_rate = encoder_ctx->sample_rate;
last_frame->nb_samples = remaining;
if (av_frame_get_buffer(last_frame, 0) >= 0) {
av_audio_fifo_read(fifo, (void**)last_frame->data, remaining);
encodeFrame(last_frame);
}
av_frame_free(&last_frame);
}
}
};
5. 内存布局详解
5.1 不同采样格式的内存结构
平面格式 (Planar) - AV_SAMPLE_FMT_FLTP:
声道0: [sample0, sample1, sample2, ...] // 连续的浮点数
声道1: [sample0, sample1, sample2, ...] // 连续的浮点数
打包格式 (Packed) - AV_SAMPLE_FMT_FLT:
[sample0_ch0, sample0_ch1, sample1_ch0, sample1_ch1, ...]
5.2 FIFO 内部结构
对于立体声 FLTP 格式:
AVAudioFifo
├── buf[0] → [左声道样本0, 样本1, 样本2, ...]
├── buf[1] → [右声道样本0, 样本1, 样本2, ...]
├── nb_samples = 当前总样本数
└── allocated_samples = 分配的总容量
6. 高级用法和最佳实践
6.1 动态调整 FIFO 大小
cpp
// 根据需求动态调整 FIFO 大小
int ensureFifoCapacity(AVAudioFifo* fifo, int required_samples) {
int current_space = av_audio_fifo_space(fifo);
if (current_space < required_samples) {
int current_size = av_audio_fifo_size(fifo);
int new_size = current_size + required_samples + 1024; // 额外预留
if (av_audio_fifo_realloc(fifo, new_size) < 0) {
std::cerr << "无法重新分配 FIFO" << std::endl;
return -1;
}
}
return 0;
}
6.2 处理不完整帧
cpp
bool processPartialFrames(AVAudioFifo* fifo, AVCodecContext* enc_ctx) {
int frame_size = enc_ctx->frame_size;
int available = av_audio_fifo_size(fifo);
// 处理完整帧
while (available >= frame_size) {
if (!encodeCompleteFrame(fifo, enc_ctx, frame_size)) {
return false;
}
available = av_audio_fifo_size(fifo);
}
// 处理剩余的不完整帧(如果需要)
if (available > 0 && available < frame_size) {
// 可以选择填充静音或等待更多数据
return handlePartialFrame(fifo, enc_ctx, available);
}
return true;
}
6.3 多线程安全考虑
cpp
#include <mutex>
class ThreadSafeAudioFifo {
private:
AVAudioFifo* fifo;
std::mutex mutex;
public:
ThreadSafeAudioFifo(enum AVSampleFormat fmt, int channels, int size) {
fifo = av_audio_fifo_alloc(fmt, channels, size);
}
~ThreadSafeAudioFifo() {
std::lock_guard<std::mutex> lock(mutex);
av_audio_fifo_free(fifo);
}
int write(void** data, int nb_samples) {
std::lock_guard<std::mutex> lock(mutex);
return av_audio_fifo_write(fifo, data, nb_samples);
}
int read(void** data, int nb_samples) {
std::lock_guard<std::mutex> lock(mutex);
return av_audio_fifo_read(fifo, data, nb_samples);
}
int size() {
std::lock_guard<std::mutex> lock(mutex);
return av_audio_fifo_size(fifo);
}
};
7. 常见问题与调试
7.1 性能问题诊断
cpp
void debugFifoState(AVAudioFifo* fifo, const char* tag) {
int size = av_audio_fifo_size(fifo);
int space = av_audio_fifo_space(fifo);
std::cout << "[" << tag << "] "
<< "Size: " << size << " samples, "
<< "Space: " << space << " samples, "
<< "Usage: " << (size * 100 / (size + space)) << "%"
<< std::endl;
}
// 在关键位置调用
debugFifoState(fifo, "Before encoding");
7.2 内存泄漏检查
cpp
class ManagedAudioFifo {
private:
AVAudioFifo* fifo;
public:
ManagedAudioFifo(enum AVSampleFormat fmt, int channels, int size)
: fifo(av_audio_fifo_alloc(fmt, channels, size)) {}
// 禁止拷贝
ManagedAudioFifo(const ManagedAudioFifo&) = delete;
ManagedAudioFifo& operator=(const ManagedAudioFifo&) = delete;
// 允许移动
ManagedAudioFifo(ManagedAudioFifo&& other) noexcept : fifo(other.fifo) {
other.fifo = nullptr;
}
~ManagedAudioFifo() {
if (fifo) {
av_audio_fifo_free(fifo);
}
}
AVAudioFifo* get() const { return fifo; }
};
8. 实际应用场景总结
- 音频转码: 平衡解码和编码速度差异
- 实时处理: 缓冲输入数据,平滑处理波动
- 格式转换: 作为不同音频格式间的中间缓冲区
- 流处理: 处理网络流中的音频数据包