FFmpeg获取视频正确的宽高比

3,015 阅读4分钟

说起音视频开发,很多问题不遇到是不知道的。就在昨天,有一朋友问我:我的视频解析出来 “宽*高=640*480”,明显宽高比是4:3的,但是实际要正常播放时,宽高比是:16:9的,市面上大部分视频播放器都能按照16:9正常播放,少数是按照4:3播放的,而他自己根据640:480计算出来也是4:3的,这样画面就是错误的变形的了。

其实在没发现这个问题之前,我也是完全不知道还有这种情况的,没办法已经问我了,总得拿自己的播放器试一下,万一是他计算错了呢,然儿我自己的播放器也是4:3的,这就尴尬了,只有想办法解决这个问题,下面是我没解决这个问题之前的播放画面:

从图中,很明显的能看出画面被变形了,那么开始解决问题吧。

为了测试,分别用VLC和爱奇艺播放器来测试,测试结果他们都是以16:9来渲染的画面,这样再次说明自己的计算比例(用的是宽高比)是错误的,用MediaInfo查看视频信息,也是一样的16:9:

虽然知道了这个问题,但是用VLC、爱奇艺播放器和MediaInfo并不能解决问题的。不过还是有一点点收获,知道了“Diaplay aspect ratio”这个名词,然后加上ffempg关键字网上搜索这个词,非常惊喜的发现竟然FFmpeg的AVStream里面有display_aspect_ratio这个属性,那么解决这个问题就简单了,直接通过这个属性获取比值就可以了(当然不可能这么简单)。最为直观的我们还是打印出来看一下:

LOG_E("display_aspect_ratio: %d:%d", pFormatCtx->streams[i]->display_aspect_ratio.den, pFormatCtx->streams[i]->display_aspect_ratio.num);

看到结果是很崩溃的,竟然是0:0,欲哭无泪啊。只有再找其他线索了。思考中(难道FFmpeg不支持)......

既然是FFmpeg解码,那么ffplay也是用的FFmpeg,那么就用ffplay来播放一下就知道了,非常幸运的是ffplay也是16:9正常播放的:

这就看到了光明了,说明FFmpeg是能解决这个问题的(那是肯定能的),然后就去看ffplay的日志,发现了ffplay竟然打印出来了dar(display_aspect_ratio)值的,附带的还打印了sar(sample_aspect_ratio)值的,而且前面还有“Input”、“Metadata”等日志:

那么解决这个问题就很简单了,看ffplay的源码找这个日志打印的地方(虽然我基于FFmpeg已经写出了播放器,但还真的没去看过ffplay的源码,不过对FFmpeg的api也算是熟悉,看源码也不是很难)。源码过程如下:

1、找到ffplay的main方法,这就是入口(基于版本:FFmpeg v3.3.2):

2、入口找到了,就要看ffplay日志打印的过程是怎样的,通过日志可以发现是:先打印输入的url地址和一些支持的解码器类型,对应于日志中“Input”开头那一句,那么开始找这个(因为这就涉及到音视频解码流程、解码线程知识了,这里就不细说了,只列出相关方法跳转路径):

这样我们就顺利的找到了日志中的“Input”信息了。

3、按照顺序再找“Metadata”日志,在“av_dump_format”方法中开始找:

这样我们就找到了“Metadata”日志了,但是这个日志所在方法里面并没有我们需要的信息,所以继续返回“av_dump_format”中开始往下找:

4、找到我们需要的信息:

这样我们就可以看到日志中的数据是怎么得到的了,是通过函数“av_reduce”得到的实际的“dar”,函数实现还是蛮复杂的:

av_reduce(&display_aspect_ratio.num, &display_aspect_ratio.den,
                  st->codecpar->width  * (int64_t)st->sample_aspect_ratio.num,
                  st->codecpar->height * (int64_t)st->sample_aspect_ratio.den,
                  1024 * 1024);

就是这一句,就能从“sar”、宽和高中得到“dar”,(哈哈有点兴奋,马上验证一下):

av_reduce(&wlVideo->dvr.num, &wlVideo->dvr.den,
                              pFormatCtx->streams[i]->codecpar->width  * (int64_t)pFormatCtx->streams[i]->sample_aspect_ratio.num,
                              pFormatCtx->streams[i]->codecpar->height * (int64_t)pFormatCtx->streams[i]->sample_aspect_ratio.den,
                              1024 * 1024);
                    LOG_E("display_aspect_ratio: %d:%d", wlVideo->dvr.den, wlVideo->dvr.num);

 

这样就正常把视频渲染出来了。

总结:问题我们是解决不完的,但是我们要培养解决问题的能力,首先分析问题,然后想办法测试问题,当在测试中找到突破口时就要去验证,去找源码等来解决,这不是一蹴而就的,而是一个慢慢成长的过程。

**** 这里给出我的(Android)视频库地址:wlmedia

测试视频下载 密码:0ok7