必须先调用avformat_network_init()等初始化函数,否则avformat_open_input可能静默失败;查找音频流应使用av_find_best_stream而非硬编码索引;解码需遵循“送包-循环收帧”推拉模型,EOF后还需发送NULL包flush。

ffmpeg\_libav 初始化失败怎么办
直接调用 avformat_open_input 前必须初始化 FFmpeg 的内部组件,否则多数操作会静默失败或返回 AVERROR_UNKNOWN。常见表现是打开文件成功但流数量为 0,或 av_find_best_stream 返回 -1。
正确做法是全局一次性调用:
avformat_network_init(); // 如需 HTTP/RTMP 等协议才必须 avdevice_register_all(); // 如需摄像头等设备输入才需要 // 实际上,现代 FFmpeg(4.0+)推荐只调用: av_log_set_level(AV_LOG_WARNING); // 可选,控制日志 // 其余 avcodec_register_all() 等已废弃,由 avformat_open_input 内部按需加载
不初始化就硬开文件,AVFormatContext->nb_streams 很可能为 0,后续所有流查找都失效。
怎么找到视频里的音频流索引
FFmpeg 不保证音频流在第几个位置,不能写死 stream_index = 1。必须遍历 AVFormatContext->streams 并检查 codecpar->codec_type。
立即学习“C++免费学习笔记(深入)”;
实操建议:
- 用
av_find_best_stream(fmt_ctx, AVMEDIA_TYPE_AUDIO, -1, -1, NULL, 0)—— 它自动跳过无效流、过滤掉数据损坏的候选,比手写循环更可靠 - 若该函数返回负值(如
AVERROR_STREAM_NOT_FOUND),说明文件确实不含可用音频流,不是代码写错了 - 拿到流索引后,立刻用
avcodec_parameters_to_context创建解码器上下文,不要复用fmt_ctx->streams[stream_idx]->codec(已废弃)
解码音频帧时为什么老卡在 avcodec_receive_frame
这是初学者最常卡住的地方:没理解 FFmpeg 解码器的“推拉模型”。avcodec_receive_frame 不是“每次调一个包就出一帧”,而是“从内部缓冲区拉一帧”,它可能返回 AVERROR(EAGAIN) 表示缓冲区空,需要先喂更多 AVPacket。
典型错误流程:
// ❌ 错误:送一个 packet 就急着收 frame
avcodec_send_packet(dec_ctx, &pkt);
avcodec_receive_frame(dec_ctx, frame); // 极大概率返回 EAGAIN
// ✅ 正确:循环送包,直到 receive 成功或明确失败
while (av_read_frame(fmt_ctx, &pkt) >= 0) {
if (pkt.stream_index == audio_stream_idx) {
avcodec_send_packet(dec_ctx, &pkt);
while (avcodec_receive_frame(dec_ctx, frame) == 0) {
// 处理 frame,例如转 PCM、写 WAV
do_audio_process(frame);
}
}
av_packet_unref(&pkt);
}
注意:avcodec_receive_frame 在 EOF 后仍可能输出剩余帧(flush),所以循环结束前要再 send NULL packet 触发 flush。
输出 PCM 数据时采样格式和布局怎么对齐
原始音频帧的 frame->format(如 AV_SAMPLE_FMT_S16)和 frame->channel_layout(如 AV_CH_LAYOUT_STEREO)不一定匹配你目标格式。直接 memcpy 到文件会播放杂音或无声。
必须做重采样:
- 用
swr_alloc()+swr_config_frame()初始化重采样器,目标格式设为你想要的(比如AV_SAMPLE_FMT_S16,44100Hz,AV_CH_LAYOUT_STEREO) -
swr_convert()输出的样本数可能与输入不等(因采样率变化),要用返回值判断实际写入字节数 - WAV 文件头需严格按 PCM 格式填写:位深、声道数、采样率,否则播放器拒绝识别
漏掉重采样或填错 WAV 头,结果就是文件能生成但无法播放——这种问题往往排查半天才发现是头信息里 bits_per_sample 写成了 8 而不是 16。


















