收藏 分享(赏)

从T264代码看帧内预测.doc

上传人:HR专家 文档编号:5937073 上传时间:2019-03-21 格式:DOC 页数:21 大小:104.50KB
下载 相关 举报
从T264代码看帧内预测.doc_第1页
第1页 / 共21页
从T264代码看帧内预测.doc_第2页
第2页 / 共21页
从T264代码看帧内预测.doc_第3页
第3页 / 共21页
从T264代码看帧内预测.doc_第4页
第4页 / 共21页
从T264代码看帧内预测.doc_第5页
第5页 / 共21页
点击查看更多>>
资源描述

1、从 T264 代码看帧内预测越来越感觉到这种看标准和理解代码的方法的好处了,以前单看标准再单看 x264都没甚么成果。现在一部分一部分的看代码和标准,虽然还有很多需要贯通起来,但是想一想已经了解一些了。或许前面就是个铺垫,又怎么会知道呢。毕竟以前不会再重来,未来还是需要自己去尝试的。或许过了几个月我又感觉到另外一种方法的好呢,这都不管了,现在我就是乐此不疲了!首先看一组 T264中的定义:/*16x16模式的亮度4 种预测 */void T264_predict_16x16_mode_0_c (uint8_t* dst, int32_t dst_stride, uint8_t* top, ui

2、nt8_t* left);void T264_predict_16x16_mode_1_c (uint8_t* dst, int32_t dst_stride, uint8_t* top, uint8_t* left);void T264_predict_16x16_mode_2_c (uint8_t* dst, int32_t dst_stride, uint8_t* top, uint8_t* left);void T264_predict_16x16_mode_20_c(uint8_t* dst, int32_t dst_stride, uint8_t* top, uint8_t* le

3、ft);void T264_predict_16x16_mode_21_c(uint8_t* dst, int32_t dst_stride, uint8_t* top, uint8_t* left);void T264_predict_16x16_mode_22_c(uint8_t* dst, int32_t dst_stride, uint8_t* top, uint8_t* left);void T264_predict_16x16_mode_3_c (uint8_t* dst, int32_t dst_stride, uint8_t* top, uint8_t* left);/ 亮度预

4、测的精确模式,一个9种4x4 luma (6 functions)void T264_predict_4x4_mode_0_c(uint8_t* dst, int32_t dst_stride, uint8_t* top, uint8_t* left);void T264_predict_4x4_mode_1_c(uint8_t* dst, int32_t dst_stride, uint8_t* top, uint8_t* left);void T264_predict_4x4_mode_2_c(uint8_t* dst, int32_t dst_stride, uint8_t* top,

5、uint8_t* left);void T264_predict_4x4_mode_20_c(uint8_t* dst, int32_t dst_stride, uint8_t* top, uint8_t* left);void T264_predict_4x4_mode_21_c(uint8_t* dst, int32_t dst_stride, uint8_t* top, uint8_t* left);void T264_predict_4x4_mode_22_c(uint8_t* dst, int32_t dst_stride, uint8_t* top, uint8_t* left);

6、void T264_predict_4x4_mode_3_c(uint8_t* dst, int32_t dst_stride, uint8_t* top, uint8_t* left); void T264_predict_4x4_mode_4_c(uint8_t* dst, int32_t dst_stride, uint8_t* top, uint8_t* left);void T264_predict_4x4_mode_5_c(uint8_t* dst, int32_t dst_stride, uint8_t* top, uint8_t* left);void T264_predict

7、_4x4_mode_6_c(uint8_t* dst, int32_t dst_stride, uint8_t* top, uint8_t* left);void T264_predict_4x4_mode_7_c(uint8_t* dst, int32_t dst_stride, uint8_t* top, uint8_t* left);void T264_predict_4x4_mode_8_c(uint8_t* dst, int32_t dst_stride, uint8_t* top, uint8_t* left);/色度模式的4种预测/ 8x8 chroma (7 functions

8、)void T264_predict_8x8_mode_0_c (uint8_t* dst, int32_t dst_stride, uint8_t* top, uint8_t* left);void T264_predict_8x8_mode_1_c (uint8_t* dst, int32_t dst_stride, uint8_t* top, uint8_t* left);void T264_predict_8x8_mode_2_c (uint8_t* dst, int32_t dst_stride, uint8_t* top, uint8_t* left);void T264_pred

