对几个重要问题的阐述

时间:2022-04-05 14:13:15

由于DPB中间的参考帧的MV都是以4X4块为单位,现在以8X8块作Direct mode模式所以必须对子块的MV作合并
JM采用的东西是如下图的方式:
x o | o x
o o | o o
- - - - -
o o | o o
x o | o x
每8X8块取外角上的4X4块的MV.
算法如下:

<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
  
b8Mode是帧间预测中对8X8块的再次细分,称为亚宏块级模式,划分定在表格b8_mode_table中
const int   b8_mode_table[6]   = {0, 4, 5, 6, 7};
其中0是8X8 Direct模式,只对B帧,4,8X8,5,8X4,6,4X8,7,4X4,以上5中模式在宏块级中统称为P8X8模式,这个可以在码流TRACE文件中可以应证。

<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

264标准中可以对宏块级语法元素按照重要的等级进行分区,可以分为分区A,B,C, 这个数据分区可以根据数组映射获得264时如何处理分区得.涉及到3个数组.
typedef enum  
{
   SE_HEADER,
   SE_PTYPE,
   SE_MBTYPE,
   SE_REFFRAME,
   SE_INTRAPREDMODE,
   SE_MVD,
   SE_CBP_INTRA,
   SE_LUM_DC_INTRA,
   SE_CHR_DC_INTRA,
   SE_LUM_AC_INTRA,
   SE_CHR_AC_INTRA,
   SE_CBP_INTER,
   SE_LUM_DC_INTER,
   SE_CHR_DC_INTER,
   SE_LUM_AC_INTER,
   SE_CHR_AC_INTER,
   SE_DELTA_QUANT_INTER,
   SE_DELTA_QUANT_INTRA,
   SE_BFRAME,
   SE_EOS,
   SE_MAX_ELEMENTS   //!< number of maximum syntax elements
} SE_type;       // substituting the definitions in elements.h
以上定义了264宏块级编码要用到的语法元素,当然不是所有的宏块编码都有以上元素,根据帧内,帧间,实际编码过程中,等有所不同
关于以上的元素具体含义,参考JM或标准可以获得,不过有些元素似乎在JM中并没有利用到,比如SE_EOS,不知道最新版如何?

