• JM8.6解码——帧类型判别


      h264裸码流,根据nalu_header可以知道类型,例如该帧是I帧,P帧/B帧。

      例如,常见的0x65代表I帧,0x41代表非关键帧,即P帧或B帧,但是只根据nalu_header是无法区分P帧和B帧的,还需进入到RBSP内部根据语法含义来做判断。

    1. JM实现的判别

     1 int FirstPartOfSliceHeader()
     2 {
     3   Slice *currSlice = img->currentSlice;
     4   int dP_nr = assignSE2partition[currSlice->dp_mode][SE_HEADER];
     5   DataPartition *partition = &(currSlice->partArr[dP_nr]);
     6   Bitstream *currStream = partition->bitstream;
     7   int tmp;
     8 
     9   UsedBits= partition->bitstream->frame_bitoffset; // was hardcoded to 31 for previous start-code. This is better.
    10 
    11   // Get first_mb_in_slice
    12   currSlice->start_mb_nr = ue_v ("SH: first_mb_in_slice", currStream);
    13 
    14   tmp = ue_v ("SH: slice_type", currStream);
    15   
    16   if (tmp>4) tmp -=5;
    17 
    18   img->type = currSlice->picture_type = (SliceType) tmp;
    19 
    20   currSlice->pic_parameter_set_id = ue_v ("SH: pic_parameter_set_id", currStream);
    21   
    22   return UsedBits;
    23 }
    View Code

      如上代码,第二次的ue(无符号哥伦布编码)得出了帧类型,但是还需注意到做差:"if (tmp>4) tmp -=5;",才能得到真正的SliceType:

    1 typedef enum {
    2     P_SLICE = 0,
    3     B_SLICE,
    4     I_SLICE,
    5     SP_SLICE,
    6     SI_SLICE
    7 } SliceType;

      例如,一段nalu数据为:0x00 00 01 65 88 80 06 64

      那么,从0x65后的字节0x88开始算起,

      第一个ue得到start_mb_nr,即其值为(2^0 - 1 + 0)= 0,0x88使用了最高位,当前bit_offset=1

      第二个ue得到tmp值,即其值为(2^3 - 1 + 000b)= 7,使用了7bit来计算,当前bit_offset=8,再将tmp-5得到2,那么picture_type为I帧。

      第三个ue得到pps_id值,从0x80的最高位算起,值为(2^0 - 1 + 0)= 0

    2. ffmpeg的实现

      实现类似,参考文件h264_slice.c,关键代码如下:

     1 static int h264_slice_header_parse(const H264Context *h, H264SliceContext *sl,
     2                                    const H2645NAL *nal)
     3 {
     4     const SPS *sps;
     5     const PPS *pps;
     6     int ret;
     7     unsigned int slice_type, tmp, i;
     8     int field_pic_flag, bottom_field_flag;
     9     int first_slice = sl == h->slice_ctx && !h->current_slice;
    10     int picture_structure;
    11 
    12     if (first_slice)
    13         av_assert0(!h->setup_finished);
    14 
    15     sl->first_mb_addr = get_ue_golomb_long(&sl->gb);
    16 
    17     slice_type = get_ue_golomb_31(&sl->gb);
    18     if (slice_type > 9) {
    19         av_log(h->avctx, AV_LOG_ERROR,
    20                "slice type %d too large at %d
    ",
    21                slice_type, sl->first_mb_addr);
    22         return AVERROR_INVALIDDATA;
    23     }
    24     if (slice_type > 4) {
    25         slice_type -= 5;
    26         sl->slice_type_fixed = 1;
    27     } else
    28         sl->slice_type_fixed = 0;
    29 
    30     slice_type         = ff_h264_golomb_to_pict_type[slice_type];
    31     sl->slice_type     = slice_type;
    32     sl->slice_type_nos = slice_type & 3;
    33 
    34     if (nal->type  == H264_NAL_IDR_SLICE &&
    35         sl->slice_type_nos != AV_PICTURE_TYPE_I) {
    36         av_log(h->avctx, AV_LOG_ERROR, "A non-intra slice in an IDR NAL unit.
    ");
    37         return AVERROR_INVALIDDATA;
    38     }
    39 
    40     sl->pps_id = get_ue_golomb(&sl->gb);
    41 ...
    42 }
    View Code

      同样也是超过4时,再减去5即得到了帧类型。

    3. 小工具实现

      本人曾做了一个小工具,参考核心代码如下:

     1 static int GetFrameType(NALU_t * nal)
     2 {
     3     bs_t s;
     4     int frame_type = 0;
     5 
     6     bs_init(&s, nal->buf+1, nal->len - 1);
     7 
     8     if (nal->nal_unit_type == NALU_TYPE_SLICE || nal->nal_unit_type ==  NALU_TYPE_IDR)
     9     {
    10         /* i_first_mb */
    11         bs_read_ue(&s);
    12         /* picture type */
    13         frame_type = bs_read_ue(&s);
    14         switch(frame_type)
    15         {
    16         case 0: case 5: /* P */
    17             nal->frame_type = FRAME_TYPE_P;
    18             break;
    19         case 1: case 6: /* B */
    20             nal->frame_type = FRAME_TYPE_B;
    21             break;
    22         case 3: case 8: /* SP */
    23             nal->frame_type = FRAME_TYPE_P;
    24             break;
    25         case 2: case 7: /* I */
    26             nal->frame_type = FRAME_TYPE_I;
    27             break;
    28         case 4: case 9: /* SI */
    29             nal->frame_type = FRAME_TYPE_I;
    30             break;
    31         default:
    32             printf("unknown frame type! nalu_data[%#x,%#x,%#x,%#x]
    ", nal->buf[0], nal->buf[1], nal->buf[2], nal->buf[3]);
    33             break;
    34         }
    35     }
    36     else
    37     {
    38         nal->frame_type = nal->nal_unit_type;
    39     }
    40 
    41     return 0;
    42 }
  • 相关阅读:
    Vue.js学习 Item14 – 过滤器与自定义过滤器
    Vue.js学习 Item13 – 指令系统与自定义指令
    Vue.js学习 Item12 – 内部响应式原理探究
    redis配置文件redis.conf详细说明
    PhpStorm下Laravel代码智能提示
    laravel 5.0 artisan 命令列表(中文简体)
    阿里云CENTOS服务器挂载数据盘
    NGINX关于配置PATHINFO
    LINUX下导入、导出MYSQL数据库命令
    ECSTORE2.0 定时任务配置
  • 原文地址:https://www.cnblogs.com/Dreaming-in-Gottingen/p/14002922.html
Copyright © 2020-2023  润新知