本文将介绍的是:
修改记录2019年4月8日首次完成该文章,内容包括:
2019年9月7日进行二次修改,修改内容如下:
第二章增加了YUV采样和存储格式的示意图,场、1080p和1080i的介绍;删除了YUV的颜色值域(ColorRange)介绍;
删除了原第三章的内容,这块会在后续中出专文介绍。
问题1引申:对要做视音频处理的开发们来说,接触MP4、MKV、AVI等各种格式视音频文件时,有什么需要注意的吗?
视音频处理可以延展出很多领域,包括解码、编码、过滤、增强处理等等。笔者目前只在解码领域探索,答案是:对于解码而言,没有区别。其他领域暂不清楚。
经过压缩算法压缩的流数据,称为『编码流』,又因为目前压缩/编码算法以H264为主,因此也常常称为『H264码流』。
编码/压缩在流媒体领域是一项非常重要的技术:从『H264码流』到『YUV流』的过程称为解码,反之称为编码。
II.帧问题3:帧为什么采用『YUV』格式?『YUV』是什么?
为此,笔者曾经花了很久去了解颜色空间、电视成像的发展史等,整理结论如下:
颜色空间
“Y”表示明亮度(Luminance、Luma),“U”和“V”则是色度(Chrominance)、浓度(Chroma)。这里表示的是色彩空间的基,即类似XYZ坐标系的一种色标表示基准,也就是说每一种颜色可以通过三维向量y^i^,u^i^,v^i^来表示。与其类似的还有RGB颜色空间、HSV颜色空间等。下图来自HowdoestheYUVcolorcodingwork?

图1.YUV坐标轴示意图
YCbCr是专门针对数字电路而诞生的;YPbPr则是模拟电路。但是,现在是数字时代,所以为了模拟电路而生的YPbPr已经逐渐被淘汰了,而YCbCr还一直发挥着作用。因此现在,YCbCr有时也会被简单地称为/认为『YUV』。
2.采样率
读者可能听说过“YUV444”,“YUV422”,“YUV420”,到这里可能会纳闷:“YUV不是颜色空间吗?为什么后面还会跟着一串数字?”因为当你看到YUV后面跟着一串数字的时候,『YUV』已经不再是颜色空间的基本含义了,而是意味着在原始『YUV流』上的采样。
在以前流媒体刚刚兴起时,还没有什么4G/5G,当时为了减小网络传输的带宽的压力,可谓做了种种努力。除了编码/压缩之外,YUV采样率也是一种。
444,422和420是三种『YUV』(在数字电路中指代YCbCr)的采样,三位数分别代表Y\U\V(数字电路中为Y\Cb\Cr,本段后同)通道的抽样比。所以可以理解,444是全采样;而422是对Y进行全采样,对U\V分别进行1/2均匀采样。有趣的问题来了,420难道是完全丢弃了V通道/分量数据嘛?答案是否定的。
首先,必须要搞明白一个问题,一帧图像是由一个个像素组成的矩形,譬如4x4的尺寸的图像,就是由16个像素点组成的。在平时接触的『RGB』图像中,每个像素必然至少由R\G\B这三个通道组成的(有的图像还有\alpha分量),每个分量的取值一般都是[0,255],也就是[2^0,2^8],因此经常说一个像素占用3字节(如果还有其他分量,比如RGBA,就另当别论)。『YUV』图像同理,它的每个像素是由Y\U\V组成的。
接下来,从整张图像宏观考虑采样问题。还是以4X4的图像为例,444的如下图2-1,这个是形象化成图像的样子,实际在机器内存储并不是这样,具体可以参见博客《图像原始格式一探究竟》。422和420分别如下图2-2和2-3。

图2-1.YUV444采样示意图
图2-1对应YUV444采样,即全采样,图示中可以看出每个像素中的Y\U\V通道都保留下来了,一般来说YUV444太大了,因此很少使用。

图2-2.YUV422采样示意图
图2-2对应YUV422采样,这种采样方式是:每个扫描线或者说每行相邻2个像素,只取1个像素的U\V分量。此外,可以计算出来,每个像素占用的大小为原来的2/3,因此说YUV422是YUV444的2/3大小。
这个时候就有一个问题,在『YUV』转『RGB』时,被抽走了U\V分量的像素要怎么办呢?做法很简单,就是相邻2个像素的Y分量公用保留着的U\V分量。

图2-2.YUV422采样示意图
图2-3对应YUV420采样,这种采样方式是:隔行进行YUV422每行采样的办法,即相邻2个像素只取1个像素的U\V分量;下一行丢弃所有的U\V分量。此外,可以计算出来,每个像素占用的大小为原来的1/2,因此说YUV420是YUV444的1/2大小。恢复U\V分量的办法同YUV422,只不过这里是2X2的矩阵共享保留着的U\V分量。
这种设计办法真的很巧妙!前文提到的"人眼对UV的敏感度最低,因此可以极大比例地压缩UV两个通道的数值",且对于图像而言,相邻的区域像素的色彩、饱和度一般非常接近,因此这种以2X2矩阵为基本单位,只保留1组U\V分量合情合理。
3.编码/存储格式
大家肯定还听说过YV12、YU12、NV12、NV21吧,看到这里是不是又纳闷:“后面的数字怎么变成2个了?而且前面的英文字母还变了?”
『打包格式』是把Y\U\V分量交叉存储,『平面格式』则是把Y\U\V严格分开存储,『半平面模式』介于两者之间,Y分量分开存储,U\V交叉存储。
以下图为例说明『打包格式』、『平面格式』和『半平面模式』应该是非常清楚的,图摘自博客YUV格式初探:

