FFmpeg多线程解码丢失帧,如何获取未解码的最终帧?
解决FFmpeg多线程解码丢帧问题
你遇到的问题是FF_THREAD_FRAME模式下的正常行为——解码器需要缓存与线程数等量的帧来填充线程池,所以前期发送数据包后不会立刻输出解码帧;当所有输入数据包发送完毕后,若不主动刷新解码器缓存,这些缓存的帧就会被丢弃,导致最后一批帧无法解码。
具体解决步骤:
- 正常处理输入数据包:循环读取并发送数据包到解码器,直到
av_read_frame()返回EOF(无更多输入)。 - 发送空数据包触发刷新:在所有输入数据包发送完成后,调用
avcodec_send_packet(codec_ctx, NULL),这会告诉解码器已无新输入,需要输出所有缓存的解码帧。 - 循环接收剩余帧:持续调用
avcodec_receive_frame(),直到它返回AVERROR_EOF,此时所有缓存的帧都会被取出。
修正后的代码示例:
AVPacket pkt = {0}; AVFrame *frame = av_frame_alloc(); int ret; AVCodecContext *codec_ctx = ...; // 你的解码器上下文 AVFormatContext *fmt_ctx = ...; // 你的格式上下文 // 正常读取并解码数据包 while (av_read_frame(fmt_ctx, &pkt) == 0) { ret = avcodec_send_packet(codec_ctx, &pkt); if (ret < 0) { av_log(NULL, AV_LOG_ERROR, "发送数据包失败: %s\n", av_err2str(ret)); av_packet_unref(&pkt); continue; } // 尝试接收所有当前可用的解码帧 while (ret >= 0) { ret = avcodec_receive_frame(codec_ctx, frame); if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) { break; } else if (ret < 0) { av_log(NULL, AV_LOG_ERROR, "接收帧失败: %s\n", av_err2str(ret)); goto cleanup; } // 处理解码后的帧(如保存、显示) process_decoded_frame(frame); av_frame_unref(frame); } av_packet_unref(&pkt); } // 发送空数据包,触发解码器刷新缓存 ret = avcodec_send_packet(codec_ctx, NULL); if (ret < 0 && ret != AVERROR_EOF) { av_log(NULL, AV_LOG_ERROR, "刷新发送失败: %s\n", av_err2str(ret)); goto cleanup; } // 接收所有缓存的剩余帧 while (ret >= 0) { ret = avcodec_receive_frame(codec_ctx, frame); if (ret == AVERROR_EOF) { break; } else if (ret == AVERROR(EAGAIN)) { continue; } else if (ret < 0) { av_log(NULL, AV_LOG_ERROR, "刷新接收失败: %s\n", av_err2str(ret)); goto cleanup; } process_decoded_frame(frame); av_frame_unref(frame); } cleanup: av_frame_free(&frame); av_packet_free(&pkt); // 其他资源清理...
额外注意事项:
- 避免使用废弃的
av_stream->codec,改用avcodec_parameters_to_context(codec_ctx, av_stream->codecpar)来初始化解码器上下文,旧API可能导致兼容性问题。 avcodec_send_packet(NULL)返回AVERROR_EOF是正常现象,无需报错,只需继续接收帧直到返回AVERROR_EOF即可。
内容的提问来源于stack exchange,提问作者Patrick McKeever
相关产品推荐
相关产品推荐

