视频SAR信息处理

4,449 阅读3分钟

SAR存储和解析

SAR表示单个像素显示的宽高比,即像素不都是按照1:1显示。 SAR存储在Mp4的moov -> trak -> mdia -> minf -> stbl -> stsd -> avc1 -> avcc Box的SPS里面,如下图所示: SPS-SAR

libavcodec/h264_ps.c文件中decode_vui_parameters函数负责解析出SAR,如下所示: SPS-SAR-FFmpeg

SAR/PAR/DAR之间的关系

  • SAR(Sample Aspect Ratio): 单个像素宽高比,即每个像素宽度与高度的比值。
  • PAR(Pixel Aspect Ratio): 像素数宽高比,图像的横向采集点数与纵向采集点数的比值,即像素个数的比值。
  • DAR(Display Aspect Ratio): 显示宽高比,图像最终展示的宽高比,播放器在渲染视频帧时,需要保持DAR比例。

三者之间的关系:** PAR * SAR = DAR **

下面看一个例子,如下图所示: 每个方格代表一个像素,宽度为5像素,高度为4像素,即PAR=5 : 4。 假设图像的显示宽度为160,高度为120,即DAR=4 : 3。 那么可以计算出SAR = DAR / PAR = 16 : 15,表示像素方格是一个长方形。
dar

FFmpeg提供了多个SAR变量:

  • AVStream->sample_aspect_ratio
  • AVStream->codecpar->sample_aspect_ratio
  • AVFrame->sample_aspect_ratio

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);

如何构造特殊SAR的视频

FFmpeg提供了Video AVFilter:setdar, setsar,可以修改视频的SAR和DAR。 例如:origin.mp4是一个10801920的视频,默认SAR是1 : 1,DAR是9:16。 现在我们把SAR设置为2 : 1,即单个像素是2 : 1的矩形,那么视频尺寸就是5401920。

ffmpeg -i origin.mp4 -vf scale=540:1920,setsar=2.0 output.mp4

output.mp4的信息如下所示: setsar

兼容特殊SAR的视频

针对上述SAR是2:1的视频,若我们不处理SAR,直接按照视频帧宽高:540*1920来处理,那么正常的9:16视频,将会被显示成4.5:16,即整个视频上下拉伸了2倍。

兼容SAR也很简单,分为CPU和GPU处理(假设是YUV420P像素数据)。

CPU

通过libyuv函数(例如:libyuv::Scale),把5401920的像素数据,缩放到9 : 16的任意尺寸,例如:540960(宽度不变,按照DAR计算出高度)。

GPU

先按照540*1920的真实像素数据,上传到YUV纹理,然后按照9 : 16的原始比例计算MVP矩阵,即计算MVP矩阵时,源纹理不是按照4.5 : 16的宽高比,而是按照9 : 16的宽高比,最后渲染到目标FBO纹理。

旋转角度和SAR的问题

若一个视频既有旋转角度,又有SAR和DAR,那这里的SAR是旋转之前的像素宽高比还是旋转之后的像素宽高比那?或者说这里的DAR是旋转之前的显示高比还是旋转之后的显示宽高比那? sar-rotation

这个问题在网上查了蛮久,都没有找到解答,最后从EXOPlayer源码MediaCodecVideoRenderer.onOutputFormatChangedPlayerView.onVideoSizeChanged中找到了答案。

SAR是旋转之前的像素宽高比,DAR是旋转之前的显示宽高比,旋转之后,SAR和DAR都要取倒数。