使用FFmpeg C API提取MP4音频时发送解码包报错-1094995529
错误原因与修复方案
错误码解析
-1094995529 对应FFmpeg的AVERROR_INVALIDDATA,表示解码器收到了无法识别的无效数据包。核心问题是解码器上下文未正确初始化输入音频流的编码参数,导致解码器不知道如何解析输入的音频数据包。
关键错误点
你的代码在创建解码器上下文cd_ctx后,直接调用avcodec_open2打开解码器,但没有将输入音频流的codecpar参数复制到解码器上下文中。解码器缺少输入音频的编码格式、采样率、通道数等关键信息,无法处理数据包。
修复步骤
1. 正确初始化解码器上下文
在avcodec_alloc_context3之后,添加avcodec_parameters_to_context将输入流的编码参数复制到解码器上下文:
// allocate codec context cd_ctx = avcodec_alloc_context3(codec); if (!cd_ctx) return (printf("Could not allocate codec context\n"), 1); // 新增:从输入流的codecpar复制参数到解码器上下文 if (avcodec_parameters_to_context(cd_ctx, fmt_ctx->streams[audio_stream_index]->codecpar) < 0) return (printf("Could not copy codec parameters to decoder context\n"), 1); // open codec if (avcodec_open2(cd_ctx, codec, NULL) < 0) return (printf("Could not open codec\n"), 1);
2. 完整处理解码流程
发送数据包到解码器后,必须循环调用avcodec_receive_frame获取解码后的原始音频帧,否则解码器内部缓冲区会溢出,导致后续错误:
// read packets AVFrame *pframe = av_frame_alloc(); if (!pframe) return (printf("Could not allocate packet frame\n"), 1); AVPacket pkt; while (1) { int ret = av_read_frame(fmt_ctx, &pkt); if (ret < 0) break; printf("packet stream index %d\n", pkt.stream_index); if (pkt.stream_index == audio_stream_index) { printf("packet size %d\n", pkt.size); ret = avcodec_send_packet(cd_ctx, &pkt); if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) printf("EAGAIN or EOF\n"); else if (ret < 0) { char errbuf[AV_ERROR_MAX_STRING_SIZE]; av_strerror(ret, errbuf, sizeof(errbuf)); return (printf("Error sending packet to decoder: %s\n", errbuf), 1); } // 新增:循环获取解码后的帧 while (ret >= 0) { ret = avcodec_receive_frame(cd_ctx, pframe); if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) break; else if (ret < 0) { char errbuf[AV_ERROR_MAX_STRING_SIZE]; av_strerror(ret, errbuf, sizeof(errbuf)); return (printf("Error receiving frame from decoder: %s\n", errbuf), 1); } // 这里可以添加将解码后的帧发送到MP3编码器的逻辑 printf("Decoded frame with %d samples\n", pframe->nb_samples); av_frame_unref(pframe); // 释放帧的引用 } } av_packet_unref(&pkt); // 必须释放数据包,避免内存泄漏 }
3. 完善输出编码器配置
你的代码仅设置了输出流的codecpar,但未创建编码器上下文并打开,无法完成MP3编码。需要添加编码器上下文的初始化:
// 创建输出编码器上下文 AVCodecContext *out_cd_ctx = avcodec_alloc_context3(out_codec); if (!out_cd_ctx) return (printf("Could not allocate output codec context\n"), 1); // 设置编码器参数 out_cd_ctx->codec_id = AV_CODEC_ID_MP3; out_cd_ctx->sample_fmt = AV_SAMPLE_FMT_FLTP; out_cd_ctx->sample_rate = 44100; out_cd_ctx->channel_layout = AV_CH_LAYOUT_STEREO; out_cd_ctx->channels = 2; out_cd_ctx->bit_rate = 128000; // MP3比特率 // 打开编码器 if (avcodec_open2(out_cd_ctx, out_codec, NULL) < 0) return (printf("Could not open output codec\n"), 1); // 将编码器参数复制到输出流的codecpar if (avcodec_parameters_from_context(out_codecpar, out_cd_ctx) < 0) return (printf("Could not copy output codec parameters\n"), 1);
4. 补充资源释放逻辑
程序结束前需要释放所有分配的资源:
// 释放资源 av_frame_free(&pframe); avcodec_free_context(&cd_ctx); avcodec_free_context(&out_cd_ctx); avformat_close_input(&fmt_ctx); avio_close(out_ctx->pb); avformat_free_context(out_ctx);
FFmpeg C API数据包处理指导
输入解码流程
- 读取数据包:用
av_read_frame从输入文件读取AVPacket,每个包对应一段压缩的音视频数据。 - 匹配流索引:根据包的
stream_index判断是音频还是视频流,只处理目标流的包。 - 发送到解码器:调用
avcodec_send_packet将数据包送入解码器,注意:- 若返回
AVERROR(EAGAIN),表示解码器需要先读取已解码的帧才能继续接收新包。 - 处理完所有数据包后,需要发送
NULL包(avcodec_send_packet(cd_ctx, NULL))来flush解码器剩余的帧。
- 若返回
- 接收解码帧:循环调用
avcodec_receive_frame获取解码后的原始帧,直到返回AVERROR(EAGAIN)或AVERROR_EOF。
输出编码流程
- 发送原始帧:将解码得到的
AVFrame送入编码器(avcodec_send_frame)。 - 接收编码包:循环调用
avcodec_receive_packet获取编码后的压缩数据包。 - 写入输出文件:设置数据包的
stream_index为输出流的索引,用av_interleaved_write_frame写入输出文件(确保音视频同步)。 - Flush编码器:发送
NULL帧(avcodec_send_frame(out_cd_ctx, NULL))来获取编码器剩余的数据包。
核心注意事项
- 资源管理:所有
AVPacket必须用av_packet_unref释放,AVFrame用av_frame_unref或av_frame_free释放,上下文用对应的avcodec_free_context、avformat_free_context释放。 - 错误处理:所有FFmpeg API的返回值都要检查,用
av_strerror将错误码转为可读字符串,方便调试。 - 参数匹配:解码器/编码器的参数必须与输入/输出流的参数一致,否则会出现无效数据或编码失败。
内容的提问来源于stack exchange,提问作者Saad Out03
相关产品推荐
相关产品推荐

