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