int assignSE2partition_NoDP[SE_MAX_ELEMENTS] =
   {   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
int assignSE2partition_DP[SE_MAX_ELEMENTS] =
   {   0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 2, 2, 2, 2, 0, 0, 0, 0 } ;
上面元素0,代表DPA,1代表DPB,2代表DPC,可以看出来假如没有定义数据分区得话:所有的语法元素都是DPA,定义了数据分区,那么帧内系数是分区B,帧间系数是分区C,其他的元素都是分区A.可以看得出来,遭遇信道丢包时,
光有DPB或DPC,没有DPA是无法解出码流的,假如没有DPB,DPC的话,还可以根据DPA的元素获得近似值。 那么JM是如何实现宏块级数据分区管理的呢,这个由Slice里面的datapartition数组管理,第一个成员为DPA, 第二个为DPB,第三个为DPC,可以根据语法元素切换输出到A,B,C中.

<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
  
把编码方式切换到CAVLC,其实在foreman测试序列,个人觉得CABAC,CAVLC编码压缩效率差别不大
SNR更是差不多,但是CAVLC要简单很多。
经过一个晚上的艰苦战斗,初步得出结论,SKIP模式对B,P帧而言,DIRECT模式只对B而言,
个人觉得DIRECT模式,SKIP模式,这种说法有些让人困惑。
下面讲B帧的DIRECT,SKIP模式:
DIRECT,SKIP,模式相同的是,他们的宏块类型都是0,运动向量残差都为0,因为MVD可以通过临块预测方式得到
但是SKIP模式的cbp为0,也就是没有残差传输,它是双向预测,参考帧在LIST0,LIST1中的
序号也是根据临块的参考帧情况获得(注意不一定序号非的是0),所以连参考帧都不用传输.,
SKIP模式有更严格的要求,CBP为0,也就是没有残差系数,达到最大的压缩效率。
再讲P帧的SKIP模式,但前象预测,参考帧为LIST0中序号为0帧,无参考帧编码,CBP为0,无残差系数编码.
她们的分块模式为0,都是16X16.
但是在CAVLC中,SKIP模式,对SKIP宏块没有任何实际编码码流,解码器是如何识别这个宏块存在的,
大家打开TRACE文件,可以看到为一个SKIP或者连续几个SKIP宏块结束后,接着下一个非SKIP宏块,
一开始就有一个语法元素,mb_skip_run有一个值,这个元素采用Exp-Golomb编码,这个值的意思就是前面有多少个SKIP块,这就是264码流
巧妙的解决方案,当遇到这个元素非0的时候,就自动往前面做SKIP宏块填充,.
@34679 mb_skip_run                                                 1 (   0)  
@34680 mb_type (P_SLICE) ( 4, 1) = 2                             010 (   1)  
@34683 mvd_l0 (0) =   -6   (org_mv -10 pred_mv   -4)              0001101 ( -6)  
@34690 mvd_l0 (1) = 0   (org_mv 0 pred_mv 0)                    1 (   0)  
@34691 mvd_l0 (0) = -11   (org_mv -20 pred_mv   -9)          000010111 (-11)  
@34700 mvd_l0 (1) = 7   (org_mv 9 pred_mv 2)              0001110 (   7)  
@34707 CBP ( 4, 1) = 0                                           1 (   0)  

*********** Pic: 2 (I/P) MB: 16 Slice: 0 **********


*********** Pic: 2 (I/P) MB: 17 Slice: 0 **********


*********** Pic: 2 (I/P) MB: 18 Slice: 0 **********

@34708 mb_skip_run                                              011 (   2)  
@34711 mb_type (P_SLICE) ( 7, 1) = 8                         00101 (   4)  
上面表示18号宏块前面有2个SKIP宏块,解码器然后可以自动填充。
其实本来解决这个问题,可以用更少的时间来解决这个问题,但是,JM97对TRACE文件输出,有一个bug,
导致,输出的TRACE混乱错误,无法理解,浪费了大量的时间,花了几个小时才找出解决这个bug,该bug位于
writeSyntaxElement2Buf_Fixed函数中,这个地方需要注释掉TRACE输出。
int writeSyntaxElement2Buf_Fixed(SyntaxElement *se, Bitstream* this_streamBuffer )
{

   writeUVLC2buffer(se, this_streamBuffer );

#if TRACE
// if(se->type <= 1)
//   trace2out (se);
#endif
   return (se->len);
}
其实上面两句是留给MBHEADER输出的,因为它不用参加RDO rate计算
个人觉得JM对TRACE文件的输出管理还有些问题,有待修改,不知道在新的JM版本中有没有修正这个bug.
这个也算是解决这个问题的副产品.

另外CABAC对贷这个问题好像不一样,有时间可以在研究这个问题

<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
  
求象素的灰度.....
#define MAX_LEN (1000*3)
unsigned char RGB[MAX_LEN]={2,2,2,4,5,6,7,8,9,0};
unsigned int mulData[7] = {313524,615514,119538};
__int64 mask1 = 0xFFFFFFFF00000000;
__int64 mask2 = 0x00000000FFFF0000;
__int64 mask3 = 0x000000000000FFFF;

int main(int argc, char* argv[])
{
char gray = 0;
unsigned int Y=(((RGB[0]*mulData[0]))+((RGB[1]*mulData[1]))+((RGB[2]*mulData[2])))>>20;
_asm{
pxor mm7,mm7
pxor mm6,mm6
mov ecx,0
sloop: movd mm0,[mulData+ecx*4]
punpcklwd mm0,mm7
mov al,[RGB+ecx]
and eax,0X000000FF
movd mm1,eax
movd mm2,eax
;;;;;;;;;;;;;;;;;;;;;;;;;;;
punpckldq mm1,mm2
movq mm4,mm1
movq mm5,mm0
punpcklwd mm4,mm1
punpcklbw mm5,mm7
pmullw mm4,mm5
;;;;;;;;;;;;;;;;;;;;;;;;;;;
pmullw mm1,mm0
pand mm1,mask1
movq mm0,mm4
pand mm0,mask2
pand mm4,mask3
psrlq mm0,8
paddd mm1,mm0
paddd mm1,mm4
movq mm3,mm1
pand mm3,mask1
psrlq mm3,16
paddd mm1,mm3
paddd mm6,mm1
inc ecx
cmp ecx,3
jl sloop
psrld mm6,20
movd eax,mm6
mov gray,al
emms
}
return 0;
}

<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
  
H.264对帧场编码问题支持的比较完整,因为曾经有人问我,在h.264码流中,是否有判别帧场编码的元素。
我当时对H.264如果认定码流帧场编码不太了解,更别说是宏块级帧场编码了。其实H.264对帧场编码有两种级别,分为
帧级和宏块级.帧级是对整个帧一开始就分为top field,bottom field.对两个field分别进行ME,MC,mode decision等. 而宏块级Field编码,就不一样了。曾经在流媒体网站H.264分块看到有一牛人对宏块级Field编码的解释,我感到疑惑,
但当时修行不够,说不出所以然,现在把那段话贴在这里:
jamie80:
"
当 MbaffFrameFlag = 1 & currMb->mb_field 时,偶数行放在宏块的上半部分(16x8)来编码,而奇数行位于下半部分来编码。
如果当前宏块的mb_field为0(帧编码)而左邻居宏块的mb_field为1(场编码),这样其左邻居像素就可以属于不同的4x4块,
至少左邻居奇数行的像素和偶数行的像素肯定不属于同一个4x4块,所以我们需要对它们区别对待。
(MB Adaptive Frame/Field给H.264带来不少麻烦)
我的答案是一个宏块级field编码,是这样子的,JM里面给出解释的是Field MB pair,场宏块对。也就是a couple of Field
没有单独存在的一个Field宏块,只有Field MB pair.上面一个是Top field MB(两个MB的偶数行),下面一个是Bottom MB
(两个宏块的奇数行),宏块序号上下是连续的,这个和一般的编号方式不同,上面一个是偶数,下面一个是奇数.
而不是象上面那段话那样,只有半个宏块的问题.
对此,我们做出解释,现在我们研究函数void dpb_split_field(FrameStore *fs),该函数完成的任务是对即将送入DPB的重建帧
进行帧场分解,解出top,bottom field,对顶底场进行1/4象素插值,复制场参数,分解运动向量,参考帧序号,参考帧id等。
宏块级帧场自适应模式需要对宏块对进行场模式和帧模式分别编码,计算她们的RDCOST,选其中最少的RDCOST作为编码方式,所以
自适应方式做最佳编码是付出了计算复杂度大大增加的代价。同样帧级帧场自适应方式需要帧编码,和场编码分别进行,
计算RDCOST最少值作为最佳编码,同样付出计算复杂度.
   回到文章开始时候提出的问题,码流中是否有标志指示后面访问单元(Access Unit)的帧场编码方式,答案是肯定的,在一个
SPS(sequence parameter set)中能找到frame_mbs_only_flag标志,该标志显示是否帧编码,如果为0,那么还有一个参数
mb_adaptive_frame_field_flag,下面对配置参数和以上两个元素的关系进行说明
PicInterlace代表帧级隔行扫描,MbInterlace代表宏块级隔行扫描
PicInterlace          =   0     # Picture AFF (0: frame coding, 1: field coding, 2:adaptive frame/field coding)
MbInterlace              =   0     # Macroblock AFF (0: frame coding, 1: field coding, 2:adaptive frame/field coding)
交叉位置(frame_mbs_only_flag, mb_adaptive_frame_field_flag),N/A为不需要该元素

         \PicInterlace
MbInterlace \

0

1

2

0

(1,N/A)

(0,0)

(0,0)

1

(0,1)

(0,1)

(0,1)

2

(0,1)

(0,1)

(0,1)