x264中的NAL流程
目前,主要是在分析NAL,做抓包實驗,所以對NAL的格式要求比較高,這個過程中讀了《新一代視頻編碼》,以前也讀過,這是沒有遇到實際的問題,讀的時候也是似懂非懂的,囫圇吞棗,現(xiàn)在要分析,要用了才知道這些相關(guān)文檔是要好好讀的,ES流也是要好好分析的。在上一篇中關(guān)于函數(shù)指針和指針函數(shù)的理論知識中,我主要是來看看x264中的NAL是怎么在封裝的。 在x264中使用的函數(shù)指針,關(guān)于NAL部分的下面的一些:
static int (*p_write_nalu)( hnd_t handle, uint8_t *p_nal, int i_size );,在這里可以看到p_write_nalu)是一個指針函數(shù),p_write_nalu = write_nalu_bsf;即對于p_write_nalu其所指向的函數(shù)write_nalu_bsf,類似的還有: p_write_nalu = write_nalu_mp4; p_write_nalu = write_nalu_mkv; //每一個NALU都是由header+payload組成的,在header的結(jié)構(gòu)是可以參考264的相關(guān)文檔
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, NAL_AUD = 9, /* ref_idc == 0 for 6,9,10,11,12 */ }; enum nal_priority_e { NAL_PRIORITY_DISPOSABLE = 0, NAL_PRIORITY_LOW = 1, NAL_PRIORITY_HIGH = 2, NAL_PRIORITY_HIGHEST = 3, }; //NAL結(jié)構(gòu)
typedef struct { int i_ref_idc; /* nal_priority_e */ int i_type; /* nal_unit_type_e */ /* Size of payload in bytes. */
int i_payload; //負(fù)載的大小 /* If param->b_annexb is set, Annex-B bytestream with 4-byte startcode. * Otherwise, startcode is replaced with a 4-byte size. * This size is the size used in mp4/similar muxing; it is equal to i_payload-4 */ uint8_t *p_payload;//如果是字節(jié)流格式的NAL時所用到的前綴4bytes } x264_nal_t; 下面主要是跟蹤x264,得到NAL的封裝流程,如下: 1.
//NAL static void x264_nal_start( x264_t *h, int i_type, int i_ref_idc ) { x264_nal_t *nal = &h->out.nal[h->out.i_nal]; nal->i_ref_idc = i_ref_idc;
nal->i_type = i_type; nal->i_payload= 0;
nal->p_payload= &h->out.p_bitstream[bs_pos( &h->out.bs ) / 8]; } //下面是對bs_pos函數(shù)的注解
static inline int bs_pos( bs_t *s ) { return( 8 * (s->p - s->p_start) + (WORD_SIZE*8) - s->i_left ); / /獲取當(dāng)前的NALU的地址?????????- s->i_left } //bs_s的結(jié)構(gòu)
typedef struct bs_s { uint8_t *p_start; uint8_t *p; uint8_t *p_end; intptr_t cur_bits;
int i_left; /* i_count number of available bits */ int i_bits_encoded; /* RD only */ } bs_t; 2. static int x264_encoder_encapsulate_nals( x264_t *h ) //NAL封裝 { int nal_size = 0, i; uint8_t *nal_buffer; for( i = 0; i < h->out.i_nal; i++ ) nal_size += h->out.nal[i].i_payload; /* Worst-case NAL unit escaping: reallocate the buffer if it's too small. */
if( h->nal_buffer_size < nal_size * 3/2 + h->out.i_nal * 4 ) { uint8_t *buf = x264_malloc( nal_size * 2 + h->out.i_nal * 4 ); if( !buf ) return -1; x264_free( h->nal_buffer ); h->nal_buffer = buf; } nal_buffer = h->nal_buffer;
for( i = 0; i < h->out.i_nal; i++ )
{ int size = x264_nal_encode( nal_buffer, h->param.b_annexb, &h->out.nal[i] ); h->out.nal[i].i_payload = size; h->out.nal[i].p_payload = nal_buffer; nal_buffer += size; } return nal_buffer - h->nal_buffer;
} 3.在2.中有:
int x264_nal_encode( uint8_t *dst, int b_annexb, x264_nal_t *nal ) { uint8_t *src = nal->p_payload; //為了同意結(jié)構(gòu)還是將字節(jié)流格式的前綴作為指針的初始值 uint8_t *end = nal->p_payload + nal->i_payload; uint8_t *orig_dst = dst; int i_count = 0, size; /* long nal start code (we always use long ones) */
if( b_annexb ) //這里是進(jìn)行字節(jié)流格式的碼流編碼,有開始前綴碼,對于RTP封裝則不需要前綴碼 { *dst++ = 0x00; *dst++ = 0x00; *dst++ = 0x00; *dst++ = 0x01; } else /* save room for size later */ dst += 4; /* nal header */
*dst++ = ( 0x00 << 7 ) | ( nal->i_ref_idc << 5 ) | nal->i_type;//第一個bit的設(shè)置是由編碼器自己控制的 while( src < end )
{ if( i_count == 2 && *src <= 0x03 ) { *dst++ = 0x03;//偽起始碼 i_count = 0; } if( *src == 0 ) i_count++; else i_count = 0; *dst++ = *src++; } size = (dst - orig_dst) - 4; //減4主要是前面的前綴碼所占的字節(jié) /* Write the size header for mp4/etc */
if( !b_annexb ) { /* Size doesn't include the size of the header we're writing now. */ orig_dst[0] = size>>24; orig_dst[1] = size>>16; orig_dst[2] = size>> 8; orig_dst[3] = size>> 0; } return size+4; //+4
} 4. static int x264_nal_end( x264_t *h ) { x264_nal_t *nal = &h->out.nal[h->out.i_nal]; nal->i_payload = &h->out.p_bitstream[bs_pos( &h->out.bs )/ 8] - nal ->p_payload; h->out.i_nal++; /* if number of allocated nals is not enough, re-allocate a larger one. */
if( h->out.i_nal >= h->out.i_nals_allocated ) { x264_nal_t *new_out = x264_malloc( sizeof(x264_nal_t) * (h->out.i_nals_allocated*2) ); if( !new_out ) return -1; memcpy( new_out, h->out.nal, sizeof(x264_nal_t) * (h->out.i_nals_allocated) ); x264_free( h->out.nal ); h->out.nal = new_out; h->out.i_nals_allocated *= 2; } return 0; } 5.p_write_nalu int write_nalu_bsf( hnd_t handle, uint8_t *p_nalu, int i_size ) { if( fwrite( p_nalu, i_size, 1, (FILE*)handle ) > 0 ) //就是把p_nalu里面的1*i_size的字節(jié)輸出到handle里面 return i_size; return -1; } 實驗跟蹤: 在編碼第一個I幀的時候,要編碼的NALU的個數(shù)為4個,這里主要是指編碼的類型為:SEI,SPS,PPS,I幀的NAL的編碼,對于一個I幀,也就是對這個GOP中的圖像序列參數(shù),圖像參數(shù)進(jìn)行編碼,即有如下: /* Write SPS and PPS */ if( i_nal_type == NAL_SLICE_IDR && h->param.b_repeat_headers ) { if( h->fenc->i_frame == 0 ) { /* identify ourself */ x264_nal_start( h, NAL_SEI, NAL_PRIORITY_DISPOSABLE ); if( x264_sei_version_write( h, &h->out.bs ) ) return -1; if( x264_nal_end( h ) ) return -1; overhead += h->out.nal[h->out.i_nal-1].i_payload + NALU_OVERHEAD; } /* generate sequence parameters */
x264_nal_start( h, NAL_SPS, NAL_PRIORITY_HIGHEST ); x264_sps_write( &h->out.bs, h->sps ); if( x264_nal_end( h ) ) return -1; overhead += h->out.nal[h->out.i_nal-1].i_payload + NALU_OVERHEAD; /* generate picture parameters */
x264_nal_start( h, NAL_PPS, NAL_PRIORITY_HIGHEST ); x264_pps_write( &h->out.bs, h->pps ); if( x264_nal_end( h ) ) return -1; overhead += h->out.nal[h->out.i_nal-1].i_payload + NALU_OVERHEAD; } int x264_encoder_headers( x264_t *h, x264_nal_t **pp_nal, int *pi_nal ) { int frame_size = 0; /* init bitstream context */ h->out.i_nal = 0; bs_init( &h->out.bs, h->out.p_bitstream, h->out.i_bitstream ); /* Write SEI, SPS and PPS. */
x264_nal_start( h, NAL_SEI, NAL_PRIORITY_DISPOSABLE ); if( x264_sei_version_write( h, &h->out.bs ) ) return -1; if( x264_nal_end( h ) ) return -1; /* generate sequence parameters */
x264_nal_start( h, NAL_SPS, NAL_PRIORITY_HIGHEST ); x264_sps_write( &h->out.bs, h->sps ); if( x264_nal_end( h ) ) return -1; /* generate picture parameters */
x264_nal_start( h, NAL_PPS, NAL_PRIORITY_HIGHEST ); x264_pps_write( &h->out.bs, h->pps ); if( x264_nal_end( h ) ) return -1; bs_flush( &h->out.bs ); frame_size = x264_encoder_encapsulate_nals( h );
/* now set output*/
*pi_nal = h->out.i_nal; *pp_nal = &h->out.nal[0]; h->out.i_nal = 0; return frame_size;
} 總結(jié)一下:對于NALU,并不是所謂的一幀對應(yīng)一個NALU,而是對于SLICE而言,一個slice就封裝層一個nal,具體一幀中有幾個nalu則是可以再pps中參數(shù)中進(jìn)行設(shè)定的,每遇到一個IDR,則此時就將對應(yīng)的SPS,PPS進(jìn)行一個更新,NAL的生成過程:先是對header里面的8個bits進(jìn)行設(shè)定,然后是Payload,中間的細(xì)節(jié),過于復(fù)雜,目前還不能夠鉆的很深,就先理解到這里。只是把大概的流程疏通。對下一步NALU的提取準(zhǔn)備。
本文來自CSDN博客,轉(zhuǎn)載請標(biāo)明出處:http://blog.csdn.net/adixia/archive/2010/01/21/5221957.aspx |
|