FFmpeg解码多出一帧?菜鸡定位一个尚未解决的BUG

最近在做demo时,需要使用 FFmpeg 解码。

由于只需要评测编码器的性能,不需要考虑解码器的效率,所以自然而然想到直接使用命令行调用 FFmpeg.exe 就行(主要是怕麻烦+菜鸡)。
当时使用了以下代码:

char* decFileName = "output.yuv";
sprintf(cmd, "ffmpeg -i %s -vframes %d %s -y", outFileName, numEncode, decFileName); //outFileName --> xxx.264
system(cmd);

很轻松的解码得到 YUV 文件,再送到后面的质量评估模块去计算 PSNR + SSIM 等等指标。

**so easy,我会心一笑。 **

可是,问题出现了!

除了第一帧 PSNR 和 SSIM 看似正确,后面的帧计算得到的 PSNR 和 SSIM 出现断崖式下降。
在这里插入图片描述
这是什么问题呢?难道是 PSNR 和 SSIM 计算模块代码有BUG? 认真Check一遍,发现没有大的问题。

在网上找了两个开源的 PSNR 计算工具,对比了下,计算的和我算出的也相同,说明后续的质量评估模块应该OK。
再对比输入输出 YUV 文件的大小和帧数,也没有发现问题。

于是乎,我就把问题定位到了两个可能的方向:

1. 解码没问题,编码器性能差。比如说,可能编码器的码控策略优化的不好,I帧分配太多Bits,P帧分配的Bits数目不够导致质量断崖式下降。

2. 编码没问题,解码出了问题。

针对第一个方向,我用 YUV 播放器对比了编码前后重建的 YUV 文件,发现肉眼所见质量几乎无变化,不可能使得 PSNR 下降超过10。所以说,编码器在编码质量这个方面应该没有问题。

于是,我又查看之后每一帧的视频质量,在对比了几百帧,直到出现了一个 场景切换帧 后,我才发现了问题。原来是出现了帧延迟 ==> 拿着第 N 帧 和 第 N+1 帧做 比较,PSNR 和 SSIM 当然会出现大差别。

所以,我重新开始从第一帧追查,发现第一帧重复了两次。也就是说,解码后的图像是 1、 1、 2、 3、 4、 5、 6、 …、 N-1的次序,然而计算质量时,对比的是原始序列第 1、 2、 3、 4、 5、 6、 …、 N-1、N 的次序。所以说,只有第一帧是匹配的,后面匹配都有问题。在场景切换帧之前,虽然误匹配,但是两帧肉眼差别很小,主观基本看不出差别,然而 PSNR 是逐像素对比计算的,即使主观区别不大,其数值也会出现断崖式下降,成功破案

比如说,这是第2帧的匹配错误,肉眼很难分辨区别,但是逐像素对比就会差别很大。
在这里插入图片描述
这是后续帧出现了明显的误匹配(上方横条),PSNR会掉至低于20,促使我发现了问题所在。

在这里插入图片描述

继续定位BUG。之前检查过,输出YUV的帧数和原始YUV的帧数相同,那么最后一帧岂不是丢帧了?

再次查看 FFmpeg 调用代码,发现是因为在调用时指定了解码帧数。把之前的代码去掉解码帧数的限制,如下所示:

char* decFileName = "output.yuv";
sprintf(cmd, "ffmpeg -i %s %s -y", outFileName, decFileName); //outFileName --> xxx.264
system(cmd);

果然,解码出的 YUV帧数是 numEncode + 1 帧,第一帧莫名其妙地重复了一次!

继续进行问题定位,定位以下两个方向:

1. 还是编码器出了问题,第一帧的码流 repeat 一次。

2. 调用 FFmpeg 的方式不对,或者 FFmpeg针对过大的文件解码可能会错误( > 2500帧, 720p)

针对第一个方向,我用 H264VISA 查看了码流,确实码流没有问题,并未重复第一帧。

所以,现在我认为我出现的问题是 “调用 FFmpeg 的方式不对,或者 FFmpeg针对过大的文件解码可能会错误( > 2500帧, 720p)”,目前该BUG还未解决,有可能又是像上次一样,基础的小BUG导致这个问题。如果有人之前遇到过这个问题,希望可以指导一下我,多谢。

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
你可以使用FFmpeg库来逐帧解码MP3文件。以下是一个使用FFmpeg解码MP3文件并逐帧处理的示例代码: ```cpp extern "C" { #include <libavformat/avformat.h> #include <libavcodec/avcodec.h> } int main() { av_register_all(); AVFormatContext *formatContext = avformat_alloc_context(); if (avformat_open_input(&formatContext, "input.mp3", NULL, NULL) != 0) { // 打开文件失败 return -1; } if (avformat_find_stream_info(formatContext, NULL) < 0) { // 获取流信息失败 return -1; } int audioStreamIndex = -1; for (int i = 0; i < formatContext->nb_streams; i++) { if (formatContext->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) { audioStreamIndex = i; break; } } if (audioStreamIndex == -1) { // 未找到音频流 return -1; } AVCodecParameters *codecParameters = formatContext->streams[audioStreamIndex]->codecpar; AVCodec *codec = avcodec_find_decoder(codecParameters->codec_id); if (codec == NULL) { // 找不到解码器 return -1; } AVCodecContext *codecContext = avcodec_alloc_context3(codec); if (avcodec_parameters_to_context(codecContext, codecParameters) != 0) { // 获取解码器上下文失败 return -1; } if (avcodec_open2(codecContext, codec, NULL) < 0) { // 打开解码器失败 return -1; } AVPacket *packet = av_packet_alloc(); AVFrame *frame = av_frame_alloc(); while (av_read_frame(formatContext, packet) >= 0) { if (packet->stream_index == audioStreamIndex) { if (avcodec_send_packet(codecContext, packet) != 0) { // 发送数据包到解码器失败 break; } while (avcodec_receive_frame(codecContext, frame) == 0) { // 在这里处理每一帧音频数据 // frame->data[0] 中存储了解码后的音频数据 // frame->nb_samples 表示每个声道的采样点数 // frame->channels 表示声道数 // frame->sample_rate 表示采样率 } } av_packet_unref(packet); } av_packet_free(&packet); av_frame_free(&frame); avcodec_close(codecContext); avcodec_free_context(&codecContext); avformat_close_input(&formatContext); return 0; } ``` 你可以将输入文件名替换为你要解码的MP3文件的路径。在代码中的注释部分,你可以根据你的需求来处理每一帧的音频数据。 请注意,上述代码只是一个简单的示例,你可能需要根据自己的项目需求进行适当的修改和错误处理。此外,你需要在编译时链接FFmpeg库,并正确设置头文件和库文件路径。 希望这能对你有所帮助!如果有任何问题,请随时问我。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值