H264 NAL 语意 (该文是转的)
在网络传输的环境下,编码器将每个NAL 各自独立、完整地放入一个分组,由于分组都有头部,解码器可以很方便地检测出NAL 的分界,依次取出NAL 进行解码。为了节省码流,H.264 没有另外在NAL 的头部设立表示起始的句法元素,我们从表7.1 可以看到这点。但是如果编码数据是储存在介质(如DVD 光盘)上,由于NAL 是依次紧密排列,解码器将无法在数据流中分辨每个NAL的起始和终止,所以必须要有另外的机制来解决这个问题。
针对这个问题,H.264 草案的附录B 中指明了一种简单又高效的方案。当数据流是存储在介质上时,在每个NAL 前添加起始码:0x000001,在某些类型的介质上,为了寻址的方便,要求数据流在长度上对齐,或必须是某个常数的倍数。考虑到这种情况,H.264 建议在起始码前添加若干字节的0 来填充,直到该NAL 的长度符合要求。
在这样的机制下,解码器在码流中检测起始码,作为一个NAL 的起始标识,当检测到下一个起始码时当前NAL 结束。H.264 规定当检测到0x000000 时也可以表征当前NAL 的结束,这是因为连着的三个字节的0 中的任何一个字节的0 要么属于起始码要么是起始码前面添加的0。
添加起始码是一个解决问题的很好的方法,但上面关于起始码的介绍还不完整,因为忽略了一个重要的问题:如果在NAL 内部出现了0x000001 或是0x000000 的序列怎么办?毫无疑问这种情况是致命的,解码器将把这些本来不是起始码的字节序列当作起始码,而错误地认为这里往后是一个新的NAL 的开始,进而造成解码数据的错位!而我们做的大量实验证明,NAL 内部经常会出现这样的字节序列。
于是H.264 提出了另外一种机制,叫做“防止竞争”,在编码器编码完一个NAL 时,应该检测是否出现下面左侧 中的四个字节序列,以防止它们和起始码竞争。如果检测到这些序列存在,编码器将在最后一个字节前插入一个新的字节:0x03,从而使它们变成 下面 右测的样子。当解码器在NAL 内部检测到有0x000003 的序列时,将把0x03 抛弃,恢复原始数据。
0x00 00 00 -------------------->0x00 00 03 00
0x00 00 01 -------------------->0x00 00 03 01
0x00 00 02 -------------------->0x00 00 03 02
0x00 00 03 -------------------->0x00 00 03 03
0x000001和0x000000在前文中已经提到,0x000002 是作保留用,而第四个0x000003是为了保证解码器能正常工作,因为我们刚才提到,解码器恢复原始数据的方法是检测到0x000003就抛弃其中的0x03,这样当出现原始数据为0x000003 时会破坏数据,所以必须也应该给这个序列插入0x03。