avcodec_send_packet返回AVERROR(EAGAIN)并非错误,而是解码器内部缓冲区已满,需先调用avcodec_receive_frame取出已解码帧以腾出空间,再继续送包;必须严格遵循“送包→取帧→再送包”循环,且送完所有包后须调用avcodec_send_packet(nullptr)触发flush,否则末尾帧会丢失。

ffmpeg 解码音频流时,avcodec_send_packet 返回 AVERROR(EAGAIN) 怎么办
这通常不是错误,而是 FFmpeg 的新 API 流式解码机制在起作用:解码器内部缓冲区已满,需先调用 avcodec_receive_frame 取出已解码的 AVFrame,腾出空间再送包。硬塞会一直返回 EAGAIN。
实操要点:
- 必须严格遵循「送包 → 取帧 → 再送包」循环,不能只送不取
- 送完所有
AVPacket后,要额外调用一次avcodec_send_packet(nullptr)触发冲刷(flush),否则末尾几帧可能滞留在解码器中 -
avcodec_receive_frame返回0表示成功拿到一帧,返回AVERROR(EAGAIN)表示暂无输出,返回AVERROR_EOF表示解码结束
解码后 AVFrame 的 format 是 AV_SAMPLE_FMT_S16,但想要 float PCM 怎么办
FFmpeg 解码器默认输出整数格式(如 AV_SAMPLE_FMT_S16 或 AV_SAMPLE_FMT_FLTP),不自动转 float。想得到 AV_SAMPLE_FMT_FLT(interleaved float32)或 AV_SAMPLE_FMT_FLTP(planar float32),得自己做重采样。
关键步骤:
立即学习“C++免费学习笔记(深入)”;
- 用
swr_alloc_set_opts配置重采样上下文:目标out_sample_fmt = AV_SAMPLE_FMT_FLT,目标out_sample_rate和out_channel_layout通常与输入一致(除非你真要改采样率或声道数) - 调用
swr_init初始化,失败则检查参数是否合法(比如AV_CH_LAYOUT_MONO和双声道数据不匹配就会 init 失败) - 每次拿到
AVFrame后,用swr_convert转换,输出 buffer 需提前 malloc:大小 =nb_samples × av_get_bytes_per_sample(AV_SAMPLE_FMT_FLT) × nb_channels
MP3/AAC 文件解码后 PCM 数据是 planar 还是 interleaved
取决于解码器输出格式,和封装格式无关。MP3 解码器(libmp3dec)默认输出 AV_SAMPLE_FMT_S16 interleaved;AAC 解码器(libfdk_aac 或 aac_decoder)多数输出 AV_SAMPLE_FMT_FLTP(planar float)或 AV_SAMPLE_FMT_S16P(planar s16)。不要假设,必须查 frame->format 和 frame->channel_layout。
判断方法:
- 若
frame->format以P结尾(如AV_SAMPLE_FMT_FLTP),就是 planar;否则是 interleaved - planar 数据:每个声道一个指针,存于
frame->data[i],共frame->channels个有效指针 - interleaved 数据:所有声道样本交叉存放,只用
frame->data[0],总样本数 =frame->nb_samples × frame->channels
提取 PCM 时容易漏掉的字节对齐和 padding 问题
AVFrame 的 linesize[0] 不一定等于 nb_samples × bytes_per_sample × channels。FFmpeg 为内存对齐会在每行末尾补零(尤其 planar 格式),所以直接 memcpy frame->data[0] 到文件可能写入多余 padding 字节,导致播放杂音或长度错乱。
安全做法:
- 计算真实音频字节数:对于 interleaved,用
frame->nb_samples × av_get_bytes_per_sample(frame->format) × frame->channels - 对于 planar,每个声道单独算:
frame->nb_samples × av_get_bytes_per_sample(frame->format),再乘声道数 - 永远别用
frame->linesize[0]当有效数据长度,它只是内存分配步长
Planar 数据拼成 interleaved 写入文件时,也得手动按样本索引重组,不能直接 memcpy 整个 data[i] 区域。


















