JM8.6解碼——幀類型判別

  h264裸碼流,根據nalu_header能夠知道類型,例如該幀是I幀,P幀/B幀。git

  例如,常見的0x65表明I幀,0x41表明非關鍵幀,即P幀或B幀,可是隻根據nalu_header是沒法區分P幀和B幀的,還需進入到RBSP內部根據語法含義來作判斷。github

1. JM實現的判別ide

 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開始算起,spa

  第一個ue獲得start_mb_nr,即其值爲0,0x88使用了最高位,當前bit_offset=1code

  第二個ue獲得tmp值,即其值爲(2^3 - 1 + 000b)=7,使用了7bit來計算,當前bit_offset=8,再將tmp-5獲得2,那麼picture_type爲I幀。blog

  第三個ue獲得pps_id值,從0x80的最高位算起,值爲(2^0 - 1 + 0)=0get

2. ffmpeg的實現it

  實現相似,參考文件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\n",
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.\n");
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]\n", 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 }
相關文章
相關標籤/搜索