NALU
H265 NALU结构与H264基本类似,主要有两点差异:
- NALU Size分割的HVCC结构和StartCode分割的Annexb结构
- NALU Header占2字节。
下图是H265 NALU结构,因为NALU Header占两字节,所以从索引2开始处理NALU Payload。
NALU Header
H265 NALU Header占2字节,如下所示:
- forbidden_zero_bit:1bit,默认为0,当传输过程中,发现NALU数据有错误时,可设置为1,以便接收方纠错或丢掉该单元。
- nal_unit_type:6bit,标识NALU类型,决定了如何解析NALU Payload,6bit可以表示64种不同类型,VCL和non-VCL NAL unit各使用32种类型。
- nuh_layer_id:6bit,标识NAL Unit所属的layer,支持未来扩展。虽然在2013年发布的HEVC的第一个版本中支持时间维度上的可扩展,但是它没有包含任何其他维度上的扩展性,所以这个版本所有的NAL units的layer ID总是会被设置为’000000’。
- nuh_temporal_id_plus1:3bit,其值减1为该NALU时域层标号,TemporalId = nuh_temporal_id_plus1 − 1,TemporalId表示NALU时域层级,根据TemporalId可以确定图像的重要性,配合nal_unit_type可以实现视频流的时域分级。
其中,nal_unit_type的取值如下所示:
H264通过code & 0X1F获取NALU Type,H265通过(code & 0x7E) >> 1获取NALU Type。
- 19, 20: IDR帧
- 32: VPS
- 33: SPS
- 34: PPS
- 39, 40: SEI
参考libavcodec/hevcdec.h的IS_IDR和IS_IRAP宏可知:[16, 23]之间的nalu type都是关键帧,其中19和20是IDR帧,即IDR帧一定是I帧,但是I帧则不一定是IDR帧。
NALU Payload
与H264类似,H265 NALU Payload = SODB + rbsp trailing bits + 防竞争码0x03,当解码时,需要反序去除防竞争码0x03和rbsp trailing bits。
Slice Type
H265 Slice Header中指定了slice type,表示I、P、B帧,取值如下所示:
根据slice type计算AVFrame->pict_type:
// hevc slice type
enum HEVCSliceType {
HEVC_SLICE_B = 0,
HEVC_SLICE_P = 1,
HEVC_SLICE_I = 2,
};
// AVFrame pict_type枚举值
enum AVPictureType {
AV_PICTURE_TYPE_NONE = 0, ///< Undefined
AV_PICTURE_TYPE_I, ///< Intra
AV_PICTURE_TYPE_P, ///< Predicted
AV_PICTURE_TYPE_B, ///< Bi-dir predicted
AV_PICTURE_TYPE_S, ///< S(GMC)-VOP MPEG-4
AV_PICTURE_TYPE_SI, ///< Switching Intra
AV_PICTURE_TYPE_SP, ///< Switching Predicted
AV_PICTURE_TYPE_BI, ///< BI type
};
// 根据slice type计算AVFrame->pict_type
AVFrame->pict_type = 3 - hevc_slice_type;
VPS
SPS
PPS
FFmpeg与HEVC
libavcodec/hevc_parse.h提供了ff_hevc_decode_extradata函数,解析hevc元数据,填充到HEVCParamSets结构体,主要是VPS、SPS和PPS。
该函数首先利用libavcodec/h2645_parse.c的ff_h2645_packet_split函数解析出所有NALU,然后通过libavcodec/hevc_ps.c对应函数去解析VPS、SPS和PPS的具体内容。
libavcodec/h2645_parse.c提供了ff_h2645_packet_split函数拆分出所有NALU单元。
libavcodec/hevc_ps.c提供了ff_hevc_decode_nal_vps函数解析VPS,ff_hevc_decode_nal_sps函数解析SPS,ff_hevc_decode_nal_pps函数解析PPS。
typedef struct HEVCParamSets {
AVBufferRef *vps_list[HEVC_MAX_VPS_COUNT];
AVBufferRef *sps_list[HEVC_MAX_SPS_COUNT];
AVBufferRef *pps_list[HEVC_MAX_PPS_COUNT];
/* currently active parameter sets */
const HEVCVPS *vps;
const HEVCSPS *sps;
const HEVCPPS *pps;
} HEVCParamSets;
libavcodec/hevc_parser.c是HEVC AVCodecParser。