AVAudioFifo

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. 实际应用场景总结

  1. 音频转码: 平衡解码和编码速度差异
  2. 实时处理: 缓冲输入数据,平滑处理波动
  3. 格式转换: 作为不同音频格式间的中间缓冲区
  4. 流处理: 处理网络流中的音频数据包
相关推荐
Croa-vo2 小时前
TikTok 数据工程师三轮 VO 超详细面经:技术深挖 + 建模推导 + 压力测试全记录
javascript·数据结构·经验分享·算法·面试
蘑菇小白2 小时前
时间复杂度
数据结构·算法
Cx330❀2 小时前
C++ STL set 完全指南:从基础用法到实战技巧
开发语言·数据结构·c++·算法·leetcode·面试
八月的雨季 最後的冰吻3 小时前
FFmepg--27-两路音频混合
ffmpeg·音视频
阿昭L4 小时前
堆结构与堆排序
数据结构·算法
.YM.Z13 小时前
【数据结构】:排序(一)
数据结构·算法·排序算法
安步当歌13 小时前
【FFmpeg】ffmpeg中zig-zag的扫描方式
ffmpeg
sin_hielo16 小时前
leetcode 2435
数据结构·算法·leetcode
crescent_悦17 小时前
PTA L1-020 帅到没朋友 C++
数据结构·c++·算法