H264学习指南

来源:互联网 发布:java tomcat绝对路径 编辑:程序博客网 时间:2024/06/07 22:21
1、第一个阶段:
首先看《H.264_MPEG-4 Part 10 White Paper》,看完之后再看《Video coding using the H.264 MPEG-4 AVC compression standard》和《Halsted.Press.H.264.And.MPEG-4.Video.Compression.Video.Coding.For.Next.Generation.Multimedia.eBook-LiB》,然后可以抽空看《Overview of the H.264_AVC Video Coding Standard.pdf》。这几篇文章看完后,你应该对H.264的整体框架有个比较深入的了解了。前三篇文章可能需要花费你两~三周的时间。
2、第二阶段:
       看代码。这个时候你最常用的工具就是标准文档和测试模型。看代码也要先从整体框架入手。先搞懂H.264的整体框架在代码里是怎么分布的,一个功能模块的前伸模块和后继模块是什么。也就是搞清楚整个代码流程。这个阶段对标准文档的使用可能很少。
3、第三阶段:
然后你找到一个自己感兴趣的切入点,开始以此为中心研究这个问题。你研究问题的时候应该是联系测试模型来研究,这个时候你就需要仔细看代码中对这个问题的实现了。这个阶段我绝对支持你一行行代码跟踪,一个参数一个参数地跟踪。而代码中不懂的地方可能需要查标准。这时你再来看标准文档就有了针对性。也因为能将标准文档和代码对应起来,从而看标准文档也不觉得有太大困难,也能明白标准文档说的是什么问题,在测试模型中是如何通过代码实现的。在这个阶段中,会牵连到很多H.264的相关知识,这样通过以点带线,以线带面。会对H.264的内容认识越来越多。而你也就找到了自己的方向。
==========【注意事项】==========
1、切忌将代码和标准文档独立开看,否则,你的困难会很大。
2、对于刚开始接触H.264的人,切忌直接看代码和标准,哪怕是将标准和代码结合起来看,你也会不太顺利。换句话说:在没有了解H.264整体框架之前,你最好什么都不要做。
在VC6下编译X264   
2008-12-05 09:01:14|  分类: X264学习笔记  标签: |字号大中小 订阅 
1、下载x264
x264的主页:http://videolan.org/
x264是用版权控制工具CVS进行更新的,其主页上不提供源码压缩包,所以我们要用专用的工具下载。工具名:TortoiseSVN。下载地址:svn://svn.videolan.org/x264/trunk/ 
2、准备编译环境所需文件 
1) 下载Service Pack for Visual Studio ,地址为: 
http://download.microsoft.com/download/vstudio60ent/SP5/Wideband-Full/WIN98Me/EN-US/vs6sp5.exe 
2) 下载 Visual C++ 6.0 Processor Pack 
支持处理器多媒体汇编指令的补丁  http://msdn.microsoft.com/vstudio/downloads/tools/ppack/default.aspx 
3) NASM the famous Netwide Assembler http://sourceforge.net/projects/nasm/
4) DirectX 9.0 SDK http://msdn.microsoft.com/library/default.asp?url=/downloads/list/directx.asp
3、设置编译环境
1) 安装 Service Pack 5,自解压后,运行setupsp5.exe
2) 安装 Visual C++ Processor Pack
3) Installing and Configuring NASM,文件名一定要改成nasm.exe ,拷贝到Microsoft Visual Studio6\vc98\bin下即可
4) Installing and Configuring the DirectX SDK 
X264总结(1)  
2009-01-30 11:46:27|  分类: X264学习笔记  标签: |字号大中小 订阅 
(在实验室师妹的总结基础上整理出来,觉得有参考价值,所以贴了出来。)
X264是由法国巴黎中心学校的中心研究所于2004年6月发起,并由许多视频编码爱好者共同完成的项目。其目标是实现实用的264编码器,所以它引入MMX、SSE等汇编指令来提高编码速度,同时摒弃了一些耗时但对编码性能提高微小的模块,如多参考帧等。
注1:MMX: 是MultiMedia eXtensions(多媒体扩展)的缩写,是第六代CPU芯片的重要特点。MMX技术是在CPU中加入了特地为视频信号(Video Signal),音频信号(Audio Signal)以及图像处理(Graphical Manipulation)而设计的57条指令,因此,MMX CPU极大地提高了电脑的多媒体(如立体声、视频、三维动画等)处理功能。 
注2: SSE(Streaming SIMD Extensions)是英特尔在AMD的3D Now!发布一年之后,在其计算机芯片Pentium III中引入的指令集,是MMX的超集。它包括70条指令,其中包含单指令多数据浮点计算、以及额外的SIMD整数和高速缓存控制指令。其优势包括:更高分辨率的图像浏览和处理、高质量音频、MPEG2视频、同时MPEG2加解密;语音识别占用更少CPU资源;更高精度和更快响应速度。
     X264代码主要的部分分为三个步骤,即数据的读入与存放,视频编码层(VCL)的视频编码和网络提取层(Network Abstraction Layer,NAL)单元输出。  
