日韩黑丝制服一区视频播放|日韩欧美人妻丝袜视频在线观看|九九影院一级蜜桃|亚洲中文在线导航|青草草视频在线观看|婷婷五月色伊人网站|日本一区二区在线|国产AV一二三四区毛片|正在播放久草视频|亚洲色图精品一区

分享

x264中的NAL流程

 lyz1985 2010-07-06
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

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多