NALU格式解析
NALU是压缩视频的基本单位,根据不同场景和传输机制NALU分为2种传输模式:分组流和字节流
分组流
分组流是基于RTP协议的方式,直接将NALU作为RTP分组的载荷部分。
字节流
字节流方式则是NALU按照解码顺序排列成字节流传输。由于NALU里没有NALU长度信息,所以如果NALU直接连接成字节流就无法区分不同的NALU,为了解决这个问题需要在每个NALU前添加起始码字段。在H.265标准的附录B中定义了相关规范。
NALU字节流生成过程:
- 在每个NALU前插入3字节起始码start_code_prefix_one_3bytes,其值为0x000001
- 如果NALU类型为VPS_NUT, SPS_NUT,PPS_NUT或者AU的第一个NALU,起始码前还要插入zero_byte,其值为0x00
- 在视频流的首个NALU的起始码(可能包含zero_byte)前插入leading_zero_8bits,其值为0x00。注意:leading_zero_8bits只能加在第一个NALU前,否则0x00后面跟上4字节0x00 00 00 01(zero_byte后跟上 leading_zero_8bits)会被认为是前一个NALU的trailing_zero_8bits
- 根据需要在每个NALU后面添加trailing_zero_8bits 作为填充数据,其值为0x00
字节流的语法格式如下表所示,通过该语法格式可以从字节流中提取NALU。可以看到通过查找起始码0x000001可以确定NALU的前边界,通过查找第一个0x00000001可以确定视频的前边界,通过查找0x00000001可以确定AU的前边界。
NALU
H.265中NALU由NALU header和NALU body两部分组成。
NALU Header
NALU头固定为2字节,其结构如下。
在ffmpeg中相关定义如下:
typedef struct H265RawNALUnitHeader {
uint8_t forbidden_zero_bit;
uint8_t nal_unit_type;
uint8_t nuh_layer_id;
uint8_t nuh_temporal_id_plus1;
} H265RawNALUnitHeader;
-
forbidden_zero_bit为1bit,其值应设置为0防止与MPEG-2起始码冲突。
-
nal_unit_type为6bit,取值范围为0-63,表示当前NALU的类型。
-
nuh_layer_id 为6bit,其值应为0。该字段保留给将来使用。
-
nuh_temporal_id_plus1 为3bit,其值减1为该NALU时域层标号。TemporalId = nuh_temporal_id_plus1 − 1
NALU的类型定义如下。
ffmpeg中关于NALU type的定义如下:
enum HEVCNALUnitType {
HEVC_NAL_TRAIL_N = 0,
HEVC_NAL_TRAIL_R = 1,
HEVC_NAL_TSA_N = 2,
HEVC_NAL_TSA_R = 3,
HEVC_NAL_STSA_N = 4,
HEVC_NAL_STSA_R = 5,
HEVC_NAL_RADL_N = 6,
HEVC_NAL_RADL_R = 7,
HEVC_NAL_RASL_N = 8,
HEVC_NAL_RASL_R = 9,
HEVC_NAL_VCL_N10 = 10,
HEVC_NAL_VCL_R11 = 11,
HEVC_NAL_VCL_N12 = 12,
HEVC_NAL_VCL_R13 = 13,
HEVC_NAL_VCL_N14 = 14,
HEVC_NAL_VCL_R15 = 15,
HEVC_NAL_BLA_W_LP = 16,
HEVC_NAL_BLA_W_RADL = 17,
HEVC_NAL_BLA_N_LP = 18,
HEVC_NAL_IDR_W_RADL = 19,
HEVC_NAL_IDR_N_LP = 20,
HEVC_NAL_CRA_NUT = 21,
HEVC_NAL_IRAP_VCL22 = 22,
HEVC_NAL_IRAP_VCL23 = 23,
HEVC_NAL_RSV_VCL24 = 24,
HEVC_NAL_RSV_VCL25 = 25,
HEVC_NAL_RSV_VCL26 = 26,
HEVC_NAL_RSV_VCL27 = 27,
HEVC_NAL_RSV_VCL28 = 28,
HEVC_NAL_RSV_VCL29 = 29,
HEVC_NAL_RSV_VCL30 = 30,
HEVC_NAL_RSV_VCL31 = 31,
HEVC_NAL_VPS = 32,
HEVC_NAL_SPS = 33,
HEVC_NAL_PPS = 34,
HEVC_NAL_AUD = 35,
HEVC_NAL_EOS_NUT = 36,
HEVC_NAL_EOB_NUT = 37,
HEVC_NAL_FD_NUT = 38,
HEVC_NAL_SEI_PREFIX = 39,
HEVC_NAL_SEI_SUFFIX = 40,
HEVC_NAL_RSV_NVCL41 = 41,
HEVC_NAL_RSV_NVCL42 = 42,
HEVC_NAL_RSV_NVCL43 = 43,
HEVC_NAL_RSV_NVCL44 = 44,
HEVC_NAL_RSV_NVCL45 = 45,
HEVC_NAL_RSV_NVCL46 = 46,
HEVC_NAL_RSV_NVCL47 = 47,
HEVC_NAL_UNSPEC48 = 48,
HEVC_NAL_UNSPEC49 = 49,
HEVC_NAL_UNSPEC50 = 50,
HEVC_NAL_UNSPEC51 = 51,
HEVC_NAL_UNSPEC52 = 52,
HEVC_NAL_UNSPEC53 = 53,
HEVC_NAL_UNSPEC54 = 54,
HEVC_NAL_UNSPEC55 = 55,
HEVC_NAL_UNSPEC56 = 56,
HEVC_NAL_UNSPEC57 = 57,
HEVC_NAL_UNSPEC58 = 58,
HEVC_NAL_UNSPEC59 = 59,
HEVC_NAL_UNSPEC60 = 60,
HEVC_NAL_UNSPEC61 = 61,
HEVC_NAL_UNSPEC62 = 62,
HEVC_NAL_UNSPEC63 = 63,
};
NALU Body
由于NALU的长度必须是整数字节,所以生成NALU的比特流通常需要填充。视频编码生成的压缩比特流片段称为SODB(String of Data Bits),SODB可能不是正好是整数字节,需要在其后填充比特变成整字节,填充后的比特流称为原始字节载荷序列(Raw Byte Sequence Payload,RBSP)。
SODB生成RBSP过程如下:
- RBSP第1字节取SODB最左端8比特,第2字节取接下来8比特,以此类推直到SODB剩余内容不足8比特。
- RBSP下一字节首先包含SODB最后几个比特,然后添加比特1,如果该字节还不满8比特后面填充0。
- 后面可能加入若干16比特的cabac_zero_word作为填充比特,其值为0x00 00。
RBSP还不能直接作为NALU Body,因为RBSP中可能含有0x00 00 01,与起始码冲突,必须先进行冲突避免处理。
其中0x00 00 02是预留码。
关于NALU更详细的内容可以参考H.265相关标准文档。
感兴趣的可以关注微信公众号Video Coding
上一篇: 环路滤波(二):HEVC去方块滤波
下一篇: 教你了解主板BIOS启动项中的设备