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、下载x264x264的主页: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.asp3、设置编译环境1) 安装 Service Pack 5,自解压后,运行setupsp5.exe2) 安装 Visual C++ Processor Pack3) 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/
温馨提示
请用电脑打开本网页,即可以免费获取你想要的了。