H264的I/P/B框架類型判斷__流媒體

來源:互聯網
上載者:User

轉自:http://blog.csdn.net/zhuweigangzwg/article/details/44152239


這裡首先說明下H264的結構:

00 00 00 01/00 00 01->nal(1bytes)->slice->宏塊->運動估計向量。

如果h264的body中出現了首碼則由00 00 00 01/00 00 01變為00 03 00 00 01/00 03 00 01.

我們看到常用naltype 像sps= 0x07 pps= 0x08 sei = 0x06   I/P/B=  0x01/0x05 也就是說只判斷naltype = 0x01/0x05是判斷不出來I/P/B框架類型的,需要到slice層去判斷用到“熵編碼”具體的“熵編碼”內容請看:“H.264官方中文版.pdf”.

下面是扣的ffmpeg的源碼判斷I/P/B框架類型的實現:

int GetFrameType(NALU_t * nal){bs_t s;int frame_type = 0; unsigned char * OneFrameBuf_H264 = NULL ;if ((OneFrameBuf_H264 = (unsigned char *)calloc(nal->len + 4,sizeof(unsigned char))) == NULL){printf("Error malloc OneFrameBuf_H264\n");return getchar();}if (nal->startcodeprefix_len == 3){OneFrameBuf_H264[0] = 0x00;OneFrameBuf_H264[1] = 0x00;OneFrameBuf_H264[2] = 0x01;memcpy(OneFrameBuf_H264 + 3,nal->buf,nal->len);}else if (nal->startcodeprefix_len == 4){OneFrameBuf_H264[0] = 0x00;OneFrameBuf_H264[1] = 0x00;OneFrameBuf_H264[2] = 0x00;OneFrameBuf_H264[3] = 0x01;memcpy(OneFrameBuf_H264 + 4,nal->buf,nal->len);}else{printf("H264讀取錯誤。\n");}bs_init( &s,OneFrameBuf_H264 + nal->startcodeprefix_len + 1  ,nal->len - 1 );if (nal->nal_unit_type == NAL_SLICE || nal->nal_unit_type ==  NAL_SLICE_IDR ){/* i_first_mb */bs_read_ue( &s );/* picture type */frame_type =  bs_read_ue( &s );switch(frame_type){case 0: case 5: /* P */nal->Frametype = FRAME_P;break;case 1: case 6: /* B */nal->Frametype = FRAME_B;break;case 3: case 8: /* SP */nal->Frametype = FRAME_P;break;case 2: case 7: /* I */nal->Frametype = FRAME_I;I_Frame_Num ++;break;case 4: case 9: /* SI */nal->Frametype = FRAME_I;break;}}else if (nal->nal_unit_type == NAL_SEI){nal->Frametype = NAL_SEI;}else if(nal->nal_unit_type == NAL_SPS){nal->Frametype = NAL_SPS;}else if(nal->nal_unit_type == NAL_PPS){nal->Frametype = NAL_PPS;}if (OneFrameBuf_H264){free(OneFrameBuf_H264);OneFrameBuf_H264 = NULL;}return 1;}


//H264一幀資料的結構體typedef struct Tag_NALU_t{unsigned char forbidden_bit;           //! Should always be FALSEunsigned char nal_reference_idc;       //! NALU_PRIORITY_xxxxunsigned char nal_unit_type;           //! NALU_TYPE_xxxx  unsigned int  startcodeprefix_len;      //! 首碼位元組數unsigned int  len;                     //! 包含nal 頭的nal 長度,從第一個00000001到下一個000000001的長度unsigned int  max_size;                //! 最多一個nal 的長度unsigned char * buf;                   //! 包含nal 頭的nal 資料unsigned char Frametype;               //! 框架類型unsigned int  lost_packets;            //! 預留} NALU_t;//nal類型enum nal_unit_type_e{NAL_UNKNOWN     = 0,NAL_SLICE       = 1,NAL_SLICE_DPA   = 2,NAL_SLICE_DPB   = 3,NAL_SLICE_DPC   = 4,NAL_SLICE_IDR   = 5,    /* ref_idc != 0 */NAL_SEI         = 6,    /* ref_idc == 0 */NAL_SPS         = 7,NAL_PPS         = 8/* ref_idc == 0 for 6,9,10,11,12 */};//框架類型enum Frametype_e{FRAME_I  = 15,FRAME_P  = 16,FRAME_B  = 17};