图3-1.YUV420P存储示意图

图3-2.YUV420SP存储示意图
图3-3.YUV420Packet存储示意图
但是关于上图的『打包格式』,笔者是是有一点疑惑的,大多数的说法是”Y\U\V通道交叉存储,相邻的像素尽量打包在一起“,图3-3中U1后面跟着的是U2而不是V1,而且Y\U\V的排列方式似乎也不完全是交叉?笔者尝试在网上搜索『打包格式』更多的例子,没有找到特别好的资料,【这里给自己挖一个坑吧】。
接下来,我们继续了解一些帧相关的概念。
常见的帧名词
帧率(FPS)
『帧率』,FPS,全称FramesPerSecond。指每秒传输的帧数,或者每秒显示的帧数,一般来说,『帧率』影响画面流畅度,且成正比:帧率越大,画面越流畅;帧率越小,画面越有跳动感。一个较权威的说法:
因此,才有说法:尽管『帧率』越高越流畅,但在很多实际应用场景中24fps就可以了。
分辨率(Resolution)
『分辨率』,也常被俗称为『图像的尺寸』或者『图像的大小』。指一帧图像包含的像素的多少,常见有1280x720(720P),1920X1080(1080P)等规格。『分辨率』影响图像大小,且与之成正比:『分辨率』越高,图像越大;反之,图像越小。
码率(BPS)
『帧率』『分辨率』和『码率』三者之间的关系
最理想的情况是画面越清晰、越流畅是最好的。但在实际应用中,还需要结合硬件的处理能力、实际带宽条件选择。高『帧率』高『分辨率』,也就意味着高『码率』,也意味着需要高带宽和强大的硬件能力进行编解码和图像处理。所以『帧率』和『分辨率』应该视情况而定。
要说三者之间的关系,其实就是对于『码率』的理解。在码率(BPS)概念中提到了几段摘自网上的说法,说的都太模糊了,笔者直到阅读了文章,才真的理解了『码率』。
首先,这些说法都没有交代一个前提:『帧率』、『分辨率』和『压缩率』都会影响『码率』。]()文章在一开始就明确指出:
奇怪的帧名词:1080p和1080i、场
笔者仅仅出于觉得有趣才放上来的,1080p和1080i、场都是相对比较“老”的概念了,在还是CRT电视的时代,显示器显示画面都是靠电子枪一行一行扫描画面才能产生一副完整的图像,这就被称作『场』,后来这个名词也不常使用了,被取代它的是『帧』。【科技在进步,过时的概念、应用都会被新兴的替换,所以真的要不断学习紧跟时代啊!】
1080p和1080i也是『场』同一时期的概念:
${数字}i的字母”i“表示Interlace,代表隔行扫描,比如奇数『场』只扫描奇数行,后一『场』即偶数『场』只扫描偶数行。这在过去是非常有用的,当时网络条件差,带宽受限,隔行扫描可以很大程度上减少传输的数据,又不至于影响观众观看体验。
${数字}p的字母”p“表示Progressive,即逐行扫描,也就是一『场』把全部画面扫描完整。这是后来才提出的概念,这也代表时代进步,带宽条件上来了。
既然都是老概念了,那为什么还要再提呢?借用文章1080P和1080i是什么意思?第一段来说:
I帧、P帧、B帧和IDR帧
I帧,英文全写IntraPicture,又称帧内编码帧,俗称关键帧。一般来说I帧不需要依赖前后帧信息,可独立进行解码。有数据表明,仅I帧的压缩率,可以达到7,这里其实可以把I帧的压缩等同于单独压缩一幅图片。至于说I帧的压缩只压缩了空间上的冗余信息,放在后续编码相关的系列文章中会详述。【这里再挖一个坑,免得自己忘记了】
P帧,英文全写predictive-frame,又称前向预测编码帧,也有帧间预测编码帧。顾名思义,P帧需要依赖前面的I帧或者P帧才能进行编解码,因为一般来说,P帧存储的是当前帧画面与前一帧(前一帧可能是I帧也可能是P帧)的差别,较专业的说法是压缩了时间冗余信息,或者说提取了运动特性。P帧的压缩率约在20左右,几乎所有的H264编码流都带有大量的P帧。
IDR帧,英文全写InstantaneousDecodingRefresh,翻译过来是即时解码刷新。听上去,这类帧并不是名词概念,倒像是个动词?IDR帧是一种特殊的I帧,它是为了服务于编解码而提出的概念,IDR帧的作用是立刻刷新,使错误不致传播,从IDR帧开始,重新算一个新的序列开始编码(摘自博客H264中I帧和IDR帧的区别)。
GOP
PTS、DTS
DTS,英文全称DecodingTimeStamp,即解码时间戳,这个时间戳的意义在于告诉解码器该在什么时候解码这一帧的数据。
PTS,英文全称PresentationTimeStamp,即显示时间戳,这个时间戳用来告诉播放器该在什么时候显示这一帧的数据。
写在后面尽管每个概念网上都可以搜到一大堆的资料,但是笔者从一个多媒体开发小白走过来,觉得能有相对系统入门的综合性介绍就会更好了!本文每个地方,都是基于笔者自己的理解,而不是简单地从网上“复制粘贴”过来的,希望能够对大家有所帮助!当然,文章中有不严谨的地方,欢迎留言告知;或者有什么有趣的话题探讨,也欢迎私信留言!





