作者:jogh.264 参考代码 JM86 一·参数说明 这一节阐述的是encoder.cfg 中的参数对编码过程的影响 要注意的是encoder.cfg 中的参数跟input 结构体中的变量是一一对应的 StartFrame:从视频流的第几帧开始编码 FramesToBeEncoded:指明了除去 B帧后将要被编码的帧数 input->no_frames = FramesToBeEncoded FrameSkip:指明了编码过程中跳过的帧数,中间有 B 帧也算跳过一帧。 NumberBFrames:相邻 I、P帧或相邻的 P帧之间的 B 帧个数,必须有 NumberBFrames< FrameSkip input->successive_Bframe = NumberBFrames IntraPeriod:I 帧出现的频率。若 IntraPeriod="3",则每 3 帧(不含 B 帧)中有一 I 帧; IntraPeriod="0" 时只有第一帧是 I 帧。 IDRIntraEnable:此值为1时每个 I帧都是 IDR,否则只有第一个 I帧是 IDR。 举例:在 StartFrame="0" FramesToBeEncoded="5" 除去B帧 FrameSkip="3" NumberBFrames="2" IntraPeriod="3" IDRIntraEnable="1" 的情况下编码情况如下,其中红色代表 IDR 帧 表 1 视频流 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 编码流 I B B P B B P B B I B B P 编码顺序 0 2 3 1 5 6 4 8 9 7 11 12 10 二·pic_order_cnt_type 为 0 的情况 这种情况下显式的计算 POC (1) 编码端 I 帧或 P 帧 toppoc 的计算 这个过程在 main()函数的组循环 “for (img->number=0; img->number < input->no_frames; img->number++){ }” 中实现 IntraPeriod或 IDRIntraEnable 为零时:这种情况下只有第一个 I 帧是 IDR 帧,比较简单。对于 I帧或 P 帧,其顶场的 POC 为 (IMG_NUMBER) * (2*(input->successive_Bframe+1)) IntraPeriod和 IDRIntraEnable 都不为零时:这种情况下每个 I 帧都是 IDR 帧,其 POC 必须设置为零, I帧出现的频率为 IntraPeriod,故其 toppoc为 (img->number % input->intra_period) * (2*(input->successive_Bframe+1)) 说明: 原程序中使用了宏定义 IMG_NUMBER “#define IMG_NUMBER (img->number - start_frame_no_in_this_IGOP)” 通过搜start_frame_no_in_this_IGOP可知这个变量在NumberOfFrameInSecondIGOP为0 (encoder_main.cfg 中就是这样设置的)时恒为 0,故有 IMG_NUMBER = img->number (2) 编码端 B帧 POC 的计算 由表一可知,在编完一 I 帧或 P 帧之后才开始对它前面的 B帧进行编码 for (img->number=0; img->number < input->no_frames; img->number++) { ……I,P 帧编码…… if ((input->successive_Bframe != 0) && (IMG_NUMBER > 0)) { …… for(img->b_frame_to_code=1; img->b_frame_to_code<=input->successive_Bframe; img->b_frame_to_code++) { } IntraPeriod或 IDRIntraEnable 为零时toppoc 等于 2+(img->number-1) * (2*(input->successive_Bframe+1)) +2* (img->b_frame_to_code-1) a) 第一个 2 指得是 IDR 的两个场; b) img->number要减一是因为要对当前帧(img->number)前面的 B帧进行编码; IntraPeriod和 IDRIntraEnable 都不为零时 toppoc等于 2+(img->number % input->intra_period-1) * (2*(input->successive_Bframe+1)) +2* (img->b_frame_to_code-1) IDR 帧前面 (3) toppoc 到 pic_order_cnt_lsb 的转化 img->pic_order_cnt_lsb =img->toppoc & ~((((unsigned int)( –1)) << (log2_max_pic_order_cnt_lsb_minus4+4))) (unsigned int)(-1)的十六进制形式是 0xffffffff,即它的每一位都是 1; log2_max_pic_order_cnt_lsb_minus4+4是图象数目(包括 B 帧)最大值的位数 z 当 toppoc >0时,img->pic_order_cnt_lsb=img->toppoc 当 toppoc <0时,img->pic_order_cnt_lsb= max_pic_order_cnt+ img->toppoc 其中 max_pic_order_cnt=1<<( log2_max_pic_order_cnt_lsb_minus4+4) 疑问: 不知道 toppoc 到 pic_order_cnt_lsb 这个过程有什么意义; POC 的值会从 0 变到很大,为什么不对它进行熵编码; (4) 解码端 toppoc 的恢复 此过程在函数 decode_poc 中执行。其思想是对于 IDR 前的 B 帧 Toppoc = pic_order_cnt_lsb - max_pic_order_cnt 否则 Toppoc = pic_order_cnt_lsb 是否减去 max_pic_order_cnt 由变量 PicOrderCntMsb 决定,对于 IDR 前的 B 帧 PicOrderCntMsb = (– max_pic_order_cnt) 否则 PicOrderCntMsb = 0 到这就不难理解 PicOrderCntMsb 的含义了, PicOrderCntMsb 反映了 toppoc的值是否小于 0。 至于另外两个参数:PrevPicOrderCntMsb 总是为 0;PrevPicOrderCntLsb 在当前图象是 IDR 或 IDR 前(视频流中)的 B 帧时为0,否则等于前一图象(编码序列中)的 PicOrderCntLsb。 三·pic_order_cnt_type 为 1 的情况 这种情况下通过 frame_num来计算 POC (1)frame_num 简介 参考《毕厚杰》7.3.4 节中 frame_num 条款的解释,对于表 1 中的图象序列,其 frame_num 的值参考如下: 表 2 视频流 0 1 2 4 5 6 8 9 10 12 13 14 16 编码流 I B B P B B P B B I B B P 编码顺序 0 2 3 1 5 6 4 8 9 7 11 12 10 frame_num 0 2 2 1 3 3 2 1 1 0 2 2 1 poc 0 2 4 6 8 10 12 -4 -2 0 2 4 6 (2)算法思想以及其解码端的实现 对于 IDR 帧,poc = 0; 对于 I 帧或P 帧 poc = frame_num*2*(input->successive_Bframe+1) 或 poc = 2*(input->successive_Bframe+1) + (frame_num – 1)*2*(input->successive_Bframe+1) 解码端实现 poc = img->ExpectedPicOrderCnt + img->delta_pic_order_cnt[0] (在 I,P 帧下为 0) 对于 I 帧或P 帧之前的 B 帧(视频流中) poc = (frame_num – 1)*2*(input->successive_Bframe+1) – 2*(input->successive_Bframe+1 – img->b_frame_to_code) 或 poc = 2*(input->successive_Bframe+1) + (frame_num – 1 – 1)*2*(input->successive_Bframe+1) + 2*( img->b_frame_to_code – 1) – 2*input->successive_Bframe 解码端的实现 poc = img->ExpectedPicOrderCnt + img->delta_pic_order_cnt[0] + active_sps->offset_for_non_ref_pic 变量说明 a) 其中 img->b_frame_to_code请参见标题一·(2) b) img->disposable_flag = (nalu->nal_reference_idc = = 0),而 nal_reference_idc 只在 B 帧时 为0,即img->disposable_flag只在B帧时为1。这也是在B帧情况下img->AbsFrameNum 要比 I 帧或P 帧多减去一个 1 的原因。 c) 其它变量参见下面小题; (3)编码端参数设置 a) img->num_ref_frames_in_pic_order_cnt_cycle: 这个参数在 init_poc( )函数中设置为 1 后就再没改动过; b) img->offset_for_ref_frame[0] : 在 StoredBPictures为0 时等于 2*(input->successive_Bframe+1); c) img->offset_for_ref_frame[1] : 没什么用,264 头文件中不会保存此变量; d) img->delta_pic_order_cnt[0] : 这个变量只对 B 帧有用,等于 2*(img->b_frame_to_code –1); 对于 I 帧或 P 帧, 其值为 0; e) active_sps->offset_for_non_ref_pic: 只对 B 帧有用,在 StoredBPictures 为0 时等于–2*input->successive_Bframe, |
|