使用FFmpeg官方示例解码MPEG1视频输出YUV像素损坏,转RGB后问题依旧
问题成因
- 解码配置问题:你使用的FFmpeg官方基础解码示例默认未开启高阶错误恢复、错误隐藏配置,当MPEG1码流中出现宏块AC系数损坏、运动矢量丢失类错误时,仅做了最低限度的错误隐藏,修复效果差,最终输出的YUV帧本身就存在损坏块。日志中
concealing xxx errors的提示也印证了这一点。 - 格式转换代码存在bug:FFmpeg解码输出的
AVFrame存在行对齐规则,每行实际占用字节数linesize会大于等于图像宽度,你构造OpenCV的Mat时没有传入linesize作为步长参数,会导致像素读取错位,就算解码出完好帧也可能出现花屏问题。
解决方案
第一步:优化解码器配置
初始化AVCodecContext时添加错误恢复、错误隐藏相关参数,尽可能修复码流错误:
// 开启严格的错误检测 av_opt_set(codec_ctx->priv_data, "err_detect", "aggressive", 0); codec_ctx->err_recognition = AV_EF_CRCCHECK | AV_EF_CAREFUL | AV_EF_COMPLIANT; // 开最高级别错误隐藏,猜测丢失的运动矢量、自动去块 codec_ctx->error_concealment = FF_EC_GUESS_MVS | FF_EC_DEBLOCK;
同时解码后判断帧的损坏标记,若frame->flags & AV_FRAME_FLAG_CORRUPT为真,可以选择丢弃该帧,复用上一帧完好的帧输出,避免用户看到花屏。
第二步:修复OpenCV转换代码
构造Mat时传入AVFrame对应的linesize作为步长参数,避免像素错位:
cv::Size actual_size(frame->width, frame->height); cv::Size half_size(frame->width/2, frame->height/2); // 新增linesize作为步长参数 cv::Mat y(actual_size, CV_8UC1, frame->data[0], frame->linesize[0]); cv::Mat u(half_size, CV_8UC1, frame->data[1], frame->linesize[1]); cv::Mat v(half_size, CV_8UC1, frame->data[2], frame->linesize[2]); cv::Mat u_resized, v_resized; cv::resize(u, u_resized, actual_size, 0, 0, cv::INTER_NEAREST); cv::resize(v, v_resized, actual_size, 0, 0, cv::INTER_NEAREST); cv::Mat yuv; std::vector<cv::Mat> yuv_channels = { y, u_resized, v_resized }; cv::merge(yuv_channels, yuv); cv::Mat bgr; cv::cvtColor(yuv, bgr, cv::COLOR_YUV2BGR); cv::imshow("x",bgr); cv::waitKey(1000/25);
如果希望更稳妥的颜色空间转换,可以直接用FFmpeg的sws_scale模块直接把YUV420P帧转换为BGR格式,无需手动处理通道合并。
内容的提问来源于stack exchange,提问作者user3807476
相关产品推荐
相关产品推荐

