x264学习笔记

3997
    


来源:
Licence:
联系:
平台:
环境:
大小:
更新:
标签:
免费下载 ×

下载APP,支持永久资源免费下载

限免产品服务请联系qq:1585269081

下载APP
免费下载 ×

下载APP,支持永久资源免费下载

下载APP 免费下载
下载 ×

下载APP,资源永久免费


如果出现不能下载的情况,请联系站长,联系方式在下方。

免费下载 ×

下载论文助手APP,资源永久免费

免费获取

如果你已经登录仍然出现不能下载的情况,请【点击刷新】本页面或者联系站长


H264学习指南  

2008-11-30 17:31:09|  分类: X264学习笔记 |  标签: |字号大中小 订阅

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 5 for Visual Studio 6 ,地址为:

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,以下为我的学习心得,以供交流,整理的过程用到了网络上的好多总结,文内不再一一指明。

//***************************************************************************
      //main:
      //****************************************************************************
int main( int argc, char **argv )   //其中argc是参数个数,argv表示入口参数地址
{
    x264_param_t param;   //x264_param_t为结构体的名字,见文件X264.h
    cli_opt_t opt;        //cli_opt_t为结构体的名字,见文件X264.c
  
#ifdef _MSC_VER
    _setmode(_fileno(stdin), _O_BINARY);
    _setmode(_fileno(stdout), _O_BINARY);
#endif

    x264_param_default( ¶m );   //对编码器的参数进行设定,此函数在common.c中定义,完成一个   x264_param_t 结构体的初始化。

    /* Parse command line */
    if( Parse( argc, argv, ¶m, &opt ) < 0 )
        return -1;                 //分析参数,读入运行参数完成文件打开,此函数在 x264.c 中定义

    /* Control-C handler */
    signal( SIGINT, SigIntHandler );

    return Encode( ¶m, &opt );  //开始编码,此函数在 x264.c 中定义。
}

 

