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,以下为我的学习心得,以供交流,整理的过程用到了网络上的好多总结,文内不再一一指明。
/
if( Parse( argc, argv, ¶m, &opt ) < 0 )
return -1; //分析参数,读入运行参数完成文件打开,此函数在 x264.c 中定义
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 ) );
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( 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. 进入编码帧
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++;
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: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 * 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;
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 )
{
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)
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( 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;
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( 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)
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 );
}
x264_nal_start( h, NAL_SPS, NAL_PRIORITY_HIGHEST );
x264_sps_write( &h->out.bs, h->sps );
x264_nal_end( h );
x264_nal_start( h, NAL_PPS, NAL_PRIORITY_HIGHEST );
x264_pps_write( &h->out.bs, h->pps );
x264_nal_end( h );
}
(5)
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.264_MPEG-4
2、第二阶段:
3、第三阶段:
然后你找到一个自己感兴趣的切入点,开始以此为中心研究这个问题。你研究问题的时候应该是联系测试模型来研究,这个时候你就需要仔细看代码中对这个问题的实现了。这个阶段我绝对支持你一行行代码跟踪,一个参数一个参数地跟踪。而代码中不懂的地方可能需要查标准。这时你再来看标准文档就有了针对性。也因为能将标准文档和代码对应起来,从而看标准文档也不觉得有太大困难,也能明白标准文档说的是什么问题,在测试模型中是如何通过代码实现的。在这个阶段中,会牵连到很多H.264的相关知识,这样通过以点带线,以线带面。会对H.264的内容认识越来越多。而你也就找到了自己的方向。
==========【注意事项】==========
1、切忌将代码和标准文档独立开看,否则,你的困难会很大。
2、对于刚开始接触H.264的人,切忌直接看代码和标准,哪怕是将标准和代码结合起来看,你也会不太顺利。换句话说:在没有了解H.264整体框架之前,你最好什么都不要做。
在VC6下编译X264
2008-12-05
1、下载x264
x264的主页:http://videolan.org/
x264是用版权控制工具CVS进行更新的,其主页上不提供源码压缩包,所以我们要用专用的工具下载。工具名:TortoiseSVN。下载地址:svn://svn.videolan.org/x264/trunk/
2、准备编译环境所需文件
1)
http://download.microsoft.com/download/vstudio60ent/SP5/Wideband-Full/WIN98Me/EN-US/vs6sp5.exe
2)
支持处理器多媒体汇编指令的补丁
3)
4)
3、设置编译环境
1)
2)
3)
4)
X264总结(1)
2009-01-30
(在实验室师妹的总结基础上整理出来,觉得有参考价值,所以贴了出来。)
X264是由法国巴黎中心学校的中心研究所于2004年6月发起,并由许多视频编码爱好者共同完成的项目。其目标是实现实用的264编码器,所以它引入MMX、SSE等汇编指令来提高编码速度,同时摒弃了一些耗时但对编码性能提高微小的模块,如多参考帧等。
注1:MMX:
注2:
数据的读入与存放:X264开辟了unused、next、current、refrence等区域分别保存未处理原始图片序列、即将编码帧序列、当前编码帧和参考帧序列,同时还开辟了fenc和fdec区域用于存放已编码帧和重构帧
视频编码层(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的类型进行操作,
X264_macroblock_chche_save(h)
计算mb
X264代码学习(一)
2008-12-07
今天是我开始学习X264代码的第一天,程序代码用的是x264-060412,以下为我的学习心得,以供交流,整理的过程用到了网络上的好多总结,文内不再一一指明。
/
}
其中,Parse(
argv[0]==
argv[1]
argv[2]==
argv[3]==
argv[4]==
这些参数的值可以由可以通过VC
_setmode(_fileno(stdin),
X264代码学习(二)
2008-12-07
atol(optarg)
对运行参数(“-o
int
{
}
然后,再次执行for循环,c
p_open_infile
int
{
}
X264代码学习(三)
2008-12-10
int
{
}
{
............
}
X264代码学习(四)
2009-01-27
1.
int
{
}
2.
void
{
}
3.
int64_t
{
#if
#else
#endif
}
4.
int
{
}
从文件中分别读取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:
//
static
{
}
X264代码学习(五)
2009-01-27
1.
static
{
}
该函数在frame.c中,原型为:
void
{
}
(4)
//若图像宽或高不是16的整数倍,调用
x264_frame_put(
static
{
}
(5)x264_slicetype_decide(
void
{
}
(6)
//将当前要编码的帧h->frames.current送给编码器,然后开始编码
(7)
(8)
TIMER_START(
//
(9)if(
//根据帧的类型初始化数据。总共有五种片的类型IDR,I,P,BREF,B
X264代码学习(六)
2009-01-28
(1)
//创建list0和list1(ref0和ref1),ref0从大到小,ref1从小到大
//x264_reference_build_list(
static
{
}
(2)
(3)
//
(4)
(5)
//
static
{
}
注1:for(
//循环22*18=396次对396个宏块进行编码,mb_xy是16*16宏块序号0~395,
//其赋值过程在x264_encoder_encode
//计算当前宏块垂直坐标(行坐标,以宏块为单位)
const
//计算当前宏块水平坐标(列坐标,以宏块为单位)
int
//在bs.h中,静态内联函数,如下:
static
x264_macroblock_cache_load(
//
x264_macroblock_analyse(
//对一个16*16块进行预测模式选择,通过比较得出最佳预测模式。
//
x264_macroblock_encode(
//
http://blog.sina.com.cn/s/blog_7420075e0100vp9t.html