//Mybs.h#pragma once#include "Information.h"//讀取位元組結構體typedef struct Tag_bs_t{unsigned char *p_start;               //緩衝區首地址(這個開始是最低地址)unsigned char *p;           //緩衝區當前的讀寫指標 當前位元組的地址,這個會不斷的++,每次++,進入一個新的位元組unsigned char *p_end;           //緩衝區尾地址//typedef unsigned char   uint8_t;int     i_left;           // p所指位元組當前還有多少 “位” 可讀寫 count number of available(可用的)位 }bs_t;/*函數名稱:函數功能:初始化結構體參    數:返 回 值:無傳回值,void類型思    路:資    料:  */void bs_init( bs_t *s, void *p_data, int i_data );/*該函數的作用是:從s中讀出i_count位,並將其做為uint32_t類型返回思路:若i_count>0且s流並未結束,則開始或繼續讀取碼流;若s當前位元組中剩餘位元大於等於要讀取的位元i_count,則直接讀取;若s當前位元組中剩餘位元小於要讀取的位元i_count,則讀取剩餘位,進入s下一位元組繼續讀取。補充:寫入s時,i_left表示s當前位元組還沒被寫入的位,若一個新的位元組,則i_left=8;讀取s時,i_left表示s當前位元組還沒被讀取的位,若一個新的位元組,則i_left=8。注意兩者的區別和聯絡。00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 0000000-------- -----000 00000000 ...寫入s時:i_left = 3讀取s時:i_left = 5我思:位元組流提前放在了結構體bs_s的對象bs_t裡了,可能位元組流不會一次性讀取/分析完,而是根據需要,每次都讀取幾位元bs_s裡,有專門的欄位用來記錄曆史讀取的結果,每次讀取,都會在上次的讀取位置上進行比如,100位元組的流,經過若干次讀取,當前位置處於中間一個位元組處,前3個位元已經讀取過了,此次要讀取2位元00001001000 01 001 (已讀過的 本次要讀的 以後要讀的 )i_count = 2(計划去讀2位元)i_left  = 5(還有5位元未讀,在本位元組中)i_shr = s->i_left - i_count = 5 - 2 = 3*s->p >> i_shr,就把本次要讀的位元移到了位元組最右邊(未讀,但本次不需要的給移到了位元組外,拋掉了)00000001i_mask[i_count] 即i_mask[2] 即0x03:00000011( *s->p >> i_shr )&i_mask[i_count]; 即00000001 & 00000011 也就是00000001 按位與 00000011結果是:00000001i_result |= ( *s->p >> i_shr )&i_mask[i_count];即i_result |=00000001 也就是 i_result =i_result | 00000001 = 00000000 00000000 00000000 00000000 | 00000001 =00000000 00000000 00000000 00000001i_result =return( i_result ); 返回的i_result是4位元組長度的,是unsigned類型 sizeof(unsigned)=4*/int bs_read( bs_t *s, int i_count );/*函數名稱:函數功能:從s中讀出1位,並將其做為uint32_t類型返回。函數參數:返 回 值:思    路:若s流並未結束,則讀取一位資    料:畢厚傑:第145頁,u(n)/u(v),讀進連續的若干位元,並將它們解釋為“不帶正負號的整數”return i_result;//unsigned int*/int bs_read1( bs_t *s );/*函數名稱:函數功能:從s中解碼並讀出一個文法元素值參    數:返 回 值:思    路:從s的當前位讀取並計數,直至讀取到1為止;while( bs_read1( s ) == 0 && s->p < s->p_end && i < 32 )這個迴圈用i記錄了s當前位置到1為止的0的個數,並丟棄讀到的第一個1;返回2^i-1+bs_read(s,i)。例:當s位元組中存放的是0001010時,1前有3個0,所以i=3;返回的是:2^i-1+bs_read(s,i)即:8-1+010=9資    料:畢厚傑:第145頁,ue(v);無符號指數Golomb熵編碼x264中bs.h檔案部分函數解讀 http://wmnmtm.blog.163.com/blog/static/382457142011724101824726/不帶正負號的整數指數哥倫布碼編碼 http://wmnmtm.blog.163.com/blog/static/38245714201172623027946/*/int bs_read_ue( bs_t *s );

