最近在做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导致这个问题。如果有人之前遇到过这个问题,希望可以指导一下我,多谢。