使用FFmpeg API编码YUV420P帧时avcodec_receive_packet返回错误1的排查
avcodec_receive_packet返回错误码1的排查方案 问题背景
使用FFmpeg API对640*480、30FPS的YUV420P原始视频帧编码为MPEG2格式,调用avcodec_receive_packet()时返回错误码1(对应FFmpeg定义的AVERROR(EAGAIN)),需排查问题以获取编码后的数据包用于TCP传输。用户提供的省略错误处理的代码如下:
EncodeFrame(const uint8_t* rawBuffer, size_t size) { const AVCodec* codec = avcodec_find_encoder(AV_CODEC_ID_MPEG2VIDEO); AVCodecContext *codec_ctx = avcodec_alloc_context3(codec); codec_ctx->bit_rate = 200000; codec_ctx->width = 640; codec_ctx->height = 480; codec_ctx->time_base = (AVRational){1,30}; codec_ctx->gop_size = 10; codec_ctx->max_b_frames = 1; codec_ctx->pix_fmt = AV_PIX_FMT_YUV420P; codec_ctx->codec_type = AVMEDIA_TYPE_VIDEO; avcodec_open2(codec_ctx, codec, nullptr); AVFrame* frame = av_frame_alloc(); frame->format = AV_PIX_FMT_YUV420P; frame->width = 640; frame->height = 480; av_image_fill_arrays(frame->data, frame->linesize, rawBuffer, AV_PIX_FMT_YUV420P, 640, 480, 1); avcodec_send_frame(codec_ctx, frame); AVPacket* pkt = av_packet_alloc(); avcodec_receive_packet(codec_ctx, pkt) ... }
错误码1的含义
错误码1对应AVERROR(EAGAIN),表示当前编码器无可用输出数据包,原因通常是:需要输入更多帧供编码器处理,或编码器因帧依赖(如B帧、GOP初始化)暂无法生成输出。
具体排查与修复步骤
1. 补全所有关键步骤的错误检查
代码省略了核心错误处理,会掩盖编码器初始化、帧数据填充等环节的潜在问题:
- 检查
avcodec_find_encoder()返回值,确认MPEG2编码器已编译进当前FFmpeg库 - 检查
avcodec_alloc_context3()是否成功分配上下文 - 检查
avcodec_open2()返回值,确保编码器正常打开 - 检查
av_image_fill_arrays()返回值,验证原始帧数据填充正确 - 检查
avcodec_send_frame()返回值,确认帧成功送入编码器
示例错误检查片段:
int ret; const AVCodec* codec = avcodec_find_encoder(AV_CODEC_ID_MPEG2VIDEO); if (!codec) { // 处理编码器不存在的错误 } AVCodecContext *codec_ctx = avcodec_alloc_context3(codec); if (!codec_ctx) { // 处理上下文分配失败的错误 } // ... 设置编码器参数 ... ret = avcodec_open2(codec_ctx, codec, nullptr); if (ret < 0) { // 处理编码器打开失败的错误 } ret = av_image_fill_arrays(frame->data, frame->linesize, rawBuffer, AV_PIX_FMT_YUV420P, 640, 480, 1); if (ret < 0) { // 处理帧数据填充失败的错误 } ret = avcodec_send_frame(codec_ctx, frame); if (ret < 0) { // 处理帧送入编码器失败的错误 }
2. 修正编码器输入输出的循环逻辑
MPEG2编码器(尤其是开启B帧时)依赖多帧输入生成输出:
- 设置
max_b_frames = 1时,编码器需要后续帧作为参考生成B帧,首次送帧后可能无输出 - GOP大小设为10时,编码器需积累足够帧才能生成I帧及后续P/B帧
正确处理逻辑是循环调用avcodec_receive_packet(),直到返回AVERROR(EAGAIN)或错误:
AVPacket* pkt = av_packet_alloc(); if (!pkt) { // 处理数据包分配失败的错误 } while (true) { int ret = avcodec_receive_packet(codec_ctx, pkt); if (ret == AVERROR(EAGAIN)) { // 当前无输出,需输入更多帧,退出循环等待下一次送帧 break; } else if (ret < 0) { // 处理编码错误 break; } // 成功获取编码数据包,执行TCP传输等后续操作 // 处理完成后必须调用av_packet_unref重置数据包 av_packet_unref(pkt); } av_packet_free(&pkt);
3. 补充帧时间戳设置
代码未设置帧的pts(显示时间戳),编码器需要时间戳来处理帧顺序和GOP逻辑:
// 假设当前是第n帧(从0开始计数) frame->pts = n; // 对应30FPS的time_base(1/30),pts直接用帧索引即可
4. 补充编码器帧率参数
MPEG2编码器需明确设置帧率,否则可能无法正确识别输入帧速率:
codec_ctx->framerate = (AVRational){30, 1};
5. 优化资源管理逻辑
每次调用EncodeFrame()都重新创建编码器上下文、帧和数据包,会导致内存泄漏且效率低下。正确做法是将编码器初始化、上下文创建放在函数外部,仅在首次调用时执行,后续调用仅负责送帧和取数据包。
6. 验证原始YUV数据正确性
确认输入的rawBuffer是标准640*480 YUV420P格式:
- YUV420P的总大小应为
640*480*3/2 = 460800字节,检查size参数是否匹配 - 确认Y、U、V分量顺序正确,无额外行对齐干扰(
av_image_fill_arrays()的align设为1符合要求)
总结
错误码1的核心原因是编码器暂无可输出数据包,可能由输入帧不足、时间戳缺失、编码器未正确初始化或原始数据异常导致。按上述步骤逐一排查,重点补全错误检查和修正编码器输入输出循环逻辑,即可解决问题。
内容的提问来源于stack exchange,提问作者CrIcKeT

