H265

2,467 阅读3分钟

NALU

H265 NALU结构与H264基本类似,主要有两点差异:

  1. NALU Size分割的HVCC结构和StartCode分割的Annexb结构
  2. NALU Header占2字节。

下图是H265 NALU结构,因为NALU Header占两字节,所以从索引2开始处理NALU Payload。 NALU

NALU Header

H265 NALU Header占2字节,如下所示: NALU Header

  • 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的取值如下所示: nalu 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

根据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.cff_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。

参考文章

  1. HEVC深度解析
  2. HEVC NAL Unit Header 和 Bitstream 结构解析