//Mybs.cpp#include "Mybs.h"void bs_init( bs_t *s, void *p_data, int i_data ){s->p_start = (unsigned char *)p_data;//用傳入的p_data首地址初始化p_start,只記下有效資料的首地址s->p       = (unsigned char *)p_data;//位元組首地址,一開始用p_data初始化,每讀完一個整位元組,就移動到下一位元組首地址s->p_end   = s->p + i_data;                //尾地址,最後一個位元組的首地址?s->i_left  = 8;                //還沒有開始讀寫,當前位元組剩餘未讀取的位是8}int bs_read( bs_t *s, int i_count ){ static int i_mask[33] ={0x00,                                  0x01,      0x03,      0x07,      0x0f,                                  0x1f,      0x3f,      0x7f,      0xff,                                  0x1ff,     0x3ff,     0x7ff,     0xfff,                                  0x1fff,    0x3fff,    0x7fff,    0xffff,                                  0x1ffff,   0x3ffff,   0x7ffff,   0xfffff,                                  0x1fffff,  0x3fffff,  0x7fffff,  0xffffff,                                  0x1ffffff, 0x3ffffff, 0x7ffffff, 0xfffffff,                                  0x1fffffff,0x3fffffff,0x7fffffff,0xffffffff};/*  數組中的元素用二進位表示如下:  假設:初始為0,已寫入為+,已讀取為-    位元組:1234   00000000 00000000 00000000 00000000下標  0x00:  00000000x[0]  0x01:  00000001x[1]  0x03:  00000011x[2]  0x07:  00000111x[3]  0x0f:  00001111x[4]  0x1f:  00011111x[5]  0x3f:  00111111x[6]  0x7f:  01111111x[7]  0xff:  11111111x[8]1位元組 0x1ff: 0001 11111111x[9] 0x3ff: 0011 11111111x[10]i_mask[s->i_left] 0x7ff: 0111 11111111x[11] 0xfff: 1111 11111111x[12]1.5位元組0x1fff: 00011111 11111111x[13]0x3fff: 00111111 11111111x[14]0x7fff: 01111111 11111111x[15]0xffff: 11111111 11111111x[16]2位元組   0x1ffff:0001 11111111 11111111x[17]   0x3ffff:0011 11111111 11111111x[18]   0x7ffff:0111 11111111 11111111x[19]   0xfffff:1111 11111111 11111111x[20]2.5位元組  0x1fffff:00011111 11111111 11111111x[21]  0x3fffff:00111111 11111111 11111111x[22]  0x7fffff:01111111 11111111 11111111x[23]  0xffffff:11111111 11111111 11111111x[24]3位元組 0x1ffffff:   0001 11111111 11111111 11111111x[25] 0x3ffffff:   0011 11111111 11111111 11111111x[26] 0x7ffffff:    0111 11111111 11111111 11111111x[27] 0xfffffff:    1111 11111111 11111111 11111111x[28]3.5位元組0x1fffffff:00011111 11111111 11111111 11111111x[29]0x3fffffff:00111111 11111111 11111111 11111111x[30]0x7fffffff:01111111 11111111 11111111 11111111x[31]0xffffffff:11111111 11111111 11111111 11111111x[32]4位元組 */    int      i_shr;    //    int i_result = 0;        //用來存放讀取到的的結果 typedef unsigned   uint32_t;    while( i_count > 0 )    //要讀取的位元數    {        if( s->p >= s->p_end )//位元組流的當前位置>=流結尾,即代表此位元流s已經讀完了。        {//            break;        }        if( ( i_shr = s->i_left - i_count ) >= 0 )//當前位元組剩餘的未讀位元,比要讀取的位元多,或者相等        {//i_left當前位元組剩餘的未讀位元,本次要讀i_count位元,i_shr=i_left-i_count的結果如果>=0,說明要讀取的都在當前位元組內//i_shr>=0,說明要讀取的位元都處於當前位元組內//這個階段,一次性就讀完了,然後返回i_result(退出了函數)            /* more in the buffer than requested */            i_result |= ( *s->p >> i_shr )&i_mask[i_count];//“|=”:按位或賦值,A |= B 即 A = A|B//|=應該在最後執行,把結果放在i_result(按位與優先順序高於複合操作符|=)//i_mask[i_count]最右側各位都是1,與括弧中的按位與,可以把括弧中的結果複製過來//!=,左邊的i_result在這兒全是0,右側與它按位或,還是複製結果過來了,好象好幾步都多餘/*讀取後,更新結構體裡的欄位值*/            s->i_left -= i_count; //即i_left = i_left - i_count,當前位元組剩餘的未讀位元,原來的減去這次讀取的            if( s->i_left == 0 )//如果當前位元組剩餘的未讀位元正好是0,說明當前位元組讀完了,就要開始下一個位元組            {                s->p++;//移動指標,所以p好象是以位元組為步長移動指標的                s->i_left = 8;//新開始的這個位元組來說,當前位元組剩餘的未讀位元,就是8位元了            }            return( i_result );//可能的傳回值之一為:00000000 00000000 00000000 00000001 (4位元組長)        }        else/* i_shr < 0 ,跨位元組的情況*/        {//這個階段,是while的一次迴圈,可能還會進入下一次迴圈,第一次和最後一次都可能讀取的非整位元組,比如第一次讀了3位元,中間讀取了2位元組(即2x8位元),最後一次讀取了1位元,然後退出while迴圈//當前位元組剩餘的未讀位元,比要讀取的位元少,比如當前位元組有3位未讀過,而本次要讀7位//???對當前位元組來說,要讀的位元,都在最右邊,所以不再移位了(移位的目的是把要讀的位元放在當前位元組最右)            /* less(較少的) in the buffer than requested */i_result |= (*s->p&i_mask[s->i_left]) << -i_shr;//"-i_shr"相當於取了絕對值//|= 和 << 都是位操作符,優先順序相同,所以從左往右順序執行//舉例:int|char ,其中int是4位元組,char是1位元組,sizeof(int|char)是4位元組//i_left最大是8,最小是0,取值範圍是[0,8]i_count  -= s->i_left;//待讀取的位元數,等於原i_count減去i_left,i_left是當前位元組未讀過的位元數,而此else階段,i_left代表的當前位元組未讀的位元全被讀過了,所以減它s->p++;//定位到下一個新的位元組s->i_left = 8;//對一個新位元組來說,未讀過的位元當然是8,即本位元組所有位都沒讀取過        }    }    return( i_result );//可能的傳回值之一為:00000000 00000000 00000000 00000001 (4位元組長)}int bs_read1( bs_t *s ){if( s->p < s->p_end ){unsigned int i_result;s->i_left--;                           //當前位元組未讀取的位元少了1位i_result = ( *s->p >> s->i_left )&0x01;//把要讀的位元移到當前位元組最右,然後與0x01:00000001進行邏輯與操作,因為要讀的只是一個位元,這個位元不是0就是1,與0000 0001按位與就可以得知此情況if( s->i_left == 0 )                   //如果當前位元組剩餘未讀位元是0,即是說當前位元組全讀過了{s->p++;                   //指標s->p 移到下一位元組s->i_left = 8;                     //新位元組中,未讀位元當然是8位}return i_result;                       //unsigned int}return 0;                                  //返回0應該是沒有讀到東西}int bs_read_ue( bs_t *s ){int i = 0;while( bs_read1( s ) == 0 && s->p < s->p_end && i < 32 )//條件為:讀到的當前位元=0,指標未越界,最多隻能讀32位元{i++;}return( ( 1 << i) - 1 + bs_read( s, i ) );}


相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.