2020-10-14

码率显示不准问题的解决方法

FPS是图像领域中的定义,是指画面每秒传输帧数,通俗来讲就是指动画或视频的画面数。FPS是测量用于保存、显示动态视频的信息数量。每秒钟帧数越多,所显示的动作就会越流畅。

视频码率就是数据传输时单位时间传送的数据位数,一般我们用的单位是kbps即千位每秒。通俗一点的理解就是取样率,单位时间内取样率越大,精度就越高,处理出来的文件就越接近原始文件。

1.1 逐行扫描

逐行扫描应用广泛,在我们目前的各类电脑显示屏、外接的显示器上,使用的都是逐行扫描。逐行扫描非常易于理解,它就是在光栅图像下,从图像的第一行顺序扫描到最后一行,也即下图:

2020-10-14
原始图像(左) 和 逐行扫描帧(右)

逐行扫描的结果,就是产生完整的一帧图像,因此它进行的通常是帧编码。逐行扫描虽然现在应用广泛,但是在最初电视机刚刚兴起,还没有电脑的时代,逐行扫描确实是不实用的。主要有以下两点原因:

  • (1)逐行扫描产生的完整的一帧图像,占据的电视信号的带宽较大
  • (2)在【彩色电视制】中我们知道,PAL制的帧频为25帧每秒,NTSC的为30帧每秒。在这种情况下,如果采用逐行扫描的方式,人眼是会察觉到闪烁的。

因此聪明的人们想到,可以将一帧图像先扫描奇数场显示在屏幕上,然后再扫描偶数场。这样在相同的时间内,PAL制每秒可显示50场,NTSC可显示60场,这样既降低了带宽,又解决了闪烁的问题,隔行扫描也就这样产生了。

1.2 隔行扫描

就像刚才说的那样,隔行扫描是先扫描奇数场,然后扫描偶数场,也即顶场和底场。如下图:

2020-10-14
隔行扫描的经典之处,就在这个先后上!

可以想象,当从第1行隔行扫到15行,扫描完顶场后,又重头开始扫底场,也即回到第2行开始扫,这中间是有个时间差的。这个时间差,如果站在摄像机采集图像的立场来理解,那么就是接下来理解隔行扫描下的视频编码方式的关键。

上面我们说,逐行扫描一般进行的是帧编码,而隔行扫描,则可以在帧编码和场编码之间做选择,那选择的依据是什么呢?接着往下看。

2、帧编码和场编码

现在我们就要回到,摄像机采集图像到编码器的阶段了。我们已经知道,摄像机采集图像的扫描方式有两种,但是因为隔行扫描的出现,它们的编码方式却不只两种。我们先来介绍最容易想到的情况,那就是针对一部视频,全部使用帧编码或全部使用场编码。

一般而言,对逐行扫描的视频全部进行帧编码。对隔行扫描的视频,就要选择是使用帧编码还是场编码,注意这里是只选择一次,然后整个视频序列都采用帧编码或场编码。如果隔行扫描采用帧编码,则是将顶场和底场合为一帧进行编码。

此时对于帧编码,在做帧间预测时,参考图像为帧图像,运动补偿也为帧运动补偿。对于场编码,则是两个场分别进行编码,在做帧间预测时,参考图像为场图像,运动补偿也为场运动补偿。

以目前二者应用的场合来看:

帧编码应用广泛,因为目前我们无论在手机端录的视频、还是在电脑端、显示器上播放的视频,基本都是逐行扫描。因此在我们这个系列教程里,很有可能并不会涉及到场编码。

由隔行扫描引出的场编码,多应用在电视业,所以对于从事互联网的人士来说,几乎碰不到场编码的情况。

不过即使如此,我们还是要继续介绍一下隔行扫描的编码方式,因为这样便于我们接下来理解H264中的几个重要的句法元素。这里我们介绍隔行扫描的前提,是整个视频序列只采用一种编码方式。

事实上,在整个视频编码中,编码方式的选择可以更灵活,这从我们上篇解析到的片层Slice_Header的field_pic_flag和bottom_field_flag就可以体会到。

3、 PAFF和MBAFF

【注】(Picture adaptive field-frame 和 Macro-block adaptive field-frame)

第2小节中,我们说的是整个视频序列只采用一种编码方式。但实际上对于隔行扫描的视频,我们还可以更灵活,通常可以分为以下三种编码方式:

  • (1)将两个场合为一帧进行帧编码
  • (2)两个场分别进行场编码
  • (3)将两个场合为一帧,但是在宏块级别上,两个上下相邻的场宏块组合成一个宏块对(MB pair,MBP),这种编码方式也称宏块级的帧场自适应,也即MBAFF,这是H264引入的一个新的编码特性。

其中前两点,不同于第2节的是,第1点和第2点是可以进行自适应切换的。也即在一个视频序列中,通过对编码效果的不同,选择是使用帧编码还是场编码,因此这种方式也称图像级的帧场自适应(PAFF)

这就是PAFF和MBAFF,下面我们更加细致一点来说明,为什么有了PAFF还需要MBAFF。

3.1 使用PAFF进行编码

在隔行扫描中,一个视频序列的运动图像部分,和非运动图像部分的编码,区别是很大的。

在开始时我们就说过,摄像机在进行隔行扫描时,顶场和底场的扫描时间上,是有一个时间差的。这个时间差是什么呢?就是当摄像机扫描完顶场的最后一行后,又回过头来扫描底场的第一行。

这样就导致,如果将顶场和底场合为一帧,那么顶场的第一行和底场的第一行就变成了相邻行,而实际上相邻行的扫描时间,整整差了一个场的扫描时间。

这对于非运动图像没什么,但是对于一个正在运动的图像而言,会导致合为一帧后,相邻两行的空间相关性比较低。所以对于运动的图像,两个场分别进行编码,相对合为一帧来编码更加节省码流。

对于非运动图像,两个场合为一帧后,空间相关性也很大,因此合为一帧进行帧编码更加节省码流。

这就是PAFF的工作方式,它会根据编码效果,调整整个图像的编码方式。

但这同时也变成了它的缺点,因为调整整个图像的编码方式,是个粒度非常粗的工作方式。对于视频序列而言,可能某些图像,同时存在运动区域和非运动区域,这时PAFF的缺点就很明显了,对这些图像使用帧编码或场编码似乎都不恰当,这时MBAFF就产生了。

3.2 使用MBAFF进行编码

MBAFF将两个场合为一帧,但是在宏块级别上,使用宏块对(MBAFF)为基本编码单元。这样在图像的运动区域,采用场编码,对于非运动区域,采用帧编码,比较两种方式产生的码流大小,就能在宏块级别选择更节省码流的编码方式。

4、H264中的帧场编码模式

前面三节,都是为这一节做铺垫。我们已经知道了,编码方式有帧编码、场编码、图像级的帧场自适应编码和宏块级的帧场自适应编码,看起来真的挺麻烦,那是因为我们没确定我们的立场。

当我们把立场放在Slice上,一个Slice的编码方式就只有三种:帧编码、场编码或MBAFF。

这就需要用到几个句法元素,分别为:SPS中的frame_mbs_only_flagmb_adaptive_frame_field_flag,Slice_Header中的field_pic_flagbottom_field_flag。具体流程如下:

2020-10-14
H264帧场编码模式判断

判断出当前Slice的编码方式后,如果是宏块级的帧场自适应,通常用字段MbaffFrameFlag等于1来表示,这也是我们在理解Slice_Header中first_mb_in_slice的语义时需要用到的字段。