1. 程式人生 > >h.264判斷一幀的結束

h.264判斷一幀的結束

最近上了一個專案,有些地方要摳一些細節,達到效果最優,其中有一處就是要從es流中整合出完整的幀。搜了一些資料,然後參照live555的程式碼做一下總結,留著以後備用。

PS: 從事流媒體開發時間不長,有哪些地方理解有誤,還希望能幫我指正出來,共同進步^_^ 

首先講一下es流的結構,看完標準發現h.264沒有幀的概念,是以片(slice也有翻譯成條帶的)為單元的。

如下圖:


參考文章:http://zhongcong386.blog.163.com/blog/static/1347278042012112762445528/

NAL_header(1Byte)+(slice_header)+(slice_data)+......+NAL_header(1Byte)+(slice_header)+(slice_data)   

slice_data= rbsp_byte=巨集塊(實實在在的視訊資料),當然上面nal_header之後的rbsp資料格式不同型別的NAL也不同。

比如:nal_unit_type ==5 的idr slice 格式是slice_header( )+slice_data( )+rbsp_slice_trailing_bits( )。 具體的各個nal slice的格式參看標準的7.3.2.*

(nal_unit_type <= 5 && nal_unit_type > 0)的nal單元都負載的VCL資料(視訊流),每個vcl型別的nal單元儲存的是一個片(slice)。

接下來就是分理出完整一幀資料。假設我們已經分離出了連續的nal_unit_type =1~5的NAL。

第一種情況 沒有下一個nal則表示當前的nal是當前幀的最後一部分資料。

第二種情況  nal_unit_type !=next_nal_unit_type  則表示上一幀已經結束。

第三種情況 nal_unit_type == next_nal_unit_type的時候有可能是同一幀的資料,也有可能是不同幀的資料。(如:兩個PP幀在一塊就有可能出現這種情況)

出現第三種情況的時候我們需要分析slice_header裡面的frame_num這一個值。如果frame_num != next_frame_num 則表示當前nal已經是當前幀的最後一部分資料。

slice_header()的句法如下: PS 太多啦,有需求直接去看標準;

slice_header( ) { 
 first_mb_in_slice 2 ue(v)
 slice_type 2 ue(v)
 pic_parameter_set_id 2 ue(v)
 frame_num 2 u(v)
 if( !frame_mbs_only_flag ) {
  field_pic_flag 2 u(1)
 if( field_pic_flag )
 bottom_field_flag 2 u(1)
 }
 if( nal_unit_type = = 5 )
 idr_pic_id 2 ue(v)
 if( pic_order_cnt_type = = 0 ) {
 pic_order_cnt_lsb 2 u(v)
 if( pic_order_present_flag && !field_pic_flag )
 delta_pic_order_cnt_bottom 2 se(v)
 }
 ..................................
 ..................................
 if( num_slice_groups_minus1 > 0 && slice_group_map_type >= 3 && slice_group_map_type <= 5)
 slice_group_change_cycle 2 u(v)
}



