1 引言
H.264 视频压缩标准具有很高的压缩效率和很好的网络支持, 非常适应于无线多媒体和基于Internet 的应用。在信息传输的过程中,不可避免的就是噪声。H.264 本身具有很好的抗噪声技术,如SPS,PPS 和图像数据的分开打包, 一帧中可有多个Slice,灵活宏块顺序(FMO)等。但是也有很多情况错误无法被完全修复。H.264 具有很高的压缩率(7~50 倍),这就意味着图像中的冗余已经被大大消除,要从其余的码流中恢复图像有很大困难。此外,H.264 压缩标准采用变长编码的CAVLC 和CABAC,一旦在这些地方出错, 解码器将无法判断下一个变长码开始的位置,导致错误的扩散。
在硬件方面,解码过程中各部分的实现都有其固定的结构,如果不及时检出错误,会导致如内存溢出、查表错误、状态机进入死循环等。解码出的图像会错位或者变形, 更严重的时候整个解码器会停止运行。
所以在解码器中加入纠错功能,及早地发现码流噪声并将解码器恢复到正常的状态是非常有意义而且非常必要的。
本项目的解码器是基于SoC 的ASIC 解决方案,它具有高速、低功耗、低成本的特点。添加纠错功能可以提高解码器的适用性和稳定性,另一方面,应该尽量减少纠错模块对原有芯片的面积和速度的影响。
2 码流结构和错误检出
H.264 标准压缩的码流具有严格的格式。公共信息被提取出来作为SPS(序列参数集), PPS(图像参数集)单独打包,与像素信息分离。SPS 和PPS 是码流中极为重要的信息,将它们单独打包可以在传输环境较差的情况下多次传送,这在一定程度上增强了码流传输的抗干扰能力。每个包被称作一个NAL 单元,根据NAL 类型,各个数据包中的数据按照协议中规定的句法元素顺序紧密排列。这为纠错提供了两点便利:(1)各个NAL 中的错误不会蔓延,即如果当前NAL 中检测出了错误而且又无法确定错误终止的地方,那么可以将当前包丢弃,直到下一个NAL 开始;(2)很多句法元素都有其固定的范围。检测解码出的元素值是否超出范围限制是一个非常有用的办法,也能尽早发现错误,避免解码器进入异常状态。
H.264 视频码流包括五个层次的信息: 序列层,图像层,片层,宏块层,子宏块层,分别与NAL 类型的SPS,PPS,Slice(包括片头,宏块,子宏块信息)相对应。
宏块是编解码处理的基本单位,也是进行错误修补的基本单位。H.264 视频码流结构如图1 所示。
图1 H.264 码流结构
在上面的几个层次中,每一层都具有其特定的句法元素结构,以及相应句法的处理办法,整个错误检测功能就是基于此实现的。目前可以检测到的错误基于以下几类:
(1)保留位错误。这是最容易检测出的错误。在H.264 协议中规定了一些保留位, 如NAL 起始的forbidden_zero_bit,SPS 中的reserved_zero_4bits 等。解码器顺序读入码流时要确定这些保留位的值是正确的,如果有误,就应该丢弃当前的包或者标记错误,做进一步的处理。
(2)句法元素值不在指定范围。大多数的句法元素,尤其是在SPS 和PPS,都有一个指定的范围。如用于计算最大帧数的log2_max_frame_num_minus4 必须在0~12 之间,如果读入的值超出这个范围,即可以判断当前读入的码流有错误。
(3)相关句法元素矛盾。有一些句法元素读入了一个错误的值,但仍然在正确的范围内,单单通过当前语句是无法检测出的。这时候就要利用句法元素之间的相关性,判断读出的值是否合理。例如PPS 中要指定当前图像所引用的序列参数信息, 即PPS->seq_parAMEter_set_id。如果当前引用的SPS 的ID 在所有收到的SPS 中不存在, 即可判断SPS 或者当前PPS 有一方出了问题; 对于slice_type 的I,B,P 各种类型,mb_type 的查表方法也不同, 如果在I slice 中出现了帧间预测的宏块类型,也可以判断出错。
以上方法都是可以通过句法元素本身的值判断出来的,可以在读入码流的同时处理,即在处理PPS、SPS 的相应语句后加入判断,不会对原有解码器造成太大的影响。
并非所有的码流错误都能直接通过句法元素的值判断出来。一些句法元素的值会影响解码方法并且被重复使用,所以有些错误是可以在解码过程中发现的,例如:
(4)引用的参考不存在。H.264 实现压缩的途径之一就是采用帧内和帧间预测,如采用了4 种16×16帧内预测模式和9 种4×4 帧内预测模式。各种模式对周边宏块的要求都有所不同,16×16 块的水平帧内预测模式所需要的相邻宏块信息如图2 所示。若当前宏块X 采用水平帧内预测,那么宏块A 必须可用。如果当前宏块位于一行的第一个,则说明这种预测方式是错误的。而在帧间预测时,要指定参考块所在参考图像的编号和位置,如果不存在当前编号所指向的参考帧或参考块的位置超出了图像范围,就说明当前的引用有误。由于宏块信息采用变长编码且没有特定符号分割,一旦发现错误,其后的数据直到下一个NAL 开始都应被丢弃。
图2 亮度分量Intra 16×16 水平预测模式
(5)查表无对应值。CAVLC 和CABAC 编码的数据由于其码长不定很难分隔出有错的数据,但是解码这些数据中包含大量的查表操作,非常有利于及早检出错误。
(6)其它异常情况。如参考队列中出现空缺,这时候只能判断前面某一帧或几帧出现了内存管理错误,管理使能句法元素daptive_ref_pic_marking_mode_flag被错误地置1,或是具体的操作类型错误等。这种情况无法在解码句法元素的时候立即判断出错误(值在正常范围内且队列没有出现异常), 虽然在人工调试的时候可以根据后面往队列中插入参考帧的情况检查出来, 但是对于实时解码器来说这样是没有意义的。这时候不需要放弃当前NAL 中的数据,只要将临近参考帧的信息复制到参考队列里即可。
以上是检错方法的大致概括。由于噪声是随机的,错误可能出现在解码过程的任何一个地方,所以只有通过调试大量码流才能达到一定的错误覆盖率,使解码器具有更好的适应能力。
3 软硬件协同系统中的纠错实现
尽管在H.264 的官方参考软件JM 中给出了较为完善的错误修补办法,但是考虑到尽量减少纠错部分对原有硬件的影响,我们采用基于帧内16×16 预测的修补办法, 其原理与解码帧内16×16 预测的方法相似。如果发现从某一宏块开始出现错误,解码器将判断周边宏块的存在和预测情况,为当前的宏块选择一个最佳的预测模式,通过周边宏块边界上的像素值修补当前宏块及其后的宏块直到当前Slice 结束。
本解码器采用软硬件协同的SoC 实现方案。在功能划分上,SPS / PPS / Slice 头的解析等分支较多的工作由灵活度较高的软件部分实现,熵解码和宏块预测等需要大量复杂运算的工作由硬件模块实现。硬件部分被分成前端和后端两个部分,前端部分包括熵解码单元,IQ / IDCT,后端包括运动补偿(MC)和滤波模块。
CPU 与各个模块、模块之间采用wishbONe 总线通信,前后端处理单元之间还有另外一条数据通道,以分担wishbone 总线的开销,其结构如图3 所示。CPU 对输入的码流做初步处理,提取出诸如图像大小、帧类型等信息,然后将处理后的数据送入硬件的前端处理部分,前端处理的输出被送入运动补偿模块恢复出像素信息并去除块效应。
错误检测是由软硬件共同完成的。软件[5]在解析SPS、PPS、Slice head 的同时判断解出的各个句法元素值是否合理,如果存在错误则通过总线向硬件发送信号HasErr_soft。例如,在解析PPS 的函数中,解码无符号指数哥伦布(ue) 得到用来表示当前PPS 所调用SPS ID 的句法元素seq_parameter_set_id, 然后判断解码器是否收到过此ID 标号的SPS, 如果无此SPS,则中断当前PPS 的解码,返回上一级函数。当前PPS的参数内容由其它PPS 复制得到。即在软件部分做以下修改:
read_new_slice() / / 读入一个NAL 单元
{
…
switch (nalu->nal_unit_type) / / 判断NAL 类型
{
…
case NALU_TYPE_PPS:
ProcessPPS(nalu); / / PPS 解析
break;
…
}
}
ProcessPPS(NALU_t 觹nalu)
{
…
pps->seq_parameter_set_id =ue_v(…);
/ / 读入当前PPS 所对应的SPS_id
if (pps->seq_parameter_set_id invalid)
/ / 若读入的SPS_id 不可用
… / / 复制前一个PPS 的内容
HasErr=1; / / 错误标志位置1
/ / (将通过总线发送信号给硬件)
return;
}
硬件主要负责宏块层和子宏块层的解码,能够发现预测模式、熵解码的错误。如果硬件解码过程中检测出错误,硬件会向软件发送HasErr_hw 信号。例如在帧内预测时,需要判断相邻宏块的可用情况。在读帧内预测模式的语句后加入以下判断:
整个纠错过程由软件控制,修补过程由软件直接发送数据给硬件后端。硬件检测出错误并向软件传递HasErr 信号,软件会做相应的数据处理,向硬件发送Err_Processing 信号, 并根据错误修补方案代替前端数据处理部分为解码器后端提供数据,如图3 中虚线所示。
图3 H.264 解码器结构图
在修补过程中, 由于采用基于空间连续性的预测,所以可以使用原有硬件中的帧内预测部分。但是,出错后原有的一些信号需要做相应的处理,否则控制部分的一些状态机会进入异常状态导致硬件无法继续正常运行。
4 实验结果
本实验先在PC 机上对C 模型修改和完善,硬件测试平台则采用Xilinx Virtex4 系列的开发板。解码器软件部分使用C 语言编写,Linux 下gcc 编译,生成的二进制文件运行于基于开放核OPENRISC1200 的CPU;硬件部分使用Verilog HDL 描述。测试所用输入码流为ITU-T 提供的参考序列[2],加入11 个固定噪声样本。实验证明解码器能顺利解码的噪声码流比例有了很大提高,图像播放流畅,很多受损的地方得到了较好的修补,但是也有一些能看出明显的修补痕迹。
5 结论
在解码器中加入错误检查和修补功能,提高了解码器处理受损码流的能力,解码器因此可以在更复杂的环境中使用,稳定性也有所提高。H.264 视频压缩标准本身具有一些支持错误修补的技术,比如可以多次重复传输SPS 和PPS,多个Slice,FMO 等。多次传输SPS 和PPS 以降低传输效率为代价获得接收端更高的准确率, 这在某些环境中是非常必要的。多个Slice 和FMO 在一定程度上增加了编码和解码的复杂度,但是它使得更多的周边宏块信息可以用于受损宏块的预测和修补。此外,就修补方式而言,也有基于帧间相关性和其它帧内预测插值方法的研究。本实验的主要目标是使解码器能顺利解码并且尽量重复利用已有的硬件单元,减少由于添加纠错功能带来的硬件面积增加。采用16×16 帧内预测的方法修补,图像质量相对于未修补时有了很大提高,但是有些地方还是有比较明显的修补痕迹。寻找更好的图像修补办法,使修补后的图像尽量接近原始图像,减少可以明显分辨的修补痕迹,这些是需要继续研究的内容。