数据的读入与存放:X264开辟了unused、next、current、refrence等区域分别保存未处理原始图片序列、即将编码帧序列、当前编码帧和参考帧序列,同时还开辟了fenc和fdec区域用于存放已编码帧和重构帧 。程序按以下顺序读入数据:首先,从YUV数据文件中读取数据存到临时变量pic_in,同时为unused开辟存储空间,并用fenc指针指向这个空间。接着,将pic_in中的图片数据拷贝到fenc所指向的区域,并在拷贝完成后对图片大小进行判断,如果长宽不为16的整数倍则进行像素扩展;将处理后的fenc区域数据放入next区域。之后,如果存在B帧,则从next区域取出B帧以后的P帧放到current区域中,也就是说先编码I、P帧再编码之间的B帧;否则,直接从next区域取出一帧存入current区域。此时current区域中存放的就是已经过预处理的即将要编码的帧数据了。最后,由于fenc区域是编码的直接对象,再将current区域中的内容拷贝到fenc中正式开始编码 。 
视频编码层(VCL)的视频编码:输入文件foreman.yuv格式是CIF,即352*288。在对一帧图像进行处理的过程中以宏块(16*16)为单位,一帧图像处理步骤如下:
定位当前处理的16*16宏块的位置;
X264_macroblock_chche_load(h,i_mb_x,I,mb_y) 
X264_macroblock_analyse(h);X264_macroblock_encode(h) 
根据h->mb.i_type的类型进行操作, 如果是I帧,则x264_macroblock_write_caclc(h,&h->out.bs); 
X264_macroblock_chche_save(h) 
计算mb stats.

X264代码学习(一)  
2008-12-07 08:30:21|  分类: X264学习笔记  标签: |字号大中小 订阅 
今天是我开始学习X264代码的第一天,程序代码用的是x264-060412,以下为我的学习心得,以供交流,整理的过程用到了网络上的好多总结,文内不再一一指明。
/
    if( Parse( argc, argv, &param, &opt )
        return -1;                 //分析参数,读入运行参数完成文件打开,此函数在 x264.c 中定义 
    
    signal( SIGINT, SigIntHandler ); 
    return Encode( &param, &opt );  //开始编码,此函数在 x264.c 中定义。
}
 
其中,Parse( argc, argv, &param, &opt )功能是分析参数,读入运行参数完成文件打开。此函数在 x264.c 中定义。假设在VC的运行参数(Project--Setting--Debug--Project arguments)我们的设置为: “-o test.264 foreman.yuv 352x288”则具体到Parse函数其输入的参数 argc == ,参数argc和argv在进入main 函数之前就已经确定了。argv 为字符串char **的类型的,相当于一个二维数组,其具体内容如下:
argv[0]== “D:\x264-060412\build\win32\bin\x264.exe”;
argv[1] == “-o” 
argv[2]== “test.264” 
argv[3]== “foreman.yuv”
argv[4]== “352x288”.
这些参数的值可以由可以通过VC 的Debug工具察看,foreman.yuv为yuv图像序列,test.264为输出的264格式文件。
 
