牵着老婆满街逛

严以律己,宽以待人. 三思而后行.
GMail/GTalk: yanglinbo#google.com;
MSN/Email: tx7do#yahoo.com.cn;
QQ: 3 0 3 3 9 6 9 2 0 .

x264编码得到的文件,在文件头部的一些东西,有了新发现

转载自:http://wmnmtm.blog.163.com/blog/static/38245714201192211245389/

下载的一个源码里,实现了把264文件通过RTP发送,但是如果发送实时编码的怎么办,序列参数集和图像参数集得自己发送,因为264文件本身在文件开头已经存储了这些了。

x264编码得到的文件,在文件头部的一些东西,有了新发现 - 加菲 - 视频会议 - 加菲
(图一)这是一个正确编码得到的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编码得到的文件,在文件头部的一些东西,有了新发现 - 加菲 - 视频会议 - 加菲
 (图二)注释掉x264_sps_write( &h->out.bs, h->sps );的情况


x264编码得到的文件,在文件头部的一些东西,有了新发现 - 加菲 - 视频会议 - 加菲
(图三)注释掉x264_pps_write( &h->out.bs, h->pps );的情况


x264编码得到的文件,在文件头部的一些东西,有了新发现 - 加菲 - 视频会议 - 加菲

(图四)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( &param );它的返回值就是x264_t,这样就可以通过h->sps和h->pps,下面是我将它们单独写入文件的截图:
x264编码得到的文件,在文件头部的一些东西,有了新发现 - 加菲 - 视频会议 - 加菲
 sps_1
x264编码得到的文件,在文件头部的一些东西,有了新发现 - 加菲 - 视频会议 - 加菲
 sps_2
x264编码得到的文件,在文件头部的一些东西,有了新发现 - 加菲 - 视频会议 - 加菲
sps_3
 
上面是一个sps(sps1,sps_2,sps_3),看着内容好少啊。写入代码是:file1.Write(h->sps,sizeof(x264_sps_t));

x264编码得到的文件,在文件头部的一些东西,有了新发现 - 加菲 - 视频会议 - 加菲 
pps


PPS的内容也好少,可能是因为大多数均未指定,采用的是默认参数的原因吧,但是图像宽度和高度在哪儿了。

h->x264_param_t    param;
这个也很关键,因为我指定的都是在这个里,
param.i_width=320;
param.i_height=240;
把这个也保一份看看。

x264编码得到的文件,在文件头部的一些东西,有了新发现 - 加菲 - 视频会议 - 加菲
h->PARAM
param.i_width=320;
param.i_height=240;
这两个数字对应的在哪呢,应该有才对啊,找找。(其实已经有了,只是现在没找到,看后面)

后来换了个参数值,又试了一下
param.i_width=320;
param.i_height=240;
x264编码得到的文件,在文件头部的一些东西,有了新发现 - 加菲 - 视频会议 - 加菲
这是i_width 为320和100的比较,一直折腾半天,发现0x64能对应上十进制100,但是320怎么也对不上。
后来又从结构体的存储上找了半天(我直观的认为结构体中的变量字段是按顺序存在一块连续空间的,对不对不知道了)

 x264编码得到的文件,在文件头部的一些东西,有了新发现 - 加菲 - 视频会议 - 加菲
 
我分析,在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

x264编码得到的文件,在文件头部的一些东西,有了新发现 - 加菲 - 视频会议 - 加菲
 
x264编码得到的文件,在文件头部的一些东西,有了新发现 - 加菲 - 视频会议 - 加菲
 
x264编码得到的文件,在文件头部的一些东西,有了新发现 - 加菲 - 视频会议 - 加菲
 
x264编码得到的文件,在文件头部的一些东西,有了新发现 - 加菲 - 视频会议 - 加菲
 


这下就对了。



小结一下:
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可以吗

posted on 2013-01-23 23:23 杨粼波 阅读(1989) 评论(0)  编辑 收藏 引用


只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理