其中,Parse( argc, argv, ¶m, &opt )功能是分析参数,读入运行参数完成文件打开。此函数在 x264.c 中定义。假设在VC的运行参数(Project--Setting--Debug--Project arguments)我们的设置为: “-o test.264 foreman.yuv 352x288”则具体到Parse函数其输入的参数 argc == 5 ,参数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( ¶m ) ------对编码器的参数进行设定和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) 将已开辟内存空间 s 的首 n 个字节的值设为值 c。

         x264_param_default( ¶m ) ------对编码器的参数进行设定, 包括cpu自动检测、视频序列参量初始化(均为默认值)、编码参量初始化。

        int c;    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值 即 c = 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 * 3 / 2 ));

     //计算总的帧数, 这里乘以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 == 0 || param->i_frame_total < i_frame_total )
          && param->i_frame_total > 0 )
           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 ) );    

    /* Create a copy of param */
    memcpy( &h->param, param, sizeof( x264_param_t ) );    //创建param的副本

             //memcpy原型:extern void *memcpy(void *dest, void *src, unsigned int count);

                  //用法:#include , 功能:由src所指内存区域复制count个字节到dest所指内存区域。
                  //说明:src和dest所指内存区域不能重叠,函数返回指向dest的指针。

    if( x264_validate_parameters( h ) < 0 )   //   函数x264_validate_parameters( h )在encoder.c中,

                                                                   //   功能为判断参数是否有效,并对不合适的参数进行修改

    {
        x264_free( h );
        return NULL;
    }

    if( h->param.psz_cqm_file )
        if( x264_cqm_parse_file( h, h->param.psz_cqm_file ) < 0 )
        {
            x264_free( h );
            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( 3 * i_width * i_height / 2 );
            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.  进入编码帧 /* Encode frames */
    for( i_frame = 0, i_file = 0, i_progress = 0;
         b_ctrl_c == 0 && (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++;

        /* update status line (up to 1000 times per input file) */
        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 == 0 ) )
        {
            int64_t i_elapsed = x264_mdate() - i_start;
            double fps = i_elapsed > 0 ? 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:%02d:%02d  \r",
                         i_frame, i_frame_total, (float)i_progress / 10, fps,
                         eta/3600, (eta/60)%60, eta%60 );
            }
            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 * 3 / 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 ) <= 0 )
        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;

    /* Do not force any parameters */
    if( pic )
    {
        pic->i_type = X264_TYPE_AUTO;
        pic->i_qpplus1 = 0;
    }
    if( x264_encoder_encode( h, &nal, &i_nal, pic, &pic_out ) < 0 )
    {
        fprintf( stderr, "x264_encoder_encode failed\n" );
    }

    for( i = 0; i < 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] ) ) > 0 )
        {
            i_file += p_write_nalu( hout, data, i_size );
        }
        else if( i_size < 0 )
        {
            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( i = 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 i = 0;
    while( list[i] ) i++;
    list[i] = frame;
}

(5)x264_slicetype_decide( h );      //判断slice的类型,在slicetype_decision.c中,如下:

void x264_slicetype_decide( x264_t *h )
{
    ...

    if( h->param.rc.b_stat_read )
    {
        /* Use the frame types from the first pass */
        for( i = 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( h );//x264_slicetype_analyse函数在slicetype_decision.c中。

        //这里要注意默认的x264里面是没有B帧的,如果需要用到B帧可以在最初的参数设置的时候,

        //用语句“--frames 数字”来定义,x264_slicetype_analyse 也只有在定义了“--frames 数字”时候,

        //才会执行。

    ...
}

(6)   /*  move some B-frames and 1 non-B to encode queue */

       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 )//程序中只有三种类型?

    /* reset ref pictures */
        x264_reference_reset( h );

        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; /* Not completely true but for now it is (as all I/P are kept as ref)*/
        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; /* Not completely true but for now it is (as all I/P are kept as ref)*/
        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; /* maybe add MMCO to forget it? -> low */
        i_slice_type = SLICE_TYPE_B;
    }
    else    /* B frame */
    {
        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) /* build ref list 0/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;

    /* build ref list 0/1, Order ref0 from higher to lower poc */
    ...

}

(2)  /* Init the rate control */
    x264_ratecontrol_start( h, i_slice_type, h->fenc->i_qpplus1 );
    i_global_qp = x264_ratecontrol_qp( h );

    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) /* Write SPS and PPS */
    if( i_nal_type == NAL_SLICE_IDR && h->param.b_repeat_headers )
    {
        if( h->fenc->i_frame == 0 )
        {
            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 );
    }

(5) /* Write frame */
    i_frame_size = x264_slices_write( h );

// encoder.c中,原型:

static inline int x264_slices_write( x264_t *h )
{
    ...
}

 关键部分为x264_slice_write( h );该函数定义在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( 8 * ( s->p - s->p_start ) + 8 - s->i_left );
       }

x264_macroblock_cache_load( h, i_mb_x, i_mb_y );

 //它是将要编码的宏块的周围宏块的值读进来。要想得到当前块的预测值,要先知道上面,左面的预测值。

// 分析参数选择合适的块编码模式,加载相邻块的信息。

x264_macroblock_analyse( h );

//对一个16*16块进行预测模式选择,通过比较得出最佳预测模式。

// 定义在analyse.c 中,计算sad值分析是否要对16*16的宏块进行分割和采用哪种分割方式合适。

x264_macroblock_encode( h );

// 根据前面分析得到的h->mb.i_type的类型,对一个16*16块进行编码。
http://lvshiqin.blog.163.com/blog/static/388204172009030114627300/

免费下载 ×

下载APP,支持永久资源免费下载

下载APP 免费下载
温馨提示
请用电脑打开本网页,即可以免费获取你想要的了。
扫描加我微信 ×

演示

×
登录 ×


下载 ×
论文助手网
论文助手,最开放的学术期刊平台
				暂无来源信息			 
回复
来来来,吐槽点啥吧

作者联系方式

×

向作者索要->