_setmode(_fileno(stdin), _O_BINARY)功能是将stdin流(或其他文件流)从文本模式   <--切换-->   二进制模式 就是stdin流(或其他文件流)从文本模式   <--切换-->   二进制模式 
X264代码学习(二)  
2008-12-07 11:16:23|  分类: X264学习笔记  标签: |字号大中小 订阅 
        本文主要分析函数x264_param_default( &param ------对编码器的参数进行设定和Parse( int argc, char **argv, x264_param_t *param, cli_opt_t *opt )------为分析参数,读入运行参数。
          memset( opt, 0, sizeof(cli_opt_t) ),函数元型为void *memset(void *s,int c,size_t n) 将已开辟内存空间 的首 个字节的值设为值 c。
         x264_param_default( &param ------对编码器的参数进行设定, 包括cpu自动检测、视频序列参量初始化(均为默认值)、编码参量初始化。
        int c;    getopt_long( argc, argv, "hi:I:b:r:cxB:q:f:o:A:m:p:t:vw8", long_options, &long_options_index);
 getopt_long() 解析入口地址的向量,最后c 得到的是 运行参数(“-o test.264 foreman.yuv  352x288”)中前面“-o”中“o”的ASCII值 即 111 。可通过VC Debug查看。 getopt_long() 定义在getopt.c中。其中用到 getopt_internal(nargc, nargv, options)也定义在getopt.c中,解析入口地址向量。
atol(optarg) 把string转为long类型,atoi(optarg)类似,参见MSDN. 
 函数定义:int strncasecmp(const char *s1, const char *s2, size_t n) 用来比较参数s1和s2字符串前n个字符,比较时会自动忽略大小写的差异, 若参数s1和s2字符串相同则返回0; s1若大于s2则返回大于0的值; s1若小于s2则返回小于0的值.
对运行参数(“-o test.264 foreman.yuv  352x288”)由c 111 ,程序跳转到case 'o',执行p_open_outfile optarg, &opt->hout ),即进入函数open_file_bsf(),功能为以二进制写的方式打开输出文件test.264,函数在nuxers.c中,原型如下:
int open_file_bsf( char *psz_filename, hnd_t *p_handle )
{
    if ((*p_handle fopen(psz_filename, "w+b")) == NULL)
        return -1;
    return 0;
}
然后,再次执行for循环,c getopt_long( ),得到c=-1, 跳出循环。由psz_filename argv[optind++]获取输入文件名foreman.yuv,得到文件的尺寸,判断文件格式为avi还是avs还是y4m,如果都不是前面的格式并且输入图片高度或宽度为0,则输出帮助文件。 由函数p_open_infile( psz_filename, &opt->hin, param )打开输入文件。
p_open_infile open_file_yuv,即进入函数open_file_yuv(),功能为以二进制读的方式打开输入文件foreman.yuv, 函数在nuxers.c中,原型如下:
int open_file_yuv( char *psz_filename, hnd_t *p_handle, x264_param_t *p_param )
{
    yuv_input_t *h malloc(sizeof(yuv_input_t));
    h->width p_param->i_width;
    h->height p_param->i_height;
    h->next_frame 0;
    if( !strcmp(psz_filename, "-") )
        h->fh stdin;
    else
        h->fh fopen(psz_filename, "rb");
    if( h->fh == NULL )
        return -1;
    *p_handle (hnd_t)h;
    return 0;
}
X264代码学习(三)  
2008-12-10 15:34:17|  分类: X264学习笔记  标签: |字号大中小 订阅 
          signal( SIGINT, SigIntHandler );
          SIGINT是信号名称或代码(Ctrl-C会产生这个信号), SigIntHandler是编写的一段程序,  singal(SIGINT,   SigIntHandler) 就是捕获SIGINT(中断)信号,当键入Ctrl-C的时候,当前执行程序调用指针函数SigIntHandler执行完后,再返回原来执行的地方接着往下走。   
         static int  Encode( x264_param_t *param, cli_opt_t *opt )在X264.c中,开始编码。
      1.   语句i_frame_total p_get_frame_total( opt->hin ),实现得到输入文件的总帧数,由于p_get_frame_total get_frame_total_yuv(见Parse()函数),所以调用函数int get_frame_total_yuv( hnd_t handle ),在文件muxers.c中,原型如下:
int get_frame_total_yuv( hnd_t handle )
{
    yuv_input_t *h handle;
    int i_frame_total 0;
    if( !fseek( h->fh, 0, SEEK_END )
    {
        uint64_t i_size ftell( h->fh );                           //计算出文件的大小
        fseek( h->fh, 0, SEEK_SET );                           //作用是将文件指针放到文件开始位置
        i_frame_total (int)(i_size h->width h->height ));
     //计算总的帧数, 这里乘以1.5是因为一个编码单位是一个亮度块加2个色度块,大小上等于1.5个亮度块
    }
    return i_frame_total;
}
    其中fseek中第一个参数为文件指针,第二个参数为偏移量,起始位置,SEEK_END 表示文件尾,SEEK_SET 表示文件头。fseek作用是将定位文件指针,用于随机访问。ftell函数返回h->fh的指针偏移位值. 
    2.    i_frame_total -= opt->i_seek;
           if( i_frame_total == || param->i_frame_total i_frame_total )
          && param->i_frame_total )
           i_frame_total param->i_frame_total;
          param->i_frame_total i_frame_total;
          //获取要求编码的帧数param->i_frame_total。
     3.   x264_t *x264_encoder_open   x264_param_t *param ), 在文件encoder.c中,对不正确的264_t结构体(h的类型是264_t )参数进行修改,并对各结构体参数、编码、预测等需要的参数进行初始化。
  x264_t *x264_encoder_open   x264_param_t *param )
{
    x264_t *h x264_malloc( sizeof( x264_t );   //分配空间并进行初始化,x264_malloc( )在common.c中。
    int i;
    memset( h, 0, sizeof( x264_t );    
    
    memcpy( &h->param, param, sizeof( x264_param_t );    //创建param的副本
             //memcpy原型:extern void *memcpy(void *dest, void *src, unsigned int count);
                  //用法:#include <string.h>, 功能:由src所指内存区域复制count个字节到dest所指内存区域。
                  //说明:src和dest所指内存区域不能重叠,函数返回指向dest的指针。
    if( x264_validate_parameters(   //   函数x264_validate_parameters( )在encoder.c中,
                                                                   //   功能为判断参数是否有效,并对不合适的参数进行修改
    {
        x264_free( );
        return NULL;
    }
    if( h->param.psz_cqm_file )
        if( x264_cqm_parse_file( h, h->param.psz_cqm_file )
        {
            x264_free( );
            return NULL;
        }
............
}
X264代码学习(四)  
2009-01-27 10:54:24|  分类: X264学习笔记  标签: |字号大中小 订阅 
1. if( p_set_outfile_param( opt->hout, param  // p_set_outfile_param set_param_bsf 判断输出文件
    {
        fprintf( stderr, "can't set outfile param\n" );
        p_close_infile( opt->hin );
        p_close_outfile( opt->hout );
        return -1;
    }
 p_set_outfile_param set_param_bsf 在muxers.c中,函数原型为:
int set_param_bsf( hnd_t handle, x264_param_t *p_param )
{
    return 0;
}
2.  x264_picture_alloc( &pic, X264_CSP_I420, param->i_width, param->i_height );
                                                                   //构造一个图像帧的初始化空间,在common.c中,函数原型为:
void x264_picture_alloc( x264_picture_t *pic, int i_csp, int i_width, int i_height )
{
    pic->i_type X264_TYPE_AUTO;
    pic->i_qpplus1 0;
    pic->img.i_csp i_csp;
    switch( i_csp X264_CSP_MASK )
    {
        case X264_CSP_I420:
        case X264_CSP_YV12:
            pic->img.i_plane 3;
            pic->img.plane[0] x264_malloc( i_width i_height );
            pic->img.plane[1] pic->img.plane[0] i_width i_height;
            pic->img.plane[2] pic->img.plane[1] i_width i_height 4;
            pic->img.i_stride[0] i_width;
            pic->img.i_stride[1] i_width 2;
            pic->img.i_stride[2] i_width 2;
            break;
        case X264_CSP_I422:
            ...
    }
}
3. i_start x264_mdate();  //用于编码用时的计算,设定起始时间,在 mdate.c中,函数原型为:
int64_t x264_mdate( void )
{
#if !(defined(_MSC_VER) || defined(__MINGW32__))
    struct timeval tv_date;
    gettimeofday( &tv_date, NULL );
    return( (int64_t) tv_date.tv_sec 1000000 (int64_t) tv_date.tv_usec );
#else
    struct _timeb tb;
    _ftime(&tb);
    return ((int64_t)tb.time (1000) (int64_t)tb.millitm) (1000);
#endif
  
4.  进入编码帧 
    for( i_frame 0, i_file 0, i_progress 0;
         b_ctrl_c == && (i_frame i_frame_total || i_frame_total == 0); )
    {
        if( p_read_frame( &pic, opt->hin, i_frame opt->i_seek )//读取
            break;
       //p_read_frame() 按照h->hin提供的输入文件的地址,读入图像的内容到&pic提供的存储区的首地址
        pic.i_pts (int64_t)i_frame param->i_fps_den;
        i_file += Encode_frame( h, opt->hout, &pic );//编码并保存,
                                               //Encode_frame( h, opt->hout, &pic )实现编码,是x264的核心部分
        i_frame++;
        
        if( opt->b_progress && param->i_log_level X264_LOG_DEBUG && 
            i_frame_total i_frame 1000 i_frame_total i_progress
                            i_frame 10 == )
        {
            int64_t i_elapsed x264_mdate() i_start;
            double fps i_elapsed i_frame 1000000. i_elapsed 0;
            if( i_frame_total )
            {
                int eta i_elapsed (i_frame_total i_frame) ((int64_t)i_frame 1000000);
                i_progress i_frame 1000 i_frame_total;
                fprintf( stderr, "encoded frames: %d/%d (%.1f%%), %.2f fps, eta %d:d:d  \r",
                         i_frame, i_frame_total, (float)i_progress 10, fps,
                         eta/3600, (eta/60)`, eta` );
            }
            else
                fprintf( stderr, "encoded frames: %d, %.2f fps   \r", i_frame, fps );
            fflush( stderr ); // needed in windows
        }
    }
      注1: //在本文环境下,p_read_frame=read_frame_yuv,read_frame_yuv()定义在muxers.c中,原型为:
int read_frame_yuv( x264_picture_t *p_pic, hnd_t handle, int i_frame )
{
    yuv_input_t *h handle;
    if( i_frame != h->next_frame )
        if( fseek( h->fh, (uint64_t)i_frame h->width h->height 2, SEEK_SET )
            return -1;
    if( fread( p_pic->img.plane[0], 1, h->width h->height, h->fh <= 0
            || fread( p_pic->img.plane[1], 1, h->width h->height 4, h->fh <= 0
            || fread( p_pic->img.plane[2], 1, h->width h->height 4, h->fh <= )
        return -1;
    h->next_frame i_frame+1;
    return 0;
}
从文件中分别读取288*352(亮度信息),144*176(Cr),144*176(Cb)的数据放入p_pic->img.plane[0],p_pic->img.plane[1],p_pic->img.plane[2],如果成功则继续执行一下程序;如果不成功则打断程序,返回-1.
注2: i_file += Encode_frame( h, opt->hout, &pic );//编码并保存,
                      //Encode_frame( h, opt->hout, &pic )实现编码,是x264的核心部分,在X264.c中,
              //这个函数主要是调用了 x264_encoder_encode( h, &nal, &i_nal, pic, &pic_out 来实现编码。 
// 原型为: 
static int  Encode_frame( x264_t *h, hnd_t hout, x264_picture_t *pic )
{
    x264_picture_t pic_out;
    x264_nal_t *nal;
    int i_nal, i;
    int i_file 0;
    
    if( pic )
    {
        pic->i_type X264_TYPE_AUTO;
        pic->i_qpplus1 0;
    }
    if( x264_encoder_encode( h, &nal, &i_nal, pic, &pic_out )
    {
        fprintf( stderr, "x264_encoder_encode failed\n" );
    }
    for( 0; i_nal; i++ )
    {
        int i_size;
        int i_data;
        i_data DATA_MAX;
        if( i_size x264_nal_encode( data, &i_data, 1, &nal[i] )
        {
            i_file += p_write_nalu( hout, data, i_size );
        }
        else if( i_size )
        {
            fprintf( stderr, "need to increase buffer size (size=%d)\n", -i_size );
        }
    }
    if (i_nal)
        p_set_eop( hout, &pic_out );
    return i_file;
}
X264代码学习(五)  
2009-01-27 16:33:28|  分类: X264学习笔记  标签: |字号大中小 订阅 
1. x264_encoder_encode( x264_t *h,  x264_nal_t **pp_nal, int *pi_nal, 
                                           x264_picture_t *pic_in,   x264_picture_t *pic_out          //encoder.c中
  (1)x264_frame_t   *frame_psnr h->fdec; // just to keep the current decoded frame for psnr calculation 
    (2)  x264_frame_t *fenc x264_frame_get( h->frames.unused );//返回h->frames.unused[0]的值,即得到了一幀图像给编码器fenc, x264_frame_get函数在encoder.c中,原型为:
static x264_frame_t *x264_frame_get( x264_frame_t *list[X264_BFRAME_MAX+1] )
{
    x264_frame_t *frame list[0];
    int i;
    for( 0; list[i]; i++ )
        list[i] list[i+1];
    return frame;
}
    (3)  x264_frame_copy_picture( h, fenc, pic_in );   //  pic_in复制到 fenc ,把要编码的帧存放到 fenc
该函数在frame.c中,原型为:
void x264_frame_copy_picture( x264_t *h, x264_frame_t *dst, x264_picture_t *src )
{
    dst->i_type     src->i_type;
    dst->i_qpplus1  src->i_qpplus1;
    dst->i_pts      src->i_pts;
    switch( src->img.i_csp X264_CSP_MASK )
    {
        case X264_CSP_I420:
            h->csp.i420( dst, &src->img, h->param.i_width, h->param.i_height );
            break;
        ....
        default:
            x264_log( h, X264_LOG_ERROR, "Arg invalid CSP\n" );
            break;
    }
}
(4) if( h->param.i_width 16 || h->param.i_height 16      x264_frame_expand_border_mod16( h, fenc );
      fenc->i_frame h->frames.i_input++;
//若图像宽或高不是16的整数倍,调用 x264_frame_expand_border_mod16( h, fenc );
x264_frame_put( h->frames.next, fenc );//将fenc拷贝给h->frames.next数组中第一个不为0的位置,该函数在encoder.c中,原型为:
static void x264_frame_put( x264_frame_t *list[X264_BFRAME_MAX], x264_frame_t *frame )
{
    int 0;
    while( list[i] i++;
    list[i] frame;
}
(5)x264_slicetype_decide( );      //判断slice的类型,在slicetype_decision.c中,如下:
void x264_slicetype_decide( x264_t *h )
{
    ...
    if( h->param.rc.b_stat_read )
    {
        
        for( 0; h->frames.next[i] != NULL; i++ )
            h->frames.next[i]->i_type =
                x264_ratecontrol_slice_type( h, h->frames.next[i]->i_frame );
    }
    else if( h->param.i_bframe && h->param.b_bframe_adaptive )
        x264_slicetype_analyse( );//x264_slicetype_analyse函数在slicetype_decision.c中。
        //这里要注意默认的x264里面是没有B帧的,如果需要用到B帧可以在最初的参数设置的时候,
        //用语句“--frames 数字”来定义,x264_slicetype_analyse 也只有在定义了“--frames 数字”时候,
        //才会执行。
    ...
}
(6)   
       while( IS_X264_TYPE_B( h->frames.next[bframes]->i_type )
            bframes++;
        x264_frame_put( h->frames.current, x264_frame_get( &h->frames.next[bframes] );
//将当前要编码的帧h->frames.current送给编码器,然后开始编码
(7)  h->fenc x264_frame_get( h->frames.current );//将当前要编码的帧给编码器
(8)   if( h->fenc->i_type == X264_TYPE_IDR )
    {
        h->frames.i_last_idr h->fenc->i_frame;
    }
TIMER_START( i_mtime_encode_frame );
// 如果是解码即时刷新片,则h->frames.i_last_idr h->fenc->i_frame;然后计时开
(9)if( h->fenc->i_type == X264_TYPE_IDR )
    {
        
        x264_reference_reset( );
        i_nal_type    NAL_SLICE_IDR;
        i_nal_ref_idc NAL_PRIORITY_HIGHEST;
        i_slice_type SLICE_TYPE_I;
    }
    else if( h->fenc->i_type == X264_TYPE_I )
    {
        i_nal_type    NAL_SLICE;
        i_nal_ref_idc NAL_PRIORITY_HIGH; 
        i_slice_type SLICE_TYPE_I;
    }
    else if( h->fenc->i_type == X264_TYPE_P )
    {
        i_nal_type    NAL_SLICE;
        i_nal_ref_idc NAL_PRIORITY_HIGH; 
        i_slice_type SLICE_TYPE_P;
    }
    else if( h->fenc->i_type == X264_TYPE_BREF )
    {
        i_nal_type    NAL_SLICE;
        i_nal_ref_idc NAL_PRIORITY_HIGH; 
        i_slice_type SLICE_TYPE_B;
    }
    else    
    {
        i_nal_type    NAL_SLICE;
        i_nal_ref_idc NAL_PRIORITY_DISPOSABLE;
        i_slice_type SLICE_TYPE_B;
    }
//根据帧的类型初始化数据。总共有五种片的类型IDR,I,P,BREF,B 。如果是解码即时刷新片(idr),则要对参考帧重新设置
 
X264代码学习(六)  
2009-01-28 10:44:34|  分类: X264学习笔记  标签: |字号大中小 订阅 
(1) 
          x264_reference_build_list( h, h->fdec->i_poc, i_slice_type );
//创建list0和list1(ref0和ref1),ref0从大到小,ref1从小到大
//x264_reference_build_list( h, h->fdec->i_poc, i_slice_type )定义在encoder.c中,如下:
static inline void x264_reference_build_list( x264_t *h, int i_poc, int i_slice_type )
{
    int i;
    int b_ok;
    
    ...
}
(2)  
    x264_ratecontrol_start( h, i_slice_type, h->fenc->i_qpplus1 );
    i_global_qp x264_ratecontrol_qp( );
    pic_out->i_qpplus1 h->fdec->i_qpplus1 i_global_qp 1;
    // 初始化速率和量化步长
(3) x264_slice_init( h, i_nal_type, i_slice_type, i_global_qp );
// 创建片头,片头初始化;
(4) 
    if( i_nal_type == NAL_SLICE_IDR && h->param.b_repeat_headers )
    {
        if( h->fenc->i_frame == )
        {
            x264_nal_start( h, NAL_SEI, NAL_PRIORITY_DISPOSABLE );
            x264_sei_version_write( h, &h->out.bs );
            x264_nal_end( );
        }
        
        x264_nal_start( h, NAL_SPS, NAL_PRIORITY_HIGHEST );
        x264_sps_write( &h->out.bs, h->sps );
        x264_nal_end( );
        
        x264_nal_start( h, NAL_PPS, NAL_PRIORITY_HIGHEST );
        x264_pps_write( &h->out.bs, h->pps );
        x264_nal_end( );
    }
(5) 
    i_frame_size x264_slices_write( );
// encoder.c中,原型:
static inline int x264_slices_write( x264_t *h )
{
    ...
}
 关键部分为x264_slice_write( );该函数定义在encoder.c中,实现一帧的编码。
注1:for( mb_xy h->sh.i_first_mb, i_skip 0; mb_xy h->sh.i_last_mb; mb_xy++ )
//循环22*18=396次对396个宏块进行编码,mb_xy是16*16宏块序号0~395, 
//其赋值过程在x264_encoder_encode 中的x264_slice_init里。 
 const int i_mb_y mb_xy h->sps->i_mb_width;  
//计算当前宏块垂直坐标(行坐标,以宏块为单位)  i_mb_width=22        
const int i_mb_x mb_xy h->sps->i_mb_width;
//计算当前宏块水平坐标(列坐标,以宏块为单位) 
int mb_spos bs_pos(&h->out.bs);
//在bs.h中,静态内联函数,如下:
static inline int bs_pos( bs_t *s )
       {
         return( s->p s->p_start s->i_left );
       }
x264_macroblock_cache_load( h, i_mb_x, i_mb_y );
 //它是将要编码的宏块的周围宏块的值读进来。要想得到当前块的预测值,要先知道上面,左面的预测值。 
// 分析参数选择合适的块编码模式,加载相邻块的信息。
x264_macroblock_analyse( );
//对一个16*16块进行预测模式选择,通过比较得出最佳预测模式。 
// 定义在analyse.c 中,计算sad值分析是否要对16*16的宏块进行分割和采用哪种分割方式合适。 
x264_macroblock_encode( ); 

// 根据前面分析得到的h->mb.i_type的类型,对一个16*16块进行编码。


http://blog.sina.com.cn/s/blog_7420075e0100vp9t.html

0 0
原创粉丝点击