@ltlovezh
2020-11-05T10:43:08.000000Z
字数 1976
阅读 1684
ffmpeg
音视频
SAR表示单个像素显示的宽高比,即像素不都是按照1:1显示。
SAR存储在Mp4的moov -> trak -> mdia -> minf -> stbl -> stsd -> avc1 -> avcc Box的SPS里面,如下图所示:
libavcodec/h264_ps.c
文件中decode_vui_parameters
函数负责解析出SAR,如下所示:
三者之间的关系:PAR * SAR = DAR
下面看一个例子,如下图所示:
每个方格代表一个像素,宽度为5像素,高度为4像素,即PAR=5 : 4。
假设图像的显示宽度为160,高度为120,即DAR=4 : 3。
那么可以计算出SAR = DAR / PAR = 16 : 15,表示像素方格是一个长方形。
FFmpeg提供了多个SAR变量:
FFprobe显示的SAR是根据libavformat/utils.c
文件中的av_guess_sample_aspect_ratio
函数计算得来,其实就是按照优先级获取SAR,取到则返回,优先级从高到底分别是:AVStream->sample_aspect_ratio > AVFrame->sample_aspect_ratio > AVStream->codecpar->sample_aspect_ratio。
对于DAR,AVStream->display_aspect_ratio始终为0:0,参考ffprobe代码,可知DAR是通过av_reduce计算得到的,如下所示:
AVRational sar, dar;
// 流编码参数
AVCodecParameters * codecpar = AVStream->codecpar;
// 计算出sar
sar = av_guess_sample_aspect_ratio(AVFormatContext, AVStream, NULL);
// 根据视频帧宽高和sar计算出dar
av_reduce(&dar.num, &dar.den, codecpar->width * sar.num, codecpar->height * sar.den, 1024*1024);
FFmpeg提供了Video AVFilter:setdar, setsar,可以修改视频的SAR和DAR。
例如:origin.mp4是一个1080*1920的视频,默认SAR是1 : 1,DAR是9:16。
现在我们把SAR设置为2 : 1,即单个像素是2 : 1的矩形,那么视频尺寸就是540*1920。
ffmpeg -i origin.mp4 -vf scale=540:1920,setsar=2.0 output.mp4
output.mp4的信息如下所示:
针对上述SAR是2:1的视频,若我们不处理SAR,直接按照视频帧宽高:540*1920来处理,那么正常的9:16视频,将会被显示成4.5:16,即整个视频上下拉伸了2倍。
兼容SAR也很简单,分为CPU和GPU处理(假设是YUV420P像素数据)。
通过libyuv函数(例如:libyuv::Scale),把540*1920的像素数据,缩放到9 : 16的任意尺寸,例如:540*960(宽度不变,按照DAR计算出高度)。
先按照540*1920的真实像素数据,上传到YUV纹理,然后按照9 : 16的原始比例计算MVP矩阵,即计算MVP矩阵时,源纹理不是按照4.5 : 16的宽高比,而是按照9 : 16的宽高比,最后渲染到目标FBO纹理。
若一个视频既有旋转角度,又有SAR和DAR,哪里这里的SAR是旋转之前的像素宽高比还是旋转之后的像素宽高比那?或者说这里的DAR是旋转之前的显示高比还是旋转之后的显示宽高比那?
这个问题在网上查了蛮久,都没有找到解答,最后从EXOPlayer源码MediaCodecVideoRenderer.onOutputFormatChanged和PlayerView.onVideoSizeChanged中找到了答案。
SAR是旋转之前的像素宽高比,DAR是旋转之前的显示宽高比,旋转之后,SAR和DAR都要取倒数。