使用FFmpeg 4.0的avcodec_decode_video2解码RTSP H264视频出错求助
解决FFmpeg 4.0中
avcodec_decode_video2解码RTSP H264高清视频的错误问题 结合你提供的代码和FFmpeg 4.0的特性,我整理了几个针对性的排查和修复方向,帮你解决高清H264解码出错的问题:
一、补全解码上下文的关键配置
高清H264(如1080P/4K)解码对上下文配置有更高要求,你当前仅复制原始Codec上下文,可能漏掉了关键设置:
- 启用多线程解码:高清流单线程解码容易出现性能瓶颈或错误,根据CPU核心数设置线程数
- 启用帧引用计数:避免内存异常导致的解码失败
- 实时流低延迟配置:适配RTSP实时传输特性
修改代码中复制上下文后的逻辑:
if(avcodec_copy_context(pCodecCtx, pCodecCtxOrig) != 0) { fGrabb=-5; // Error copying codec context } else { // 添加高清解码必备配置 pCodecCtx->thread_count = av_cpu_count(); // 自动匹配CPU核心数 pCodecCtx->refcounted_frames = 1; // 启用帧引用计数,避免内存问题 pCodecCtx->flags |= AV_CODEC_FLAG_LOW_DELAY; // 适配RTSP实时流的低延迟需求 // Open codec if(avcodec_open2(pCodecCtx, pCodec, NULL)<0) { fGrabb=-6; // Could not open codec } // ... 后续帧分配等代码 }
二、修复RTSP流的数据包处理问题
RTSP实时流存在数据包乱序、丢包或时间戳缺失的可能,会直接导致解码失败:
- 让FFmpeg自动生成缺失的时间戳,避免时序错误
- 不要跳过任何视频数据包,哪怕看起来无效
在打开输入流后添加配置:
if(avformat_open_input(&pFormatCtx, nameVideoStream, NULL, NULL)!=0) { fGrabb=-1; // Couldn't open file } else { // 让FFmpeg自动生成缺失的时间戳,适配RTSP流 pFormatCtx->flags |= AVFMT_FLAG_GENPTS; // Retrieve stream information if(avformat_find_stream_info(pFormatCtx, NULL)<0) { fGrabb=-2; // Couldn't find stream information } // ... 后续找视频流等代码 }
三、完善解码错误的检测与处理
你的代码未检查avcodec_decode_video2的返回值,无法及时捕获解码错误并处理:
int len = avcodec_decode_video2(pCodecCtx, pFrame, &frameFinished, &packet); if (len < 0) { // 打印错误信息,方便定位问题 char errbuf[1024]; av_strerror(len, errbuf, sizeof(errbuf)); // 这里可以加入日志记录逻辑 av_free_packet(&packet); continue; // 跳过错误包,继续处理下一个 } nPacket++; // Did we get a video frame? if(frameFinished) { // ... 格式转换与窗口显示代码 }
四、修复过时API与资源泄漏问题
FFmpeg 4.0中部分API已过时,不正确的资源释放会间接导致解码不稳定:
- 用
av_packet_unref(&packet)替代过时的av_free_packet(&packet) - 确保所有分配的资源在退出时完全释放
在函数末尾补充资源释放逻辑:
// 释放缩放上下文 sws_freeContext(sws_ctx_to_RGB); sws_freeContext(sws_ctx_to_YUV); // 释放缓冲区 av_free(buffer); av_free(bufferRGB); av_free(bufferYUV); // 释放格式上下文与解码上下文 avformat_close_input(&pFormatCtx); avcodec_free_context(&pCodecCtx);
五、推荐替换为更稳定的新解码API
FFmpeg 4.0中avcodec_decode_video2已被标记为过时,新的avcodec_send_packet+avcodec_receive_frameAPI对高清流的兼容性更好,替换解码循环逻辑:
while ((fGrabb==1)||(fGrabb==100)) { if (av_read_frame(pFormatCtx, &packet) >= 0) { if(packet.stream_index==videoStream) { int ret = avcodec_send_packet(pCodecCtx, &packet); if (ret == AVERROR(EAGAIN)) { // 需要更多数据,继续等待 } else if (ret < 0) { char errbuf[1024]; av_strerror(ret, errbuf, sizeof(errbuf)); // 记录错误信息 av_packet_unref(&packet); continue; } // 循环接收解码后的帧(可能一次解码多帧) while (ret >= 0) { ret = avcodec_receive_frame(pCodecCtx, pFrame); if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) { break; } else if (ret < 0) { char errbuf[1024]; av_strerror(ret, errbuf, sizeof(errbuf)); // 记录错误信息 break; } // 解码成功,执行格式转换与显示逻辑 sws_scale(sws_ctx_to_YUV, (uint8_t const * const *)pFrame->data, pFrame->linesize, 0, pCodecCtx->height, pFrameYUV->data, pFrameYUV->linesize); sws_scale(sws_ctx_to_RGB, (uint8_t const * const *)pFrame->data, pFrame->linesize, 0, pCodecCtx->height, pFrameRGB->data, pFrameRGB->linesize); HDC hdc=GetDC(hWndM); SetDIBitsToDevice(hdc, 0, 0, pCodecCtx->width, pCodecCtx->height, 0, 0, 0, pCodecCtx->height,pFrameRGB->data[0], (LPBITMAPINFO)&RealBsqHdr, DIB_RGB_COLORS); ReleaseDC(hWndM,hdc); av_frame_unref(pFrame); } } av_packet_unref(&packet); } }
建议你先添加错误日志打印,用av_strerror把错误码转换为可读信息,这样能更精准定位是解码失败、数据包问题还是资源不足导致的错误。
内容的提问来源于stack exchange,提问作者Nguyen Ba Thi
相关产品推荐
相关产品推荐