9、ict_8x8_mode_20_c(uint8_t* dst, int32_t dst_stride, uint8_t* top, uint8_t* left);void T264_predict_8x8_mode_21_c(uint8_t* dst, int32_t dst_stride, uint8_t* top, uint8_t* left);void T264_predict_8x8_mode_22_c(uint8_t* dst, int32_t dst_stride, uint8_t* top, uint8_t* left);void T264_predict_8x8_mode_3_

10、c (uint8_t* dst, int32_t dst_stride, uint8_t* top, uint8_t* left); 上面的注释的几种是对应的标准中的说明,可以进行参照。而进行实现(T264)中的函数就变得多了,但是类型还是一样的,比如在亮度16x16 的 20、21、22 就是对应的一个类型的预测,就是16x16中的所谓第二种 DC 预测模式。说起这个类型,在其他两个块中也是有的,就其说明来看,是很牛的:Mode 2 (DC prediction)is modified depending on which samples AM have previously been co

11、ded; each of the othermodes may only be used if all of the required prediction samples are available。模式2(DC 预测):取决于 A-M 中在前面已经编码的采样进行修正,而其他的模式只能在其需要的象素全部可得的时候才可以用。这样看,好像其是很厉害的,用了甚么方法可以使得其不用要求固定象素可得来预测呢?其实这样也太为难人家了,谁也不能在甚么都不知道的情况下,做个精确的预测,而模式2其实就是一种很简单的处理,是在象素不可得的时候的一种很无赖的处理:牺牲了精确性。看代码:/ none availab

