HEVC/H265帧类型判断及NALU TYPE介绍

1.H265-NALU-Type介绍

NAL_TRAIL_N = 0, 
NAL_TRAIL_R = 1, 
NAL_TSA_N = 2, 
NAL_TSA_R = 3, 
NAL_STSA_N = 4, 
NAL_STSA_R = 5, 
NAL_RADL_N = 6, 
NAL_RADL_R = 7, 
NAL_RASL_N = 8, 
NAL_RASL_R = 9, 
NAL_BLA_W_LP = 16, 
NAL_BLA_W_RADL = 17, 
NAL_BLA_N_LP = 18, 
NAL_IDR_W_RADL = 19, 
NAL_IDR_N_LP = 20, 
NAL_CRA_NUT = 21, 
NAL_VPS = 32, 
NAL_SPS = 33, 
NAL_PPS = 34, 
NAL_AUD = 35, 
NAL_EOS_NUT = 36, 
NAL_EOB_NUT = 37, 
NAL_FD_NUT = 38, 
NAL_SEI_PREFIX = 39, 
NAL_SEI_SUFFIX = 40,

 

2.H264和H265获取帧类型/NALU类型的区别


H264获取方式为 code&0x1f 取低5位,如00 00 00 01 65 则65&0x1f=5表示IDR帧
H265获取方式为 (code & 0x7E)>>1 , 如00 00 00 01 40 则(40&0x7e)>>1 = 32--vps

3.H265中帧类型的介绍GOP等


细节可查看此博客:

http://www.cnblogs.com/DwyaneTalk/p/5711430.html
H265和H264的不同有H265引入了多种方式的帧编码方式,其次H265的GOP不像H264那种为封闭式,H265是封闭和开放式都支持;
H264封闭式GOP: H264中单个GOP内部帧解码不会依赖其它GOP,即每个GOP都可独立解码,关键点就是IDR帧的强制刷新起了阻隔作用,避免错误帧相互影响,所以H264中GOP开始的第一帧必须为IDR关键帧,IDR为I帧的一种采用帧内编码无参考方式,但I帧不一定是IDR帧,普通I帧会参考前边的IDR,在RTC中,用的最多的就是IDR关键帧。

  •  关于GOP。

          这是图像组(Group of Pictures)的意思,表示编码的视频序列分成了一组一组的有序的帧的集合进行编码。每个GOP一定是以一个I帧开始的,但是却不一定指代的是两个I帧之间的距离。因为一个GOP内可能包含几个I帧,只有第一个I帧(也就是第一帧)才是关键帧。在程序cfg中,GOP的长度和两个I帧的距离也是两个不同参数指定的(如IntraPeriod和GOP Size或者类似的参数)。所以,两个I帧的间距不可能大于GOP的长度,一般情况是更小的。

  • 关于IDR。

         这个词儿的全称是Instantaneous Decoding Refresh,是在H.264中定义的结构。在H.264中,IDR帧一定是I帧,而且一定是GOP的开始,也是H.264 GOP的关键帧。但是反过来却不成立,I帧不一定是IDR帧。GOP的长度不是定死不变的,在H.264的编码器中,如果判定场景发生变化,那么及时不到原定GOP的末尾,也会在这个位置加入一个IDR,作为新一个GOP的开始。此时这个GOP的长度就被缩小了。
 

  • 闭合GOP和开放GOP(closed GOP/open GOP),CRA

         闭合GOP是H.264中GOP的格式。在H.264的GOP中,所有的GOP都是独立解码的,与其他GOP无关,即它们都是“封闭”的。但是在HEVC中,GOP的结构发生了变化,采用了“开放”的结构,在解码过程过可能会参考其他GOP的数据。这时,一个GOP的起始帧命名为CRA, clean random access,同样采用帧内编码,但是这个GOP内的帧间编码帧可以越过CRA参考前一个GOP的数据,这便是GOP的open。
 

  • 关于BLA

        BLA只是CRA在视频流切换情况下的一种特例。视频流在某个RAP上要求切换到另一个视频流继续解码,则直接将该CRA同另一个视频流中的接入CRA连接,后者便是BLA。由于BLA之前解码到缓存的视频流与当前视频流无关,因此其特性类似于直接从该点进行随机存取后的CRA。
 

  • RASL和RADL

        这是两种GOP间的图像类型。如果解码器从某个CRA随机接入,则按照显示顺序的后面几帧数据由于缺少参考帧而不能解码,这些图像将被解码器抛弃,即skip leading。而对于没有从当前CRA接入的数据,这些图像可以被正常解码显示,因此称为decodable leading。由于这些数据是有可能舍弃的,因此其他图像(trailing pictures)不能参考这些数据,否则万一这些图像被舍弃,将会有更多的图像受其影响而不能正常解码。