具體live555分析的程式碼我摘錄下來,貼在下面
fHaveSeenFirstByteOfNALUnit =true表示當前幀結束
unsigned H264VideoStreamParser::parse() {
..........................
..........................
   // If this NAL unit is a VCL NAL unit, we also scan the start of the next NAL unit, to determine whether this NAL unit
    // ends the current 'access unit'.  We need this information to figure out when to increment "fPresentationTime".
    // (RTP streamers also need to know this in order to figure out whether or not to set the "M" bit.)
    Boolean thisNALUnitEndsAccessUnit = False; // until we learn otherwise 
    if (haveSeenEOF()) {//最後一個nal肯定是幀的最後一部分了
      thisNALUnitEndsAccessUnit = True;
    } else {
      Boolean const isVCL = nal_unit_type <= 5 && nal_unit_type > 0; //Would need to include type 20 for SVC and MVC #####
      if (isVCL) {//
		  u_int8_t firstByteOfNextNALUnit;
		  testBytes(&firstByteOfNextNALUnit, 1);
		  u_int8_t next_nal_ref_idc = (firstByteOfNextNALUnit&0x60)>>5;
		  u_int8_t next_nal_unit_type = firstByteOfNextNALUnit&0x1F;
	if (next_nal_unit_type >= 6) {//nal_unit_type !=next_nal_unit_type的情況
 	// The next NAL unit is not a VCL; therefore, we assume that this NAL unit ends the current 'access unit':
	  thisNALUnitEndsAccessUnit = True;
	} else {
	  // The next NAL unit is also a VCL.  We need to examine it a little to figure out if it's a different 'access unit'.
	  // (We use many of the criteria described in section 7.4.1.2.4 of the H.264 specification.)
	  Boolean IdrPicFlag = nal_unit_type == 5;
	  Boolean next_IdrPicFlag = next_nal_unit_type == 5;
	  if (next_IdrPicFlag != IdrPicFlag) {
	    // IdrPicFlag differs in value
	    thisNALUnitEndsAccessUnit = True;
	  } else if (next_nal_ref_idc != nal_ref_idc && next_nal_ref_idc*nal_ref_idc == 0) {
	    //nal_ref_idc differs in value with one of the nal_ref_idc values being equal to 0
	    thisNALUnitEndsAccessUnit = True;
	  } else if ((nal_unit_type == 1 || nal_unit_type == 2 || nal_unit_type == 5)
		     && (next_nal_unit_type == 1 || next_nal_unit_type == 2 || next_nal_unit_type == 5)) {//這是第三種情況主要的功能在
//analyze_slice_header中
	    // Both this and the next NAL units begin with a "slice_header".
	    // Parse this (for each), to get parameters that we can compare:
	    // Current NAL unit's "slice_header":
	    unsigned frame_num, pic_parameter_set_id, idr_pic_id;
	    Boolean field_pic_flag, bottom_field_flag;
		analyze_slice_header(fStartOfFrame + fOutputStartCodeSize, fTo, nal_unit_type,
			frame_num, pic_parameter_set_id, idr_pic_id, field_pic_flag, bottom_field_flag);  
	    // Next NAL unit's "slice_header":
	    u_int8_t next_slice_header[NUM_NEXT_SLICE_HEADER_BYTES_TO_ANALYZE];
	    testBytes(next_slice_header, sizeof next_slice_header);
	    unsigned next_frame_num, next_pic_parameter_set_id, next_idr_pic_id;
	    Boolean next_field_pic_flag, next_bottom_field_flag;
		analyze_slice_header(next_slice_header, &next_slice_header[sizeof next_slice_header], next_nal_unit_type,
			next_frame_num, next_pic_parameter_set_id, next_idr_pic_id, next_field_pic_flag, next_bottom_field_flag);
	    if (next_frame_num != frame_num) {
	      // frame_num differs in value
	      thisNALUnitEndsAccessUnit = True;
	    } else if (next_pic_parameter_set_id != pic_parameter_set_id) {
	      // pic_parameter_set_id differs in value
	      thisNALUnitEndsAccessUnit = True;
	    } else if (next_field_pic_flag != field_pic_flag) {
	      // field_pic_flag differs in value
	      thisNALUnitEndsAccessUnit = True;
	    } else if (next_bottom_field_flag != bottom_field_flag) {
	      // bottom_field_flag differs in value
	      thisNALUnitEndsAccessUnit = True;
	    } else if (next_IdrPicFlag == 1 && next_idr_pic_id != idr_pic_id) {
	      // IdrPicFlag is equal to 1 for both and idr_pic_id differs in value
	      // Note: We already know that IdrPicFlag is the same for both.
	      thisNALUnitEndsAccessUnit = True;
	    }
	  }
..........................
}

先就這樣,下次再補充。

這一篇部落格有更好的方法和大家一塊分享http://www.360doc.com/content/13/0913/15/13084517_314201133.shtml