@ltlovezh
        
        2020-11-05T02:43:08.000000Z
        字数 1976
        阅读 2557
    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;// 计算出sarsar = av_guess_sample_aspect_ratio(AVFormatContext, AVStream, NULL);// 根据视频帧宽高和sar计算出darav_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都要取倒数。