举例说明:
假设视频序列的显示顺序为①,这是一个完整的GOP,解码顺序为②
①I B B P B B P B B P
②I P B B P B B P B B
在H.264中,第一个I帧为IDR,GOP为闭合结构,因此两个GOP组成视频的结构为
I B B P B B P B B PI B B P B B P B B P(显示顺序)
I P B B P B B P B BI P B B P B B P B B(解码顺序)
而在HEVC中,两个I帧为CRA,GOP为开放结构,因此GOP的结构为:
I B B P B B P B B PB B I B B P B B P B(显示顺序)
I P B B P B B P B B I B B P B B P B B...(解码顺序)
        两个红色的B帧表示的是按照解码顺序在CRA之后,该GOP内参考的前一个GOP进行编码的图像。这样便很容易得知,如果选择在第二个CRA进行随机接入,这两个红色的B帧将会由于没有参考帧无法解码而被舍弃。这两个红色的B帧即RASP。如果没有选择这个CRA进行随机接入,这两个红色B帧将可以顺利解码,即成为RADP。
        对于BLA,情况也是类似的。由于出现码流拼接,第二段码流的CRA之后的B也会因为没有参考帧无法解码而丢弃。很容易理解,此时缓存中的参考帧数据还来自上一段码流,跟当前码流没关系,当然不能用作B的参考了。
之余HEVC这么设计的目的,我觉得应该是为了编码效率考虑的。因为B帧的压缩比相对是最高的,引入这种设计可以在不影响随机存取性能的前提下,尽可能增大B帧的比重,提高整体压缩编码的性能。
以下是一些国外研究者在论坛中对这个问题的一些讨论,可以拿来做一下参考:

 www.linkedin.com/groups/IDR-vs-CRA-3724292.S.125836481

forum.doom9.org/archive/index.php/t-105129.html

H265中引入的新结构:

  • Random access point picture

    IDR(instantaneous decoder refresh)
    BLA(broken link access)
    CRA(clean random access)

  • Leading picture RASL和RADL的总称

    RADL(random access decodable leading)在随机接入点后,可以按顺序解码的图像
    RASL(random access skipped leading)因为包含帧间信息而不能解码的图像--会被丢弃

  • Temporal sub-layer access picture随机接入点后按解码和输出顺序的图像。

    TSA(temporal sublayer access)
    STSA(stepwise temporal sublayer access)

    因此可以说随机接入点的图像一定是I帧,则H265中I帧的类型有以下几种IDR/BLA/CRA,也就是NALU中的16~21均为I帧,代码可参考ffmpeg相关判断代码:

   switch (type) {
        case NAL_VPS:        vps++;  break;
        case NAL_SPS:        sps++;  break;
        case NAL_PPS:        pps++;  break;
        case NAL_BLA_N_LP:
        case NAL_BLA_W_LP:
        case NAL_BLA_W_RADL:
        case NAL_CRA_NUT:
        case NAL_IDR_N_LP:
        case NAL_IDR_W_RADL: irap++; break;
        }


 

  • 10
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
获取H.265/HEVC中 Slice Type 类型的代码示例如下: ```c++ #include <stdio.h> #define MAX_SLICE_NUM 1024 // NALU类型 typedef enum { NALU_TYPE_SLICE = 1, NALU_TYPE_IDR = 5, NALU_TYPE_SPS = 7, NALU_TYPE_PPS = 8, } NaluType; // slice类型 typedef enum { SLICE_TYPE_B = 0, SLICE_TYPE_P = 1, SLICE_TYPE_I = 2, } SliceType; int main(int argc, char *argv[]) { // 打开码文件 FILE *fp = fopen("test.h265", "rb"); if (!fp) { printf("Open file failed!\n"); return -1; } // 初始化变量 int nalu_len = 0; unsigned char nalu_header[4] = {0x00, 0x00, 0x00, 0x01}; // NALU头 unsigned char slice_header[4] = {0x00, 0x00, 0x00, 0x01}; // Slice头 unsigned char buffer[MAX_SLICE_NUM] = {0}; int buffer_len = 0; int slice_type = -1; // 循环读取NALU while (!feof(fp)) { // 读取NALU长度 if (fread(&nalu_len, 4, 1, fp) != 1) { break; } // 读取NALU fread(buffer, 1, nalu_len, fp); // 获取NALU类型 int nalu_type = buffer[0] & 0x1f; // 如果是Slice类型,则获取Slice Type if (nalu_type == NALU_TYPE_SLICE || nalu_type == NALU_TYPE_IDR) { // 获取Slice头 unsigned char *slice_start = buffer; while (slice_start < buffer + nalu_len) { if (slice_start[0] == 0x00 && slice_start[1] == 0x00 && slice_start[2] == 0x00 && slice_start[3] == 0x01) { break; } slice_start++; } if (slice_start >= buffer + nalu_len - 4) { continue; } // 获取Slice Type slice_type = slice_start[4] % 5; // 输出Slice Type printf("Slice Type: %d\n", slice_type); } } // 关闭文件 fclose(fp); return 0; } ``` 上述代码通过循环读取NALU判断NALU类型是否为 Slice 类型,如果是,则从NALU中获取 Slice Type 类型。其中,Slice Type 类型定义如下: - 0:B slice - 1:P slice - 2:I slice 需要注意的是,在 H.265/HEVC中,Slice Type 类型可能存在于 Slice Header 中的第一个字节的后 5 位。因此,需要先找到 Slice Header 的起始位置,再从中获取 Slice Type

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值