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 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_type15 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_id21 22 return UsedBits; 23 }
如上代码,第二次的ue(无符号哥伦布编码)得出了帧类型,但是还需注意到做差:"if (tmp>4) tmp -=5;",才能得到真正的SliceType:
1 typedef enum { 2 P_SLICE = 03 B_SLICE,1)">4 I_SLICE,1)">5 SP_SLICE,1)">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的实现
static int h264_slice_header_parse(const H264Context *h,H264SliceContext *sl,1)"> 2 const H2645NAL *nal) 3 4 const SPS *sps; 5 const PPS *pps; 6 ret; 7 unsigned slice_type,tmp,i; 8 field_pic_flag,bottom_field_flag; 9 int first_slice = sl == h->slice_ctx && !h->current_slice; 10 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->18 if (slice_type > 9) { 19 av_log(h->avctx,AV_LOG_ERROR,1)">20 slice type %d too large at %d\n21 slice_type,sl->first_mb_addr); 22 AVERROR_INVALIDDATA; 23 } 24 425 slice_type -= 26 sl->slice_type_fixed = 127 } else 28 sl->slice_type_fixed = 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 & 333 34 if (nal->type == H264_NAL_IDR_SLICE && 35 sl->slice_type_nos != AV_PICTURE_TYPE_I) { 36 av_log(h->avctx,A non-intra slice in an IDR NAL unit.\n); 37 38 39 40 sl->pps_id = get_ue_golomb(&sl->41 ... 42 }
同样也是超过4时,再减去5即得到了帧类型。
3. 小工具实现
int GetFrameType(NALU_t * nal) bs_t s; int frame_type = 5 6 bs_init(&s,nal->buf+1,nal->len - 7 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(&14 switch(frame_type) 15 { 16 case 0: 5: P 17 nal->frame_type = FRAME_TYPE_P; 18 break19 1: 6: B 20 nal->frame_type = FRAME_TYPE_B; 21 3: 8: SP 23 nal->frame_type =24 25 2: 7: I 26 nal->frame_type = FRAME_TYPE_I; 27 28 4: 9: SI 29 nal->frame_type =30 31 default: 32 printf(unknown frame type! nalu_data[%#x,%#x,%#x]\n",nal->buf[0],1)">1],1)">2],1)">]); 33 34 } 35 36 37 38 nal->frame_type = nal->nal_unit_type; 39 40 41 return 42 }