全部博文(113)
分类: 嵌入式
2011-02-21 17:39:42
本文讲解的H.264的马赛克问题是以T264为基础的。T264是国内的开源项目,官方网站为,目前和JM(H.264的官方源码)、X264(http://developers.videolan.org/x264.html)一起被称为H.264三大Codec。T264的程序做过汇编优化,速度还可以,但只能解T264本身的码流。
MPEG定义
根据MPEG的定义,帧分为I帧,B帧,P帧。这些帧定义如下:
I帧:就是关键帧,I帧压缩采用基准帧模式,只提供帧内压缩,即把帧图像压缩到I帧时,仅仅考虑了帧内的图像。I帧压缩不能除去帧间冗余度。帧内压缩基于离散余弦变换(DCT),类似于JPEG和H.261图像中使用DCT的压缩标准。I帧压缩可以得到6;1的压缩比而不产生任何可觉察的模糊现象。
B帧:为双向帧间编码。它从前面和后面的I帧或P帧中提取数据。B帧基于当前帧与前一帧和后一帧图像之间的差别进行压缩。B帧压缩可以达到200:1的压缩比,其文件尺寸一般为I帧压缩尺寸的15%,不到P帧压缩尺寸的一半。
P帧:采用了前向预测,意思是P帧是根据前面的P帧或I帧预测得来的。P帧采用预测编码,利用相邻帧的一般统计信息进行预测。也就是说,它考虑运动特性,提供帧间编码。P帧预测当前帧与前面最近的I帧或P帧的差别。
H.264补充
H.264中也有I B P帧,不同的是,H.264是以多幅图像为参考,因此可以获取更高的压缩比,以及更连贯的动作。但是这也造成H.264耗资源大,延时也略大于MPEG-4。
除了I B P帧外,H.264提出了SP和SI帧,这两种帧介于I和P之间。SP的编码原理和P类似,仍是基于帧间预测的运动补偿预测编码,两者之间的差异在于SP帧能够参照不同参考帧重构出相同的图像帧。充分利用这一特性,SP帧可以取代I帧,广泛应用于Bitstream Switching, Splicing, Random Access, Fast Forward Fast Backward以及Error Recovery等应用中。同时大大降低了码率的开销。SI则是基于帧内预测的编码技术,其重构图像的方法和SP完全相同。
SP帧的编码效率尽管略低于P,却远远高于I,大大改善了H.264的网络亲和性,支持灵活的流媒体服务应用,具有很强的抗误码性能,适于在噪声干扰大、丢包率高的无线信道中传输。
T264中帧的概念
T264中主要包括了IDR,I,P,B四种。
IDR:类似于H.264标准的I帧,在马赛克问题中起着至关重要的作用。在IDR之后的图像不会再以IDR之前的图像为参考,间断性的插入IDR,可以把视频分成段。
I:类似于H.264标准的SP帧,进行了帧间的预测。T264对I的取名让我们走了不少弯路。我们一直把I作为关键帧来处理,但是发现在网络丢包的情况下只播放I帧,也会有马赛克现象出现。而且前面的包丢的越多,I帧的马赛克现象越严重,这就表明I帧也和前面的帧作了帧内预测。对IDR帧的实验和研究才让我们走出误区,IDR才是真正的标准中的I帧。
P和B:这两个和标准中一致,和多幅图像做了帧间的预测。
目前T264支持的码流主要有如下几种:
0) IDR P P P …..P I P P P P … P I P P …
1) IDR B P B P…..B P B P I B P B P …
2) IDR B B P B B P…..B B P I B B P
在数据的刚开始总会有一个IDR帧出现,之后就不再有IDR。这样可以让数据量很小,但是解码端必须要收到第一个IDR帧,否则整个数据都将不能正确解码,这就导致我们实验时必须先启动解压端,然后再启动压缩端,以保证第一个IDR顺利到达。
后两种带有B的码流在目前的T264的源码中已经不再使用。B帧的加入虽然可以让数据量变小,但是也会造成CPU消耗过多,在丢包的情况下数据难以恢复的现象。我想T264只所以不采用后面两种码流,大概就是考虑这些问题。
目前T264采用的码流为第一种,对于本地文件压缩,这种码流非常适合,但是对于网络传输来说,这种码流有着重大的问题。因为网络传输时第一个IDR帧很可能不能被解压缩端所获取,这样就造成严重的误码和程序错误。对于这个问题,我的解决方案是定期投放IDR帧。由于过多的IDR会带来数据量变大,为此我们又要采取另外一种措施,就是尽可能放弃I帧,以此来减少由于IDR数量增加造成的数据量增大的问题。因此我们的码流如下所示:
IDR P P P …..P IDR P P P P … P IDR P P …
在确定好我们的码流后,我将对马赛克问题做详细的分析。目前我们采用了每10帧一个IDR的方式,为了更好的说明问题作出了如下图表:
码流 | IDR | P | P | P | P | P | P | P | P | P | IDR | P | P | P | P | P |
帧号 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
上表中1和11号帧为IDR帧,其余位P帧。假设第7帧丢失,如下图所示:
码流 | IDR | P | P | P | P | P | P | P | P | IDR | P | P | P | P | P | |
帧号 | 1 | 2 | 3 | 4 | 5 | 6 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
那么根据P帧的特性,第8帧就会参考前面的序列1-6来解码,由于第7帧和第6帧是有细微的区别的,这样第八帧率在解码的时候就会产生误码,也就会有轻微的马赛克现象。
当第9帧再参考第8帧解码时,由于8本身已经有了轻微的马赛克,9将会对马赛克放大。到了10马赛克更加严重。
如下表所示,如果丢失第3帧:
码流 | IDR | P | P | P | P | P | P | P | P | IDR | P | P | P | P | P | |
帧号 | 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
4、5、6、7、8、9、10将会依次对马赛克进行放大,这时的马赛克会比丢失第7帧更严重。
这也说明,在同一个IDR和P的序列中丢失的包越往前,马赛克越严重。直到下一个IDR帧,马赛克将会结束。
分析了马赛克出现的原理之后,我们提出了马赛克问题的解决方案。仍然拿上面丢失第7帧的例子来看。
码流 | IDR | P | P | P | P | P | P | P | P | IDR | P | P | P | P | P | |
帧号 | 1 | 2 | 3 | 4 | 5 | 6 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
我们可以考虑在丢失第7帧的情况下,同时也把该序列中在该帧后面的其他帧丢失,本例中,我们应该丢失8、9、10,等待下一个IDR帧的到来。
如果丢失IDR帧,我们会把IDR相关的帧序列都丢失,以防止误码的产生。
这种方式可以有效地解决马赛克现象,源节点发出去30fps的视频,在网络上丢包之后到接收端可能看到的只是20fps,或者10fps,但是不会有马赛克问题产生。
我们这种解决方式也有缺陷的地方,比如第7帧的丢失,造成8,9,10帧也跟着丢失,那么在播放视频时,1-6帧都播放的很流畅,到7-10就会停住,也就是会出现忽快忽慢的现象。但是由于互联网丢包的随机性,这种现象在互联网上可能不太严重。另外这种方式对接收到的帧的利用率不是很高,如果第3帧丢失,那么第4-10帧即使收到也要人为的丢失。以后我们可以考虑利用相关的帧对丢失的帧进行预测,重新生成丢失的帧,并补齐。但是这样的实现的难度很大,目前还没有一个技术去实现这个。还有一个办法就是8、9、10帧做去方块滤波处理,同时对边缘进行模糊处理,让8-10模糊的播放出来,而不至于出现马赛克和忽快忽慢的现象。
H.264的加入让我们的带宽有了1/3的节省,目前30fps 320*240的视频占据带宽大概为15kBytes/s,比MPEG-4的25kBytes/s以上有了很大提高。同时马赛克问题也得到了合适的解决。