x264編碼得到的文件,在文件頭部的一些東西,有了新發現
轉載自:http://wmnmtm.blog.163.com/blog/static/38245714201192211245389/
猜想:
上面是一個sps(sps1,sps_2,sps_3),看著內容好少啊。寫入代碼是:file1.Write(h->sps,sizeof(x264_sps_t));
下載的一個源碼里,實現了把264文件通過RTP發送,但是如果發送實時編碼的怎么辦,序列參數集和圖像參數集得自己發送,因為264文件本身在文件開頭已經存儲了這些了。

(圖一)這是一個正確編碼得到的xxx.264文件,用UltraEDit打開的文件開始部分
在x264.dsw工程中,找了一下,找到點相關的東西。
另外,x264.exe在編碼文件時,只有一個地方進行文件的寫入,就是
int write_nalu_bsf( hnd_t handle, uint8_t *p_nalu, int i_size )
{
if (fwrite(p_nalu, i_size, 1, (FILE *)handle) > 0)
{
return i_size;
}
return -1;
}
如果把函數里的代碼注釋掉,會發現,雖然在不停的編碼,但最終文件大小始終為0,從這一點可以證明只有此處進行文件的寫入操作。
那么,序列參數集是哪里來的呢,又找到一部分東西,在encoder.c文件的函數:
int x264_encoder_encode( x264_t *h,/* 指定編碼器 */
x264_nal_t **pp_nal, /* x264_nal_t * */
int *pi_nal, /* int */
x264_picture_t *pic_in,
x264_picture_t *pic_out )
中,有如下代碼:
/* Write SPS and PPS 寫序列參數集、圖像參數集以及SEI版本信息,并不是寫入文件,而是寫入輸出緩沖區 最后通過p_write_nalu中的fwrite寫入到文件*/
if( i_nal_type == NAL_SLICE_IDR && h->param.b_repeat_headers )
{
printf("encoder.c : Write SPS and PPS");
system("pause");//暫停,任意鍵繼續
if( h->fenc->i_frame == 0 )
{
/* identify ourself */
x264_nal_start( h, NAL_SEI, NAL_PRIORITY_DISPOSABLE );
x264_sei_version_write( h, &h->out.bs );
x264_nal_end( h );
}
/* generate sequence parameters */
x264_nal_start( h, NAL_SPS, NAL_PRIORITY_HIGHEST );
x264_sps_write( &h->out.bs, h->sps );
x264_nal_end( h );
/* generate picture parameters */
x264_nal_start( h, NAL_PPS, NAL_PRIORITY_HIGHEST );
x264_pps_write( &h->out.bs, h->pps );
x264_nal_end( h );
}
/* Write frame 寫入幀,并不是寫入文件,而是寫入輸出緩沖區*/
i_frame_size = x264_slices_write( h );
在圖一中,看到有一個網址http://www.videolan.org,在源碼中搜索這個網址,找到唯一的一處,也就是如下的函數:
void x264_sei_version_write( x264_t *h, bs_t *s )
{
int i;
// random ID number generated according to ISO-11578
const uint8_t uuid[16] = {
0xdc, 0x45, 0xe9, 0xbd, 0xe6, 0xd9, 0x48, 0xb7,
0x96, 0x2c, 0xd8, 0x20, 0xd9, 0x23, 0xee, 0xef
};
char version[1200];
int length;
char *opts = x264_param2string( &h->param, 0 );
sprintf( version, "x264 - core %d%s - H.264/MPEG-4 AVC codec - "
"Copyleft 2005 - http://www.videolan.org/x264.html - options: %s",
X264_BUILD, X264_VERSION, opts );
x264_free( opts );
length = strlen(version)+1+16;
bs_write( s, 8, 0x5 ); // payload_type = user_data_unregistered
// payload_size
for( i = 0; i <= length-255; i += 255 )
bs_write( s, 8, 255 );
bs_write( s, 8, length-i );
for( i = 0; i < 16; i++ )
bs_write( s, 8, uuid[i] );
for( i = 0; i < length-16; i++ )
bs_write( s, 8, version[i] );
bs_rbsp_trailing( s );
}

(圖二)注釋掉x264_sps_write( &h->out.bs, h->sps );的情況

(圖三)注釋掉x264_pps_write( &h->out.bs, h->pps );的情況

(圖四)x264_sei_version_write( h, &h->out.bs );注釋掉,但是播放是一樣的
既然注釋掉仍能播,說明它不是必須的,僅是個版權聲明
如果是實時的話,可以模仿下面的這兩句代碼發送序列參數集和圖像參數集:
x264_sps_write( &h->out.bs, h->sps );
x264_pps_write( &h->out.bs, h->pps );
實際就是要發送h->sps和h->pps
因為我編碼的文件是可以播放的,所以實際已經產生了sps和pps,直接發送就行了。
因為h是已知的,h = x264_encoder_open( ¶m );它的返回值就是x264_t,這樣就可以通過h->sps和h->pps,下面是我將它們單獨寫入文件的截圖:

sps_1

sps_2

sps_3

pps
PPS的內容也好少,可能是因為大多數均未指定,采用的是默認參數的原因吧,但是圖像寬度和高度在哪兒了。
h->x264_param_t param;
這個也很關鍵,因為我指定的都是在這個里,
param.i_width=320;
param.i_height=240;
把這個也保一份看看。

h->PARAM
param.i_width=320;
param.i_height=240;
這兩個數字對應的在哪呢,應該有才對啊,找找。(其實已經有了,只是現在沒找到,看后面)
后來換了個參數值,又試了一下
param.i_width=320;
param.i_height=240;

這是i_width 為320和100的比較,一直折騰半天,發現0x64能對應上十進制100,但是320怎么也對不上。
后來又從結構體的存儲上找了半天(我直觀的認為結構體中的變量字段是按順序存在一塊連續空間的,對不對不知道了)

我分析,在unsigned int 和int共占8字節,那么第9、10、11、12個字節應該就是i_width,但還是對不上。直到ing提到小頭和大頭,也就是主機字節序和網絡字節序時,才驚醒過來。
原來,前面一直是對的,只是自己看成是錯的了,呵呵。
0x 64 00 ,實際應該看成 0x 00 64 ,換算成二進制表示: 0000 0000 0110 0100
0x 40 01 ,實際應該看成 0x 01 40 ,換算成二進制表示: 0000 0001 0100 0000




這下就對了。
小結一下:
1、結構體的各字段存儲在一段連續的內存中。
2、主機字節序,用二進制查看器查看到 0x 64 00 ,換算二進制時,要按 0x 00 64這樣反過來。(說法不標準,呵呵)
3、x264.exe編碼得到的文件,前面有一段版權聲明,是沒用的。
4、file1.Write(&(h->param),sizeof(x264_param_t));這樣把h->param寫入二進制文件是正確的。
5、只發送sps和pps可以嗎