12、le 注意:全不可得/voidT264_predict_16x16_mode_22_c(uint8_t* dst, int32_t dst_stride, uint8_t* top, uint8_t* left)int32_t i, j;for(i = 0 ; i 2;*(cur_dst + 8) = *(cur_dst + 13) = (left2 + (left1 2;*(cur_dst + 4) = *(cur_dst + 9) = *(cur_dst + 14) = (left1 + (left0 2;*(cur_dst) = *(cur_dst + 5) = *(cur_dst +

13、10) = *(cur_dst + 15) = (left0 + (*(left - 1) 2;*(cur_dst + 1) = *(cur_dst + 6) = *(cur_dst + 11) = (*(top - 1) + (top0 2;*(cur_dst + 2) = *(cur_dst + 7) = (top0 + (top1 2;*(cur_dst + 3) = (top1 + (top2 2; 这是在左和上都可得的情况下进行的4x4亮度第四种模式预测:下右。从参数开始看,dst肯定是目的象素地址了;dst_-stride 目的象素每两行之间的距离(算完一行后,加上这个就可以到下一

14、行,这段代码没用这个参数) ;top 和 left 就是上面和左面的象素了(就是标准种的 A-M,而这里用的到的就是 ABCD,IJKLM,分别是 top0-3,left0-3,而 M 既是top-1,也是 left-1,可对应代码看) 。用方块的画法看差值不是很明确,用点的方法就很好了。 。 。 。 。 x x x x。 x x x x。 x x x x。 x x x x从左上到下右方向45度画斜线,就可以这样分组(目的象素按行分别为0-15 )0、5 、10 、15为一组;1 、 6、11 为一组;2 、7为一组;3 为一组;4、9、14为一组;8、13为一组;12为一组。同组内的象素使用

15、相同的象素点来预测。以0这组为例,画线表示该组象素和 top0,left0,left-1(top-1)有关,那么就是这三个象素计算得到,很明显left-1(top-1)的关系重大一些,那么就是权重大就表示了。最后结果就是round(left0/4+left-1/2+top0/4),对比代码可以更为明确。而其他象素的计算也是类似的,就是中间的象素权重大,表示相关性高。其他模式也是类似的方法,都是对应可得象素的情况来决定预测方式。这就是帧内预测了。T264主线dct 之前 就要进入公司工作了,要接收眭师兄的编码工作,一直对 H264和平台使用的 T264的对应关系不是很清楚,所知道的就是很有限的几

16、个算法,总体的概念还需要加强。这几天终于把前面的部分整合起来了。所用的 t264的版本是师兄们改过的,对原始的版本进行了一定程度的简化。但是总体结构还是一样的。进入 mian 函数,在直接看主要的 return encode(); len = C264enc_Frame(t, buf, dst );- len = T264_encode_frame(t, nal_pos, nal_num, (uint8_t*)dst, dst_size );这就到了按帧编码的部分了,在这个函数中主要看一个大循环。前面的很多初始化我们可以先不要浪费过多精力。虽然这个在整个看代码过程中是最头痛的,太多的结构体,太

17、多的出错异常等处理,再加上 H264本身的各种处理很容易让人眼花缭乱。个人认为前面的初始化要结合后面的函数调用来看就明确了,首先对 H264的数据分层结构有个大概的了解就看主线,这样就会让人有成就感。而且在后面看到的重要函数在回去看初始化就知道前面用结构体,复杂的结构体来初始化以及各种处理是多么的明智了(这也可能有更好的办法) 。那么我们就先看这个函数中的大循环吧。for(i = 0 ; i mb_height ; i +)/从第一行到最后一行,分宏块 T264_mb_load_context_all(t, i, t-mb_width);/加载预测信息,为后面的预测编码做准备/*QueryPe

18、rformanceCounter(*/t1 = CLK_getltime();for(k = 0 ; k mb_width ; k +)/从第一列到最后一列宏块初始化T264_mb_mode_decision_all(t, k, skip_thrsh);/预测模式选择/T264_mb_save_context_ME(t, k);T264_mb_save_context_all(t, k); if( k != t-mb_width-1)T264_mb_reset_context_all(t, k+1);t-sad_all += t-mb_linek.sad;/QueryPerformanceCo

19、unter(/pred_time += t4.LowPart - t3.LowPart;/t2 = CLK_getltime();/pred_time += t2-t1;for(k = 0 ; k mb_width; k +)T264_mb_encode(t, k); /宏块编码,从第一列到最后一列/QueryPerformanceCounter(/encode_time += t1.LowPart - t4.LowPart;/t3 = CLK_getltime();/encode_time +=t3-t2;for(k = 0 ; k mb_width ; k +) T264_mb_encod

20、e_post(t, k);/* save the rec frame */T264_mb_save_context_all(t,k); 循环并未完成,但是主要的处理已经结束了,后面的就是对编码以后的处理了。当编码一个宏块后,它的信息就要进入预测的范围内,就是来更新预测集里面的宏块这样的处理。这部分的主要注释见上。那么现在就进入 T264_mb_encode(t, k);函数意义:对 t 帧的第 k 个宏块进行编码。voidT264_mb_encode(C264_t* t, int32_t k) /在这里进行宏块编码if(t-mb_linek.mb_mode = P_MODE)/p 模式,帧间T

21、264_encode_inter_y(t, k);T264_encode_inter_uv(t, k);t-stat.p_block_numt-mb_linek.mb_part +;else if(t-mb_linek.mb_mode = P_SKIP)t-stat.skip_block_num+;/else if (t-mb_linek.mb_mode = B_MODE)/ T264_encode_inter_y(t);/ T264_encode_interb_uv(t);/ t-stat.p_block_num0 +;/else if (t-mb_linek.mb_mode = I_4x4

22、 | t-mb_linek.mb_mode = I_16x16)T264_encode_intra_y(t, k);/帧内4x4 或是16x16/ Chroma/T264_mode_decision_intra_uv(t, k);T264_encode_intra_uv(t, k);t-stat.i_block_numt-mb_linek.mb_mode +;主线函数都位于 t264enc.c。进入该函数后根据先前判断的帧类型以及预测类型选择不同的编码函数。T264_mode_decision_intra_uv(t, k);不要被这个函数的名称迷惑,类型已经确定。自认为这个函数的名称取的并不好

23、,我们进入该函数。uint32_tT264_mode_decision_intra_uv(_RW C264_t* t, int32_t k)DECLARE_ALIGNED_MATRIX(pred8x8u, 8, 8, uint8_t, CACHE_SIZE);DECLARE_ALIGNED_MATRIX(pred8x8v, 8, 8, uint8_t, CACHE_SIZE);DECLARE_ALIGNED_MATRIX(topcacheu, 1, 8 + CACHE_SIZE, uint8_t, CACHE_SIZE);DECLARE_ALIGNED_MATRIX(leftcacheu, 1

24、, 8 + CACHE_SIZE, uint8_t, CACHE_SIZE);DECLARE_ALIGNED_MATRIX(topcachev, 1, 8 + CACHE_SIZE, uint8_t, CACHE_SIZE);DECLARE_ALIGNED_MATRIX(leftcachev, 1, 8 + CACHE_SIZE, uint8_t, CACHE_SIZE);uint32_t sad8x8 =0xffffffff;uint8_t* pred8x8freeu0 = pred8x8u;uint8_t* pred8x8freeu1 = t-mb_linek.pred_i8x8u;uin

25、t8_t* pred8x8freev0 = pred8x8v;uint8_t* pred8x8freev1 = t-mb_linek.pred_i8x8v;int32_t modes;int32_t bestmode;int32_t preds9;int32_t i;uint8_t* top_u, *left_u;uint8_t* top_v, *left_v;static const uint8_t fixmode =Intra_8x8_DC,Intra_8x8_LEFT,Intra_8x8_TOP,Intra_8x8_PLANE,Intra_8x8_DC,Intra_8x8_DC,Intr

26、a_8x8_DC;top_u = top_v = left_u = left_v = T264_intra_8x8_available(t, preds, for(i = 0 ; i pred8x8mode(pred8x8freeu1,8,top_u,left_u);t-pred8x8mode(pred8x8freev1,8,top_v,left_v);sad = t-cmpMB_8x8(t-mb_linek.src_u, t-stride_uv, pred8x8freeu1, 8) +t-cmpMB_8x8(t-mb_linek.src_v, t-stride_uv, pred8x8free

27、v1, 8) +t-mb_linek.lambda * eg_size_ue(t-bs, fixmodemode);if (sad mb_linek.pred_i8x8u)memcpy(t-mb_linek.pred_i8x8u, pred8x8freeu0, sizeof(uint8_t) * 8 * 8);if (pred8x8freev0 != t-mb_linek.pred_i8x8v)memcpy(t-mb_linek.pred_i8x8v, pred8x8freev0, sizeof(uint8_t) * 8 * 8);/fixed prediction mode DCLEFT D

28、CTOP DC128 = DCt-mb_linek.mb_mode_uv = fixmodebestmode;return sad8x8; /选择较小的残差t-pred8x8mode(pred8x8freeu1, 8 top_u, left_u);我们以这个来说明前面的初始化。在 t264enc.c 中的781行:T264_init_cpu(C264_t* t)函数的t-pred16x16Intra_16x16_TOP = T264_predict_16x16_mode_0_c;t-pred16x16Intra_16x16_LEFT = T264_predict_16x16_mode_1_c;

29、t-pred16x16Intra_16x16_DC = T264_predict_16x16_mode_2_c;t-pred16x16Intra_16x16_PLANE = T264_predict_16x16_mode_3_c;t-pred16x16Intra_16x16_DCTOP = T264_predict_16x16_mode_20_c;t-pred16x16Intra_16x16_DCLEFT = T264_predict_16x16_mode_21_c;t-pred16x16Intra_16x16_DC128 = T264_predict_16x16_mode_22_c;在对应在

30、 T264_mode_decision_intra_uv(_RW C264_t* t, int32_t k)中的static const uint8_t fixmode =Intra_8x8_DC,Intra_8x8_LEFT,Intra_8x8_TOP,Intra_8x8_PLANE,Intra_8x8_DC,Intra_8x8_DC,Intra_8x8_DC;就知道了这个过程。首先让帧结构体 C264_t:t 的成员变量指针只想几个预测函数(在 predict.c 中) ,再在定义了几个 modes 的静态结构后分别对比几种预测模式的残差大小,取最小的。此时保存预测类型,保存残差回去编码,

31、就退后到了函数 T264_mb_encode(C264_t* t, int32_t k)中的 T264_encode_intra_uv(t, k);再向上返回就可以看见 dct 变换和量化了,在此就不往下说明了。值得说明的是,也许会怀疑在前面的帧间和帧内的亮度预测中并没有这个名字为类型选择而实质上进行残差选择的函数,这可以在色度的这个函数得到启发,再去寻找他们的相似函数,在 T264_encode_frame 函数,t264enc.c 的1552行调用的T264_mb_mode_decision_all 满足了我们这样的寻找。这样我们就说明了编码的前部分工作。之后就是了解游程编码,大概码率控制

32、也在后面的部分。下部分就是了解这部分代码了,而首先就是看看标准。毕厚杰的新一代视频压缩标准H.264/AVC 很好。人民邮电出版,在此推荐一下。X264 源代码分析相关说明:1. 使用版本: x264-cvs-2004-05-112. 这次的分析基本上已经将代码中最难理解的部分做了阐释,对代码的主线也做了剖析, 如果这个主线理解了,就容易设置几个区间, 进行分工阅读,将各个区间击破了.3. 需要学习的知识:a) 编码器的工作流程 .b) H.264的码流结构,像 x264_sps_t,x264_pps_t 等参数的定义基本上都完全符合标准文档中参数集的定义,抓住主要参数, 次要参数也应该有所了

33、解.c) 数学知识,对 dct 变换等与数学相关的知识的编程实现要有较好理解.d) C 语言的知识.涉及到 c 语言的较多不经常用的特性, 如函数指针数组, 移位运算,结构体的嵌套定义等.e) 耐心,对 h.264的复杂性要有清醒的认识.3.参考资料 :a) 新一代视频压缩编码标准-h.264/avc 毕厚杰主编,人民邮电出版社.b) 网上的流媒体论坛,百度,google 等搜索引擎.4. 阅读代码的方法:a) 较好的方法是利用 vc 的调试器,如果对某个函数感兴趣,可以将断点设置在它的前面. 然后采用step into,step over 等方法进去该函数一步步分析. 当然本身要对程序执行流

34、程要有较清楚认识, 不然不知道何时 step into,何时 step over.b) 建议应该先对照标准弄清各个结构体成员的意义.源代码主要过程分析:1. 进入 x264.c 中的 main 函数.刚开始是读取默认参数,如果你设置了参数的话会修改 param 的.i_ret = Encode( 这条语句使过程进入 x264.c 中的 Encode 函数.2. X.264的 encode 函数 .A. i_frame_total = 0;if( !fseek( fyuv, 0, SEEK_END ) )int64_t i_size = ftell( fyuv );fseek( fyuv, 0,

35、 SEEK_SET );i_frame_total = i_size / ( param-i_width * param-i_height * 3 / 2 )上面这段计算出输入文件的总帧数.B. h = x264_encoder_open( param )这个函数是对不正确的参数进行修改,并对各结构体参数和 cabac 编码,预测等需要的参数进行初始化.C. pic = x264_picture_new( h );该函数定义在COREcommon.c 中.首先分给能容纳 sizeof(x264_picture_t)字节数的空间,然后进行初始化.这里看一下 x264_picture_t 和 x2

36、64_frame_t 的区别. 前者是说明一个视频序列中每帧的特点.后者存放每帧实际的象素值.注意区分.D. for( i_frame = 0, i_file = 0; i_ctrl_c = 0 ; i_frame+ )int i_nal;x264_nal_t *nal;int i;/* read a frame */if( fread( pic-plane0, 1, param-i_width * param-i_height, fyuv ) plane1, 1, param-i_width * param-i_height / 4, fyuv ) plane2, 1, param-i_wi

37、dth * param-i_height / 4, fyuv ) i_frame % (h-param.i_iframe * h-param.i_idrframe) = 0 )确定这是立即刷新片.这里很好理解.但到了 if( h-param.i_bframe 0 )/可以 B 帧编码时.就有问题了.注意我们编完 I 帧后碰到了一个 B 帧,这时我们先不对它进编码.而是采用 frame = x264_encoder_frame_put_from_picture( h, h-frame_next, pic )函数将这个 B 帧放进 h-frame_next 中.好,这里出现了 h-frame_ne

38、xt,在 h 中同时定义了下面几个帧数组用以实现帧的管理 .x264_frame_t *bframe_currentX264_BFRAME_MAX; /* store the sequence of b frame being encoded */x264_frame_t *frame_nextX264_BFRAME_MAX+1; /* store the next sequence of frames to be encoded */搞清意义, 下一个帧,而不一定是 B 帧.x264_frame_t *frame_unusedX264_BFRAME_MAX+1; /* store unuse

39、d frames */注意区分这3个数组.同时还有下面4个函数(定义在ENCODERencoder.c 中).x264_encoder_frame_put_from_picture();x264_encoder_frame_put();x264_encoder_frame_get();x264_frame_copy_picture();这3个数组和4个函数可以说完成了整个帧的类型的判定问题.这个里面 if ,else 语句较多,容易使人迷惑.但我们只要把握下面一个观点就可以看清实质:在不对 P 帧进行编码之前,我们不对 B 帧进行编码,只是把B 帧放进缓冲区( 就是前面提到的数组).比如视频序

40、列:I B B P B B P先确立第一个帧的类型,然后进行编码.然后是2个 B 帧,我们把它放进缓冲区数组. 然后是 P 帧,我们可以判定它的类型并进行编码.同时, 我们将缓冲区的 B 帧放进 h-bframe_currenti,不过这时 P 帧前的两个B 帧并没有编码.当读到 P 帧后面的第一个 B 帧时,我们实际上才将 h-bframe_current 数组中的第一个 B 帧编码, 也就是将在 I 帧后面的第一个 B 帧(说成 P 帧前面的第一个 B 帧容易误解 J)编码.依此类推,把握好上面4个函数的调用流程和指针操作的用法,就可以将帧的类型判定这个问题搞明白了 .F. 然后是速率控制

41、(先不说这个 ,因为它对编码的流程影响不大),看看建立参考帧列表的操作,也就是x264_reference_build_list( h, h-fdec-i_poc ); (定义在ENCODERencoder.c 中).光看这个函数是不行的,它是和后面的这个函数( 如下)一起配合工作的.if( i_nal_ref_idc != NAL_PRIORITY_DISPOSABLE )/B 帧时.x264_reference_update( h );If 条件是判断当前帧是否是 B 帧,如果是的话就不更新参考列表, 因为 B 帧本来就不能作为参考帧嘛!如果是 I 帧或 P 帧的话,我们就更新参考帧列表.

42、我们看到了一个 for 循环, 两个 dowhile 循环.这是实现的关键,具体看代码,不好用语言说明白.G. 进入另一个复杂的领域:写 slice 的操作,刚开使挺简单, 如我下面的注释 ./* - Write the bitstream - */* Init bitstream context */h-out.i_nal = 0;/out 的声明在 bs.h 中.bs_init( /空出8 位./* Write SPS and PPS */if( i_nal_type = NAL_SLICE_IDR )/不是每次都要写 SPS and PPS,只有碰见立即刷新片时才写./* generat

43、e sequence parameters */x264_nal_start( h, NAL_SPS, NAL_PRIORITY_HIGHEST );x264_sps_write( x264_nal_end( h );/* generate picture parameters */x264_nal_start( h, NAL_PPS, NAL_PRIORITY_HIGHEST );x264_pps_write( x264_nal_end( h );不过看下面那个函数(就进入了复杂的领域).H. x264_slice_write()(定义在ENCODERencoder.c 中),这里面是编码的

44、最主要部分, 下面仔细分析.前面不说,看下面这个循环, 它是采用 for 循环对一帧图像的所有块依次进行编码.for( mb_xy = 0, i_skip = 0; mb_xy sps-i_mb_width * h-sps-i_mb_height; mb_xy+ )/h-sps-i_mb_width 指的是从宽度上说有多少个宏快 .对于宽度也就是288 / 16 = 18const int i_mb_y = mb_xy / h-sps-i_mb_width;const int i_mb_x = mb_xy % h-sps-i_mb_width;/这两个变量是定义宏块的位置.而不是指宏块中元素的

45、位置./* load cache */x264_macroblock_cache_load( h, i_mb_x, i_mb_y );/是把当前宏块的 up 宏块和 left宏块的 intra4x4_pred_mode,non_zero_count 加载进来,放到一个数组里面,这个数组用来直接得到当前宏块的左侧和上面宏块的相关值.要想得到当前块的预测值,要先知道上面,左面的预测值,它的目的是替代 getneighbour 函数./* analyse parameters* Slice I: choose I_4x4 or I_16x16 mode* Slice P: choose betwee

46、n using P mode or intra (4x4 or 16x16)* */TIMER_START( i_mtime_analyse );x264_macroblock_analyse( h );/定义在 analyse.h 中.TIMER_STOP( i_mtime_analyse );/* encode this macrobock - be carefull it can change the mb type to P_SKIP if needed */TIMER_START( i_mtime_encode );x264_macroblock_encode( h );/定义在 E

47、nc/encoder.c 中.TIMER_STOP( i_mtime_encode );截止到这就已经完成编码的主要过程了,后面就是熵编码的过程了(我也没看到那, 但认为前面才是编码的主要过程).下面对这个过程进行分析.A. x264_macroblock_cache_load( h, i_mb_x, i_mb_y );它是将要编码的宏块的周围的宏块的值读进来, 要想得到当前块的预测值,要先知道上面,左面的预测值,它的作用相当于 jm93中的getneighbour 函数.B. 进入 x264_macroblock_analyse( h )函数(定义在Encanalyse.c 中,这里涉及到了

48、函数指针数组,需要好好复习, 个人认为这也是 x264代码最为复杂的一个地方了).既然已经将该宏块周围的宏块的值读了出来,我们就可以对该宏块进行分析了( 其实主要就是通过计算 sad 值分析是否要将16*16的宏块进行分割和采用哪种分割方式合适).看似很复杂,但我们只要把握一个东西就有利于理解了:举个生活中的例子来说:如果你有2元钱,你可以去买2袋1元钱的瓜子,也可以买一袋2元钱的瓜子,如果2袋1元钱的瓜子数量加起来比1袋2元钱的瓜子数量多,你肯定会买2袋1元的.反之你会去买那2 元1袋的.具体来说,对于一个16*16的块,如果它是 I 帧的块 ,我们可以将它分割成16 个4*4 的块,如果这16个块的 sad 加起来小于按16*16的方式计算出来的 sad 值, 我们就将这个16*16的块分成16个4*4 的块进行编码 (在计算每个4*4的块的最小sad 值时已经知道它采用何种编码方式最佳了), 否则采用16*16的方式编码 (同样我们也已知道对它采用哪种编码方式最为合适了.如果它是 P 帧或 B 帧的块,同样是循环套循环,但更为复杂了,可以看我在 analyse.c 中的注释.这里还要注意的是提到了x264_predict

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 企业管理 > 经营企划

本站链接:文库   一言   我酷   合作


客服QQ:2549714901微博号:道客多多官方知乎号:道客多多

经营许可证编号: 粤ICP备2021046453号世界地图

道客多多©版权所有2020-2025营业执照举报