转载请标明是引用于 http://blog.csdn.net/chenyujing1234
欢迎大家提出意见,一起讨论!
需要源码的请与我联系。
参考书籍<
用于甚低数码率的音频/视频编码标准,它由一个数据比特数据流格式和相应的一组协议组成。
用于表示由自然或合成的音频、视频及对象数据组成的多媒体内容。
和MPEG-1、MPEG-2相比,MPEG-4中系统的概念发生了极大的变化,过去系统仅指整体结构、多路复用及同步功能,
而MPEG-4除此之外,又引入了视听对象及描述、场景描述、传输多媒体集成框架(Delivery Multimedia Integration Framework, DMIF)以及上载码流等概念
来支持基于内容的交换型多媒体应用。同时MPEG-4支持将自然信息和合成信息进行组合,包括图像与自然景物、合成声音与自然声音的组合。
MPEG-4之前有四个运动图像编码标准:
MPEG-1 、MPEG-2、H.261、H.263
这四个标准就是把视频序列按时间先后分为一系列的帧,每一帧图像又分成16X16的宏块来进行运行补偿和编码。
这种基于是帧、块和像素的编码称为第一代视频编码方案,在编码过程中不考虑图像的具体结构和内容,因而会产生以下主要问题:
将图像固定地分成相同大小块,在高缩比的情况下会产生严重的块效应;
不能对图像的内容进行查询、编辑和回放等;
没有利用人类视频系统的特性。
MPEG-4是基于图像的内容进行编码的,根据内容将图像分成不同的视频对象VO(Video Object)。
例如在可视电话系统中,经常将讲话的人作为前景视频对象,将其余部分作为背景视频对象。
前景视频对象由于包含重要的边界和轮廓信息,而且纹理又是理解图像的重要附加信息,因而在编码过程中应尽可能地保留这部分信息。
在编码过程中前景对象与背景对象采用不同的编码策略。前景对象的编码尽可能保留视频对象的细节和平滑。对人们不关心的背景可以采用大压缩比的编码策略,甚至不
予传输,而是在解码端用其他的背景拼接成新的背景。
MPEG-4视频标准还将合成技术用于编码。在视频非编码标准中定义了人脸部的合成编码。通过定义人脸的模型以及动画参数,在编码过程中只传输这些动画参数,不仅能
提高编码效率,还能用于虚拟电视会议系统的实现。在合成编码还实现了网格变形编码,对视频对象生成二维网格或三维网格模型,在编码的大部分时间中只编码传输网格
顶点的位置变化,在解码端利用已知的纹理信息和传来的网格顶点位置变化来合成编码的视频对象。
MPEG-4标准通过定义框架、级、算法、工具 4层结构来分层描述具体应用中的编码方案。
(1)一个框架是一类应用的完整编码码流语法描述,它是整个MPEG-4码流语法的一个子集。
即使在给定框架的语法规定范围内,依然可能由于码流中参数的不同而导致编码器和解码器的功能和编码效率产生很大的差别。
(2)为了处理这个问题,在每个框架中又通过定义级来规定码流中各个参数的类型和限制。
(3)算法是为了完成框架所要求的功能而组织起来的一些工具集合。
(1) 基于对象的功能。
MPEG-4标准的系统部分用来描述组成一幅画的各个视频对象之间的空间和时间的关系。
(2)形状编码
视频对象可以是任意形状的物体,因而定义了平面(Alpha Plane) 来描述对象的形状和位置信息。
平面主要有二进制和灰度两种。
二进制只确定某一像素点是否属于当前的对象,灰度平面可以描述对象的透明度,将不同的对象混合起来,完成某些特技效果,通常对平面的编码叫做形状编码。
(3)基于对象的编辑和交互式操作。
允许随机访问每个视频对象,具体地讲是能以0.5S的时间间隔访问对象,可以单独解码对象的形状信息,而不解码对象的纹理信息,也能对视频对象进行剪贴、平移和旋转。
(4) 多输入流多视点的编码和解码。
能有效地对来自不同的视频源图像进行编码,并能根据配置文档,在解码端把这些对象的码流同步地解码出来组成一幅图。在多视点立体视频编辑的应用中,
MPEG-4能有效地利用同一场景中不同视点图你信息的冗余性提高编码效率,对同一个场景仅能支持4个视点的编码。
(5) 码率方式。
可以支持各种不同的传输速率下的编码,其中包括小于64Kbit/s的低速码率、码率为64-384Kbit/s的高码率编码。
(6)不同分辨率的编码。
提供了灵活的编码策略,这些策略包括对编码图像时间分辨率、空间分辨率和解码图像的信噪比的控制。
时间分辨率编码策略是每隔几帧选一帧作为基帧,基帧是必须编码传输的,只有传输带宽允许或解码端要求才编码中间帧,使运动图像看起来更平滑;
空间分辨率编码策略是在编码端对图像进行抽样使图像尺寸变小.
信噪比编码策略是根据传输带宽的限制和实际应用的需要,降低解码图像的信噪比来提高压缩率。
(7)在噪声环境中的鲁棒性。
MPEG-4对每个对象的关键数据如对象的头信息和形状信息提供更高的容错保护,并提供一个可逆变长编码码表,能检验传输中产生的误码。
(8) 脸部动画。
(9)网格编码
可以对需要编码的二维或三维视频对象生成各种网格模型,例如三维的高度栅格、脸和身体的三维多边形网格,以及用二维Delaunay网格来描述物体。
MPEG-4能有效地进行基于网络模型的视频对象运动信息编码,包括编码网格的拓扑形状以及每个结点的运动矢量。
(10)全局运动估计和Sprite编码。
块匹配运动补偿技术对摄像头运行所产生的整幅图像的变化不是很有效。因此MPEG-4定义了二维和三维全局运动模型,包括平移、旋转、映射和投影等来补偿。
MPEG-4是基于内容的方式表示视频数据的,正是这个特点使得MPEG-4具有高效率、基于内容交互(操作、编辑、访问等)和基于内容分级扩展(空域分级、时域分级)等
优点。MPEG-4中引入了VO(video object)的概念实现基于内容的表示。VO的结构依赖于具体应用和实现系统,在要求超低比特率的情况下,VO可以是一个矩形帧(即传统
MPEG-1,8263中的矩形帧),从而与原来的标准兼容;
在VM中,VO被定义为画面中分割出来的不同物体,每个VO有3类信息来描述:运动信息、形状信息和纹理信息。
编码的第一步是VO的形成,先要从原始视频流中
分割出VOL,
由编码控制机制为不同的VO以及各个VO的3类信息分配码率,
以及各个VO分别独立编码,
最后将和个VO的码流复合成一个位流。
(1) 视频对象。
VO是指视频流中用户可以获取和操作的个体。它由时间上连续的帧面序列构成。
在MPEG-4中,视频对象又分为自然视频对象和合成视频对象。
自然视频对象是指从自然图像和视频序列中分割出来的某一视频区域;
合成视频对象是指表面通过若干顶点和曲面定义的,由计算机产生2D/3D对象。
(2)视频对象平面。
视频对象平面(VOP)是指在某一时刻某一帧画面的视频对象。VOP的引入直接导致了基于内容的分级编解码技术。
VOP编码针对视频对象(VO)形状、运动、纹理等可以进行独立编码、存储和传输。
(3)Sprite图像。
Sprite图像是指在一段VideoChip中所有可见的,属于某一视频对象的像素所组成的图像,主要是针对背景视频对象的特点而提出的。
在许多应用场合,背景视频对象自身没有任何局部运动,为了有效地编码视频对象,可以将其在一段时间内的内容拼接成一幅完整的背景图像,
这样的图像就叫做Sprite图像。
MPEG-4定义了两种Sprite:
一种是静态Sprite。即在整个编码过程中保持不变的Sprite.例如:视频电话和视频会议中的背景全景图。
另一种是动态Sprite,即在编码过程中不断动态更新产生的Sprite。
实现 Sprite编码,必须满足:
A、前景对象与背景对象要能很好的分开;
B、无痕迹地从一段视频或一些图像中拼接出Sprite图像。
(4) MPEG视频流逻辑结构。
一个完整的视频序列通常由几个视频段(Video Session,VS)构成,每个VS由一个或多个VO组成,每个VO又由一个或多个视频对象层(Video Object Layer, VOL)构成,
每个VOL代表一个层次,即基本层或增强层,每个层表示某一种分辨率。在每个层中,都有时间上连续的一系列VOP。
视频编码器包括形状编码(对于任意形状)、运动信息编码和纹理编码。
基本编码方法为:首先对输入的原图像序列进行场景分析和对象分割,以划分不同的VOP,得到各个VOP的形状和位置信息,它可以用
Alpha平面来表示。对Alpha平面进行压缩编码和传送,在接收端就可以恢复Alpha平面。提取的形状和位置信息又用来控制VOP的运动和纹理编码。
对运动编码和纹理编码仍然采用经典的运动预测/补偿法。
输入第N帧的VOP与帧存储器中存储的N-1帧的VOP进行比较,找到运行矢量,然后对两帧VOP的差值进行量化、编码。编码后得到的纹理信息,与运动编码器和形状编码器
输出的运动信息和形状信息复合形成该VOP的比特流层。不同视频对象的VOP序列分别进行编码,形成各自的比特流层,经复合后在信道上传送。
传送的顺序依次为形状信息、运动信息和纹理信息。
下图是MPEG-4 Video编码的基本框图。
(1)形状编码。
VOP形状信息有两类:二值形状和灰度形状信息。二值形状信息用0、1表示VOP的形状。
0表示非VOP区域,1表示VOP区域。
二值形状信息的编码采用基于运动补偿块的技术,可以是无损或有损编码。
灰度形状信息用0-255之间的值来表示VOP的透明程序,其中0表示完全透明(相当于二值信息中的0);
255表示完全不透明(相当于二信信息中的1);灰度信息是二值信息的扩展,它可以用来表示透视的物体,并降低混迭现象。
灰度信息的编码采用基于块的运动补偿DCT方法,属于有损编码。
MPEG-4中采用位图法来表示这两类形状信息。VOP被限定在一个矩形窗口内,称之为VOP窗口,窗口的长、宽均为16的整数倍,
同时保证VOP窗口中非VOP的宏数目最小。
位图法实际上是一个边框矩阵的编码,矩阵被分成16X16的“形状块”,允许进行有损编码,这通过对边界信息进行子采样实现,
同时允许使用宏块的运动向量来做形状的运行补偿。
实验证明,位图表示法具有较高的编码效率和较低的运算复杂度。
(2) 运动信息编码
MPEG-4采用运动预测和运动补偿技术来去除图像信息中的时间冗余成分,而这些运动信息的编码技术可视为现有标准向任意形状的VOP的延伸。
VOP的编码有3种模式,即帧内编码模式(I-VOP)、帧间预测编码模式(P-VOP)、帧间双向预测编码模式(B-VOP)。
VOP如形状编码一样,外加了边框,边框分成16X16的宏块,宏块内是8X8的块。
在MPEG-4中运动预测和运动补偿可以是基于16X16像素宏块的,也可以是基于8X8像素块的。
为了适应任意形状VOP,MPEG-4引入了图像填充技术和多边形匹配技术。图像填充技术利用VOP内部的像素值来外推VOP外的像素值,以此获得运行预测的参考值。
多边形匹配技术则将VOP的轮廓宏块的活跃部分包含在多边形之内,以此来增加运动估值的有效性。
(3) 纹理编码
纹理信息有两种:内部编码的I-YOP的像素值; 帧间编码的P-VOP、B-VOP的运动估计残差值。
仍然采用基于分块的纹理编码。VOP边框仍被分成16X16的宏块。
纹理编码方式分三种情况:
一是VOP外、边框内的块,不编码;
二是VOP内的块,采用经典的DCT方法;
三是部分在VOP内,部分在VOP外的块采用图像填充技术来获取VOP外的像素值,之后进行DCT编码。
(1) 基于纹理的分割。
主要是应用模式识别的技术来聚类,但要注意分割结果适度。分割太粗,不能有效地压缩;分得太细,就有可能是物体的各部分,这样对于压缩和基于内容的操作不利。
(2) 基于运动的分割。
将具有同一运动参数模型的区域聚类,从而达到分割的目的。这种方法可以分割出运动的物体,但会使得基于运动一致必的分割实现起来非常,效果也不太理想。
(3) 纹理和运动结合的分割。
在运行一致性表现得非常明显的区域用运动分割,在一些细节或运动复杂区域仍采用纹理分割。
MPEG-4提供了基于对象的分级功能,并同时兼顾MPEG-2中的图像分级功能。视频分级编码的目的在于:
(1)以不同的带宽及不同的显示能力进行视频数据库的浏览,以及在多媒体环境下视频内容的多分辨率播放;
(2)提供分层的视频流以适应按优先顺序进行视频传播的要求。
提供了两种分级方法:空域分级(Spatial Scalability)和时域分级(Temporal Scalability).通过视频对象层(VOL)的数据结构实现了基于内容的分级编码。
每一种分级编码都至少有两层VOL,低层称为基本层,高层称为增强层。
许多图像序列中的背景本身实际上是静止的,由于摄像机的运动才造成了它们的改变。可以通过图像的镶嵌技术将整个序列的背景图像拼接成一个大的完整的背景,
这就是Sprite图像。
Sprite也可以指图像序列中保持不变的比较小的VOL,例如电视台的台标,由于Sprite图像本身是不变的,所以只需传输一次,然后根据摄像机的运动参数在接收端
重建背景,这样大大减少传输数据量。
这个函数通过param1输入待压缩的视频帧,param1的实际类型是ENC_PARAM
然后把压缩过的视频帧放到param2中返回,
enc_opt表示调用方式,有3种调用方式:ENC_OPT_WRITE, ENC_OPT_INT 和 ENC_OPT_RELEASE,
分别表示正常压缩、压缩初始化和压缩完成。
typedef struct _ENC_PARAM_ {
int x_dim; // 待压缩帧的x大小
int y_dim; // 待压缩帧的y大小
float framerate;// 待压缩帧序列本来的帧频
long bitrate; // 压缩后的目标帧率
long rc_period; // the intended rate control averaging period
long rc_reaction_period; // 帧率控制的反向时间
long rc_reaction_ratio; // 低/高端帧频控制的比例
long max_key_interval; // 关键帧之间的最大间隔
int max_quantizer; // 量化器的上限值
int min_quantizer; // 量化器的下限值
int search_range; // 运动估计的向前搜索范围
} ENC_PARAM;
typedef struct _REFERENCE
{
unsigned long handle;
float framerate;
long bitrate;
long rc_period;
long rc_reaction_period;
long rc_reaction_ratio;
long max_key_interval;
int x_dim, y_dim;
int prev_rounding;
int search_range;
int max_quantizer;
int min_quantizer;
long seq;
long curr_run; /* 最后关键帧前的当前运行 */
Vop *current; /* 要编码的当前帧 */
Vop *reference; /* 参考帧 - 重构之前的帧 */
Vop *reconstruct; /* 中间重构的帧 - used in inter */
Vop *error; /* 中间的错误帧 - 用整型来保存预测错误 */
struct _REFERENCE *pnext;
} REFERENCE;
要对视频进行MPEG-4压缩,首先要以ENC_OPT_INT方式调用encore()函数,
然后对待压缩的每一个视频帧都按顺序以ENC_OPT_WRITE方式调用一次encore()函数,
最后以ENC_OPT_RELEASE方式调用一次encore()就可以了。
int encore(unsigned long handle, unsigned long enc_opt, void *param1, void *param2)
{
static REFERENCE *ref = NULL;
static VolConfig *vol_config;
// 下面的链表保存之前压缩过的参考
REFERENCE *ref_curr, *ref_last = NULL;
int x_dim, y_dim, size, length;
int headerbits = 0;
Vop *curr;
ref_curr = ref_last = ref;
while (ref_curr != NULL)
{
if (ref_curr->handle == handle) break;
ref_last = ref_curr;
ref_curr = ref_last->pnext;
}
// 如果没有找到匹配的参考,则创建一个新的句柄
if (ref_curr == NULL)
{
if (enc_opt & ENC_OPT_RELEASE) return ENC_OK;
ref_curr = (REFERENCE *)malloc(sizeof(REFERENCE));
ref_curr->handle = handle;
ref_curr->seq = 0;
ref_curr->curr_run = 0;
ref_curr->pnext = NULL;
if (ref) ref_last->pnext = ref_curr;
else ref = ref_curr;
}
// 按要求初始化一个句柄
if (enc_opt & ENC_OPT_INIT)
{
#ifdef _RC_
ftrace = fopen("trace.txt", "w");
fflush(ftrace);
#endif
init_fdct_enc();
init_idct_enc();
// 初始化率控制器
ref_curr->framerate = ((ENC_PARAM *)param1)->framerate;
ref_curr->bitrate = ((ENC_PARAM *)param1)->bitrate;
ref_curr->rc_period = ((ENC_PARAM *)param1)->rc_period;
ref_curr->rc_reaction_period = ((ENC_PARAM *)param1)->rc_reaction_period;
ref_curr->rc_reaction_ratio = ((ENC_PARAM *)param1)->rc_reaction_ratio;
ref_curr->x_dim = ((ENC_PARAM *)param1)->x_dim;
ref_curr->y_dim = ((ENC_PARAM *)param1)->y_dim;
ref_curr->max_key_interval = ((ENC_PARAM *)param1)->max_key_interval;
ref_curr->search_range = ((ENC_PARAM *)param1)->search_range;
ref_curr->max_quantizer = ((ENC_PARAM *)param1)->max_quantizer;
ref_curr->min_quantizer = ((ENC_PARAM *)param1)->min_quantizer;
ref_curr->current = AllocVop(ref_curr->x_dim, ref_curr->y_dim);
ref_curr->reference = AllocVop(ref_curr->x_dim + 2 * 16,
ref_curr->y_dim + 2 * 16);
ref_curr->reconstruct = AllocVop(ref_curr->x_dim, ref_curr->y_dim);
ref_curr->error = AllocVop(ref_curr->x_dim, ref_curr->y_dim);
init_vop(ref_curr->current);
init_vop(ref_curr->reference);
init_vop(ref_curr->reconstruct);
init_vop(ref_curr->error);
ref_curr->reference->hor_spat_ref = -16;
ref_curr->reference->ver_spat_ref = -16;
SetConstantImage(ref_curr->reference->y_chan, 0);
vol_config = (VolConfig *)malloc(sizeof(VolConfig));
init_vol_config(vol_config);
vol_config->frame_rate = ref_curr->framerate;
vol_config->bit_rate = ref_curr->bitrate;
RateCtlInit(8 /* initial quant*/, vol_config->bit_rate / vol_config->frame_rate,
ref_curr->rc_period, ref_curr->rc_reaction_period, ref_curr->rc_reaction_ratio);
return ENC_OK;
}
// 释放和句柄有关的参考
if (enc_opt & ENC_OPT_RELEASE)
{
if (ref_curr == ref) ref = NULL;
else ref_last->pnext = ref_curr->pnext;
if (ref_curr->current) FreeVop(ref_curr->current);
if (ref_curr->reference) FreeVop(ref_curr->reference);
if (ref_curr->reconstruct) FreeVop(ref_curr->reconstruct);
if (ref_curr->error) FreeVop(ref_curr->error);
free(ref_curr);
free(vol_config);
if (ftrace) {
fclose(ftrace);
ftrace = NULL;
};
return ENC_OK;
}
// 对相关参数进行初始化 (need to be cleaned later)
max_quantizer = ref_curr->max_quantizer;
min_quantizer = ref_curr->min_quantizer;
x_dim = ref_curr->x_dim;
y_dim = ref_curr->y_dim;
size = x_dim * y_dim;
curr = ref_curr->current;
curr->width = x_dim;
curr->height = y_dim;
curr->sr_for = ref_curr->search_range;
curr->fcode_for = get_fcode(curr->sr_for);
// 下面对输入的参数进行变换
// 下面这个函数使用图像数据由short int表示变成了由unsigned char 表示
// 因为这部分MPEG-4编程代码之前的MoMuSys代码是用short int存放数据的
YUV2YUV(x_dim, y_dim, ((ENC_FRAME *)param1)->image,
curr->y_chan->f, curr->u_chan->f, curr->v_chan->f);
// 调整当前图像的取整方式
curr->rounding_type = 1 - ref_curr->prev_rounding;
Bitstream_Init((void *)(((ENC_FRAME *)param1)->bitstream));
if (ref_curr->seq == 0)
{
headerbits = PutVoVolHeader(x_dim, y_dim, curr->time_increment_resolution, ref_curr->framerate);
}
#ifdef _RC_
fflush(ftrace);
fprintf(ftrace, "\nCoding frame #%d\n", ref_curr->seq);
#endif
if (ref_curr->curr_run % ref_curr->max_key_interval == 0)
{
curr->prediction_type = I_VOP;
#ifdef _RC_
fprintf(ftrace, "This frame is forced to be coded in INTRA.\n");
fprintf(ftrace, "It has been %d frame since the last INTRA.\n", ref_curr->curr_run);
#endif
}
else
curr->prediction_type = P_VOP;
// 视频对象的编码函数 Code the image data (YUV) of the current image
VopCode(curr,
ref_curr->reference,
ref_curr->reconstruct,
ref_curr->error,
1, //enable_8x8_mv,
(float)ref_curr->seq/ref_curr->framerate, // time
vol_config);
length = Bitstream_Close();
((ENC_FRAME *)param1)->length = length;
// 更新率控制参数
RateCtlUpdate(length * 8);
ref_curr->prev_rounding = curr->rounding_type;
ref_curr->seq ++;
ref_curr->curr_run ++;
if (curr->prediction_type == I_VOP)
{
((ENC_RESULT *)param2)->isKeyFrame = 1;
ref_curr->curr_run = 1;
}
else
((ENC_RESULT *)param2)->isKeyFrame = 0;
return ENC_OK;
}
上面encore()函数进行的编码操作是由VopCode()函数完成的,函数对输入的视频对象平面进行形状、纹理和运动信息编码。
输入Vop必须是有封闭界面的。函数包含对一个帧进行编码的基本流程。
struct vop
{
/* Actual syntax elements for VOP (standard) */
Int prediction_type; /* VOP 预测类型 */
Int mod_time_base; /* VOP modulo time base (absolute) */
Float time_inc; /* VOP 时间增量 (相对于上一mtb) */
Int rounding_type;
Int width; /* VOP 高 (smallest rectangle) */
Int height; /* VOP 宽 (smallest rectangle) */
Int hor_spat_ref; /* VOP 水平参考. (for composition) */
Int ver_spat_ref; /* VOP 竖直参考. (for composition) */
Int intra_dc_vlc_thr;
Int quantizer; /* P-VOPs的VOP 量化器 */
Int intra_quantizer; /* I-VOPs的VOP 量化器 */
/* 从VOL拷贝的 Syntax 元素 */
Int time_increment_resolution;
Int intra_acdc_pred_disable; /* VOL disable INTRA DC prediction */
Int sr_for; /* VOP 运动向向量的搜索范围 */
Int fcode_for; /* VOP 运动向量的动态范围 */
Int quant_precision;
Int bits_per_pixel;
/* 指向图像 (YUVA) 和相关系 VOPs */
Image *y_chan; /* VOP texture 中的Y组件*/
Image *u_chan; /* VOP texture 中的U组件*/
Image *v_chan; /* VOP texture 中的V组件*/
};
typedef struct vop Vop;
Void VopCode(Vop *curr, // 编码的视频对象
Vop *reference, // 参考视频对象平面
Vop *reconstruct, // 前的构造VOP
Vop *error,
Int enable_8x8_mv, // 8X8运行向量标志
Float time, // 帧间间隔时间
VolConfig *vol_config)
{
ImageF *mot_x=NULL, *mot_y=NULL;
Image *MB_decisions=NULL;
Int edge,f_code_for=1;
Vop *error_vop=NULL;
Int vop_quantizer;
Float mad_P = 0., mad_I = 0.;
Float IntraMBRatio = 1.;
Int numberMB, i, IntraMB;
edge = 0;
f_code_for = curr->fcode_for;
if (curr->prediction_type == P_VOP)
{
/* 进行运动估计和补偿*/
MotionEstimationCompensation(curr, reference,
enable_8x8_mv, edge ,f_code_for,
reconstruct, &mad_P, &mot_x,&mot_y,&MB_decisions);
/* 计算各宏块在视频对象平面内部的百分比 */
IntraMB = 0;
numberMB = MB_decisions->x * MB_decisions->y;
for (i = 0; i < numberMB; i ++)
if (MB_decisions->f[i] == MBM_INTRA) IntraMB ++;
IntraMBRatio = (float)IntraMB / (float)numberMB;
#ifdef _RC_
fprintf(ftrace, "ME with MAD : %f\n", mad_P);
fprintf(ftrace, "%4.2f of the MBs are I-MBs.\n", IntraMBRatio);
#endif
}
else
mad_P = SCENE_CHANGE_THREADHOLD * 2;
if ((mad_P < SCENE_CHANGE_THREADHOLD / 3) ||
((mad_P < SCENE_CHANGE_THREADHOLD) && (IntraMBRatio < MB_RATIO_THREADHOLD)))
{
// mad 满足要求,继续按P帧形成编码
curr->prediction_type = P_VOP;
error->prediction_type = P_VOP;
#ifdef _RC_
fprintf(ftrace, "Coding mode : INTER\n");
#endif
vop_quantizer = RateCtlGetQ(mad_P);
curr->quantizer = vop_quantizer;
error->quantizer = vop_quantizer;
#ifdef _RC_DEBUG_
fprintf(stdout, "RC: >>>>> New quantizer= %d\n", vop_quantizer);
#endif
SubImage(curr->y_chan, reconstruct->y_chan, error->y_chan);
SubImage(curr->u_chan, reconstruct->u_chan, error->u_chan);
SubImage(curr->v_chan, reconstruct->v_chan, error->v_chan);
BitstreamPutVopHeader(curr,time,vol_config);
VopShapeMotText(error, reconstruct, MB_decisions,
mot_x, mot_y, f_code_for,
GetVopIntraACDCPredDisable(curr), reference,
NULL/*mottext_bitstream*/);
}
else
{
// mad 太大.
// 按I帧形式编码
curr->prediction_type = I_VOP;
curr->rounding_type = 1;
#ifdef _RC_
fprintf(ftrace, "Coding mode : INTRA\n");
#endif
// 因为上次计算mad是假定在内部编码基础上的,所以应该重新计算MAD值
if (mad_I == 0.) mad_I = (Float) compute_MAD(curr);
vop_quantizer = RateCtlGetQ(mad_I);
curr->intra_quantizer = vop_quantizer;
curr->rounding_type = 1;
BitstreamPutVopHeader(curr,time,vol_config);
/* 内部编码模式下对纹理进行编码 */
VopCodeShapeTextIntraCom(curr,
reference,
NULL/*texture_bitstream*/
);
}
if (MB_decisions) FreeImage(MB_decisions);
if (mot_x) FreeImage(mot_x);
if (mot_y) FreeImage(mot_y);
ImageRepetitivePadding(reference->y_chan, 16);
ImageRepetitivePadding(reference->u_chan, 8);
ImageRepetitivePadding(reference->v_chan, 8);
Bitstream_NextStartCode();
return;
} /* CodeVop() */
这方面的代码文件为mot_est_comp.h、 mot_est_comp.c、mot_est_mb.h和mot_est_mb.c, 包括运动估计和运行补偿两个部分。
(3、1)
运动估计和预测补偿函数MotionEstimationCompensation。
功能:估计运动方向并进行运动补偿运算。在高级模式估计运动向量,函数输出为4个,包含运动向量横纵坐标的图像及其处理模式。
Void
MotionEstimationCompensation (
Vop *curr_vop, /* <-- 当前视频面对象 */
Vop *prev_rec_vop, /* <-- 重建的参考视频对象平面(1/2 pixel)*/
Int enable_8x8_mv, /* <-- 8x8 运动向量 (=1) or only 16x16 运动向量 (=0)*/
Int edge, /* <-- 限制(==0)/非限制(==edge) mode*/
Int f_code, /* <-- 运动向量搜索范围 1/2 pel: 1=32,2=64,...,7=2048*/
Vop *curr_comp_vop, /* <-> 进行运动补偿的视频对象平面*/
Float *mad, /* <-> ME/MC结果的容忍值*/
Image **mot_x, /* --> 运动向量水平坐标*/
Image **mot_y, /* --> 运动向量竖直坐标*/
Image **mode /* --> 每个个宏块的模式*/
)
{
Image *pr_rec_y; /* 参考图像(重建后) */
Image *pi_y; /* Interp.ref.image Y */
Image *mode16;
Image *mv16_w;
Image *mv16_h;
Image *mv8_w;
Image *mv8_h;
SInt *prev_ipol_y, /* 有边界的参考原始图像 */
*prev_orig_y; /* Ref. original image with edge (subimage) */
Int vop_width, vop_height;
Int br_x;
Int br_y;
Int br_height;
Int br_width;
Int mv_h, mv_w;
/*GETBBR*/
br_y=curr_vop->ver_spat_ref;
br_x=curr_vop->hor_spat_ref;
br_height=curr_vop->height;
br_width=curr_vop->width;
mv_h=br_height/MB_SIZE;
mv_w=br_width/MB_SIZE;
/*
** 假定pre_rec_vop和prev_vop初始值相等
*/
vop_width=prev_rec_vop->width;
vop_height=prev_rec_vop->height;
/*
** 得到图像并将它们插入
*/
pr_rec_y = prev_rec_vop->y_chan;
prev_orig_y = (SInt*)GetImageData(pr_rec_y);
pi_y = AllocImage (2*vop_width, 2*vop_height, SHORT_TYPE);
InterpolateImage(pr_rec_y, pi_y, GetVopRoundingType(curr_vop));
prev_ipol_y = (SInt*)GetImageData(pi_y);
/*
* 给所有运动向量和模式数据分配存储空间
*
*/
mode16=AllocImage (mv_w,mv_h,SHORT_TYPE);
SetConstantImage (mode16,(Float)MBM_INTRA);
/*
** mv16 有2X2个重复的运动向量值,以便在CodeVopVotion和MotionEstimation
* 之间共享运动向量的预测函数
*/
mv16_w=AllocImage (mv_w*2,mv_h*2,FLOAT_TYPE);
mv16_h=AllocImage (mv_w*2,mv_h*2,FLOAT_TYPE);
mv8_w =AllocImage (mv_w*2,mv_h*2,FLOAT_TYPE);
mv8_h =AllocImage (mv_w*2,mv_h*2,FLOAT_TYPE);
SetConstantImage (mv16_h,+0.0);
SetConstantImage (mv16_w,+0.0);
SetConstantImage (mv8_h,+0.0);
SetConstantImage (mv8_w,+0.0);
SetConstantImage (curr_comp_vop->u_chan, 0);
SetConstantImage (curr_comp_vop->v_chan, 0);
/* 对每一个宏块计算它的运动向量和模式
*/
MotionEstCompPicture(
(SInt *)GetImageData(GetVopY(curr_vop)), //curr_vop,
prev_orig_y, /* Y padd with edge */
prev_ipol_y, /* Y interpolated (from pi_y) */
(SInt*)GetImageData(prev_rec_vop->u_chan) + (vop_width/2) * (16/2) + (16/2),
(SInt*)GetImageData(prev_rec_vop->v_chan) + (vop_width/2) * (16/2) + (16/2),
prev_rec_vop->hor_spat_ref,
prev_rec_vop->ver_spat_ref,
vop_width,vop_height,
enable_8x8_mv,
edge,
GetVopSearchRangeFor(curr_vop),
f_code,
GetVopRoundingType(curr_vop),
br_x,br_y, /* pos. of the bounding rectangle */
br_width,br_height, /* dims. of the bounding rectangle */
(SInt*)GetImageData(curr_comp_vop->y_chan),
(SInt*)GetImageData(curr_comp_vop->u_chan),
(SInt*)GetImageData(curr_comp_vop->v_chan),
mad,
(Float*)GetImageData(mv16_w),
(Float*)GetImageData(mv16_h),
(Float*)GetImageData(mv8_w),
(Float*)GetImageData(mv8_h),
(SInt*) GetImageData(mode16)
);
/* 将结果转MOMUSYS格式 */
GetMotionImages(mv16_w, mv16_h, mv8_w, mv8_h, mode16, mot_x, mot_y, mode);
/* 释放动态申请的内存Deallocate dynamic memory */
FreeImage(mv16_w); FreeImage(mv16_h);
FreeImage(mv8_w); FreeImage(mv8_h);
FreeImage(mode16);
FreeImage(pi_y);
}
(3、2) 运动向量和预测误差计算的函数MotionEstCompPicture().
这个函数在MotionEstimationCompensation()函数中调用到了。这个函数计算运动向量和预测器的误差,并且完成运动补偿计算。
运动向量一般为8X8和16X16大小,预测器误差是针对整个视频对象平面而言的,并且运动补偿也是对整个视频对象平面而言的。
Void MotionEstCompPicture (
SInt *curr, // 当前视频对象平面
SInt *prev, // 用边界填充的原始Y向量
SInt *prev_ipol_y,//
SInt *prev_ipol_u,// 用边界填充的原始U分量
SInt *prev_ipol_v,// 用边界填充的原始V分量
Int prev_x, // 前一个视频对象的水平位置位置的绝对值
Int prev_y, // 前一个视频对象的竖直位置位置的绝对值
Int vop_width, // 前一个视频对象的水平方向大小
Int vop_height, // 前一个视频对象的竖直方向大小
Int enable_8x8_mv,// 如果运动向量为8x8,则为1; 如果运动向量为16X16,则为0
Int edge, // 围绕参考视频对象平面的边界
Int sr_for, // 向前搜索的范围
Int f_code, // 运动向量的搜索范围
Int rounding_type,// 当前视频对象环绕的类型
Int br_x, // 当前视频对象水平空间位置的绝对值
Int br_y, // 当前视频对象竖直空间位置的绝对值
Int br_width, // 当前视频对象平面边界的宽度
Int br_height, // 当前视频对象平面边界的高度
SInt *curr_comp_y,// 运动补偿的当前Y值
SInt *curr_comp_u,// 运动补偿的当前U值
SInt *curr_comp_v,// 运动补偿的当前V值
Float *mad, // 当前ME/MC的误差的容忍值
Float *mv16_w, // 16X16运动向量的水平预测值
Float *mv16_h, // 16X16运动微量的竖直预测值
Float *mv8_w, // 8X8运动向量的水平预测值
Float *mv8_h, // 8X8运动向量的竖直预测值
SInt *mode16 // 预测运动向量的模式
);
(3、3) 运动向量图像编码函数Bits_CountMB_Motion()。
它在VopCode中按P帧形成编码时调用的VopShapeMotText函数中被调用。(在下面我们会讲到VopShapeMotText函数)
功能:根据宏块的编码模式和Alpha平面图像对运动图像进行编码。编码后的数据接在位流后面。
返回值:Int mv_bits, 通过这个数据返回增加到位流的位数。
描述:
函数中不考虑图像大小的合法性;
假设输出图像的空间已经被申请好了;
不对透明的宏块进行编码;
在不完全透明的宏块里的8X8的透明块的运动向量被转成MV(0, 0),使它不会在重建块和运动向量预测时被使用。
Int
Bits_CountMB_Motion(
Image *mot_h, /* <-- 运动向量的所有水平分量- per block */
Image *mot_v, /* <-- 运动向量的所有竖直分量 - per block */
Image *alpha, /* <-- alpha平面图像 - per block */
Image *modes, /* <-- 宏块的编码模式 (SInt) - per MB*/
Int h, /* <-- 宏块的水平坐标 */
Int v, /* <-- 宏块的竖直坐标 */
Int f_code, /* <-- 运动向量是1/2或1/4 pel 单位 1=32,2=64,...,7=2048 */
/* <-- flag for quarter pel MC mode */
Int quarter_pel,/* 1/4 pel 单位MC模式标志 */
Image *bs, /* --> 输出 (SInt) */
Int error_res_disable,
Int after_marker,
Int **slice_nb,
Int arbitrary_shape
)
{
Int vdim, hdim; /* 宏块的大小 */
Float *ph, *pv; /* 运动向量 */
SInt *pm; /* 模式 */
SInt *pa; /* Alpha */
Int mode;
Int bits_mot = 0;
/* From switch statement */
Int i, error_flag=0,mvx=0,mvy=0;
Float pred_h, pred_v;
Float diff_h, diff_v;
Int bh, bv;
Int local_f_code;/* MW QPEL 07-JUL-1998 */
Float subdim; /* MW QPEL 07-JUL-1998 */
vdim = (Int)modes->y;
hdim = (Int)modes->x;
ph=(Float*)GetImageData(mot_h);
pv=(Float*)GetImageData(mot_v);
pm=(SInt*)GetImageData(modes);
pa=NULL;//(SInt*)GetImageData(alpha);
/* MW QPEL 07-JUL-1998 >> */
/* 根据quarter_pel设置local_f_code和subdim的值 */
if (quarter_pel)
{
local_f_code = f_code+1;
subdim = 4.0;
}
else
{
local_f_code = f_code;
subdim = 2.0;
}
/* << MW QPEL 07-JUL-1998 */
switch (mode=MB_MODE(h,v))
{
case MBM_INTRA:
break;
case MBM_INTER16:
/* 进行预测 */
find_pmvs(mot_h,mot_v,modes,alpha,h,v,0,MBM_TRANSPARENT,
/* MW QPEL 07-JUL-1998 */
quarter_pel,&error_flag,&mvx,&mvy,0);
pred_h=((Float)mvx)/subdim; /* MW QPEL 07-JUL-1998 */
pred_v=((Float)mvy)/subdim; /* MW QPEL 07-JUL-1998 */
/* MW QPEL 07-JUL-1998 */
bits_mot += WriteMVcomponent(local_f_code, (Int)(subdim*(MBV_H(h,v) - pred_h)), bs);
/* MW QPEL 07-JUL-1998 */
bits_mot += WriteMVcomponent(local_f_code, (Int)(subdim*(MBV_V(h,v) - pred_v)), bs);
break;
case MBM_INTER8:
i=1;
for (bv=0; bv<=1; bv++)
for (bh=0; bh<=1; bh++)
{
find_pmvs(mot_h,mot_v,modes,alpha,h,v,i,MBM_TRANSPARENT,
/* MW QPEL 07-JUL-1998 */
quarter_pel,&error_flag,&mvx,&mvy,0);
pred_h=((Float)mvx)/subdim; /* MW QPEL 07-JUL-1998 */
pred_v=((Float)mvy)/subdim; /* MW QPEL 07-JUL-1998 */
i++;
diff_h=BV_H(h,v,bh,bv)-pred_h;
diff_v=BV_V(h,v,bh,bv)-pred_v;
/* MW QPEL 07-JUL-1998 */
bits_mot += WriteMVcomponent(local_f_code, (Int)(subdim*diff_h), bs);
/* MW QPEL 07-JUL-1998 */
bits_mot += WriteMVcomponent(local_f_code, (Int)(subdim*diff_v), bs);
}
break;
}
return bits_mot;
}
Void /* MVP/Noel */
find_pmvs(
Image *mot_x, /* x-motion vector field */
Image *mot_y, /* y-motion vector field */
Image *MB_decisions, /* MB modes */
Image *B_decisions, /* field with number of vectors per MB */
Int x, /* xpos of the MB in multiples of 16 (hor coord) */
Int y, /* ypos of the MB in multiples of 16 (ver coord) */
Int block, /* block 号 (0 if one vector per MB, 1..4 else) */
Int transparent_value, /* 透明度的值 (0=enc, 2=dec) */
Int quarter_pel, /* MW QPEL 06-JUL-1998 */ /* 预测量化器的标志 pel mc */
Int *error_flag, /* 看是否一个错误发生 */
Int *mvx, /* 水平预测运动向量 [ in half-pixels units ] */
Int *mvy, /* 竖直预测运动向量 [ in half-pixels units ] */
Int newgob
)
{
Float p1x,p2x,p3x;
Float p1y,p2y,p3y;
Int xin1, xin2, xin3;
Int yin1, yin2, yin3;
Int vec1, vec2, vec3;
Int rule1, rule2, rule3;
Int subdim; /* MW QPEL 06-JUL-1998 */
Float *motxdata = (Float *) GetImageData(mot_x);
Float *motydata = (Float *) GetImageData(mot_y);
Int xM = GetImageSizeX(mot_x);
Int xB = xM;
Int mb_mode, sum;
/* MW QPEL 06-JUL-1998 >> */
if (quarter_pel)
{
subdim=4;
}
else
{
subdim=2;
}
/* << MW QPEL 06-JUL-1998 */
/* In a previous version, a MB vector (block = 0) was predicted the same way
as block 1, which is the most likely interpretation of the VM.
Therefore, if we have advanced pred. mode, and if all MBs around have
only one 16x16 vector each, we chose the appropiate block as if these
MBs have 4 vectors.
This different prediction affects only 16x16 vectors of MBs with
transparent blocks.
In the current version, we choose for the 16x16 mode the first
non-transparent block in the surrounding MBs
*/
switch (block)
{
case 0:
vec1 = 1 ; yin1 = y ; xin1 = x-1;
vec2 = 2 ; yin2 = y-1; xin2 = x;
vec3 = 2 ; yin3 = y-1; xin3 = x+1;
break;
case 1:
vec1 = 1 ; yin1 = y ; xin1 = x-1;
vec2 = 2 ; yin2 = y-1; xin2 = x;
vec3 = 2 ; yin3 = y-1; xin3 = x+1;
break;
case 2:
vec1 = 0 ; yin1 = y ; xin1 = x;
vec2 = 3 ; yin2 = y-1; xin2 = x;
vec3 = 2 ; yin3 = y-1; xin3 = x+1;
break;
case 3:
vec1 = 3 ; yin1 = y ; xin1 = x-1;
vec2 = 0 ; yin2 = y ; xin2 = x;
vec3 = 1 ; yin3 = y ; xin3 = x;
break;
case 4:
vec1 = 2 ; yin1 = y ; xin1 = x;
vec2 = 0 ; yin2 = y ; xin2 = x;
vec3 = 1 ; yin3 = y ; xin3 = x;
break;
default:
printf("Illegal block number in find_pmv (mot_decode.c)");
*error_flag = 1;
*mvx=*mvy=0;
return ;
}
if (block==0)
{
/* according to the motion encoding, we must choose a first non-transparent
block in the surrounding MBs (16-mode)
*/
if (x>0 /*&& ValidCandidateMVP(x,y,xin1,yin1,vec1,xB,transparent_value,
MB_decisions,dcsn_data)*/)
rule1 = 0;
else
rule1 = 1;
if ((y>0 && newgob==0) /*&& ValidCandidateMVP(x,y,xin2,yin2,vec2,xB,transparent_value,
MB_decisions,dcsn_data)*/)
rule2 = 0;
else
rule2 = 1;
if ((x != xB/2 -1) &&
((y>0 && newgob==0)) /*&& ValidCandidateMVP(x,y,xin3,yin3,vec3,xB,transparent_value,
MB_decisions,dcsn_data)*/)
rule3 = 0;
else
rule3 = 1;
}
else
{
/* check borders for single blocks (advanced mode) */
/* rule 1 */
/* left border */
if (((block == 1 || block == 3) && (x == 0)) /*||
/* left block/MB is transparent *
(!ValidCandidateMVP(x,y,xin1,yin1,vec1,xB,transparent_value,
MB_decisions,dcsn_data))*/)
rule1 = 1;
else
rule1 = 0;
/* rule 2 */
/* top border */
if (((block == 1 || block == 2) && (y == 0)) /*||
/* top block/MB is transparent *
(!ValidCandidateMVP(x,y,xin2,yin2,vec2,xB,transparent_value,
MB_decisions,dcsn_data))*/)
rule2 = 1;
else
rule2 = 0;
/* rule 3 */
if (((block == 1 || block == 2) && (x == xB/2 -1 || y == 0)) /*||
/* right & top border *
/* right block/MB is transparent *
(!ValidCandidateMVP(x,y,xin3,yin3,vec3,xB,transparent_value,
MB_decisions,dcsn_data))*/)
rule3 = 1;
else
rule3 = 0;
}
if (rule1 )
{
p1x = p1y = 0;
}
else if (((mb_mode = ModeMB(MB_decisions,xin1,yin1)) >= MBM_FIELD00) && (mb_mode <= MBM_FIELD11))
{
/* MW QPEL 06-JUL-1998 */
sum = subdim*(BV(motxdata, xM, xin1, yin1, 0, 0) + BV(motxdata, xM, xin1, yin1, 1, 0));
p1x = (Float)((sum & 3) ? ((sum | 2) >> 1) : (sum >> 1))/(Float)subdim;
sum = subdim*(BV(motydata, xM, xin1, yin1, 0, 0) + BV(motydata, xM, xin1, yin1, 1, 0));
p1y = (Float)((sum & 3) ? ((sum | 2) >> 1) : (sum >> 1))/(Float)subdim;
}
else
{
p1x = BV(motxdata, xM, xin1, yin1, vec1&0x1, vec1>>1 );
p1y = BV(motydata, xM, xin1, yin1, vec1&0x1, vec1>>1 );
}
if (rule2)
{
p2x = p2y = 0 ;
}
else if (((mb_mode = ModeMB(MB_decisions,xin2,yin2)) >= MBM_FIELD00) && (mb_mode <= MBM_FIELD11))
{
/* MW QPEL 06-JUL-1998 */
sum = subdim*(BV(motxdata, xM, xin2, yin2, 0, 0) + BV(motxdata, xM, xin2, yin2, 1, 0));
p2x = (Float)((sum & 3) ? ((sum | 2) >> 1) : (sum >> 1))/(Float)subdim;
sum = subdim*(BV(motydata, xM, xin2, yin2, 0, 0) + BV(motydata, xM, xin2, yin2, 1, 0));
p2y = (Float)((sum & 3) ? ((sum | 2) >> 1) : (sum >> 1))/(Float)subdim;
}
else
{
p2x = BV(motxdata, xM, xin2, yin2, vec2&0x1, vec2>>1 );
p2y = BV(motydata, xM, xin2, yin2, vec2&0x1, vec2>>1 );
}
if (rule3 )
{
p3x = p3y =0;
}
else if (((mb_mode = ModeMB(MB_decisions,xin3,yin3)) >= MBM_FIELD00) && (mb_mode <= MBM_FIELD11))
{
/* MW QPEL 06-JUL-1998 */
sum = subdim*(BV(motxdata, xM, xin3, yin3, 0, 0) + BV(motxdata, xM, xin3, yin3, 1, 0));
p3x = (Float)((sum & 3) ? ((sum | 2) >> 1) : (sum >> 1))/(Float)subdim;
sum = subdim*(BV(motydata, xM, xin3, yin3, 0, 0) + BV(motydata, xM, xin3, yin3, 1, 0));
p3y = (Float)((sum & 3) ? ((sum | 2) >> 1) : (sum >> 1))/(Float)subdim;
}
else
{
p3x = BV(motxdata, xM, xin3, yin3, vec3&0x1, vec3>>1 );
p3y = BV(motydata, xM, xin3, yin3, vec3&0x1, vec3>>1 );
}
if (rule1 && rule2 && rule3 )
{
/* all MBs are outside the VOP */
*mvx=*mvy=0;
}
else if (rule1+rule2+rule3 == 2)
{
/* two of three are zero */
/* MW QPEL 06-JUL-1998 */
*mvx=(Int) subdim*(p1x+p2x+p3x); /* MW QPEL 06-JUL-1998 */
*mvy=(Int) subdim*(p1y+p2y+p3y); /* MW QPEL 06-JUL-1998 */
}
else
{
/* MW QPEL 06-JUL-1998 */
/* MW QPEL 06-JUL-1998 */
*mvx=(Int)(subdim*(p1x+p2x+p3x-MAX(p1x,MAX(p2x,p3x))-MIN(p1x,MIN(p2x,p3x))));
/* MW QPEL 06-JUL-1998 */
*mvy=(Int)(subdim*(p1y+p2y+p3y-MAX(p1y,MAX(p2y,p3y))-MIN(p1y,MIN(p2y,p3y))));
}
#ifdef _DEBUG_PMVS_
fprintf(stdout,"find_pmvs (%2d,%2d, rule %1d%1d%1d) :\np1 %6.2f / %6.2f\np2 %6.2f / %6.2f\np3 %6.2f / %6.2f\n",x,y,rule1,rule2,rule3,p1x,p1y,p2x,p2y,p3x,p3y);
#endif
return;
}
这部分代码所在的文件是 text_code.h、text_code.c.
(4、1)帧内纹理编码函数VopCodeShapeTextIntraCom()(VOP内部纹理编码)。本函数实现的是一个视频对象平面的帧内纹理编码,
它包括了组合形状编码模式和纹理编码模式。
它在VopCode中按 I 帧形成编码时调用的。
Void VopCodeShapeTextIntraCom(
Vop *curr, // 当前要进行编码的视频对象
Vop *rec_curr, // 当前重建的视频对象平面
Image *mottext_bitstream)
{
Int QP = GetVopIntraQuantizer(curr);
Int Mode = MODE_INTRA;
Int* qcoeff;
Int i, j;
Int CBP, COD;
Int CBPY, CBPC;
Int num_pixels = GetImageSizeX(GetVopY(curr));
Int num_lines = GetImageSizeY(GetVopY(curr));
Int vop_type;
Int ***DC_store; // 为做 DC/AC 预测 存储宏块系数
Int MB_width = num_pixels / MB_SIZE; // 宏块的宽
Int MB_height = num_lines / MB_SIZE; // 宏块的高
Int m;
Int ACpred_flag=-1;
Int direction[6];
Int switched=0;
Int DQUANT =0;
Bits nbits, *bits;
bits = &nbits;
qcoeff = (Int *) malloc (sizeof (Int) * 384);
#ifdef _RC_DEBUG_
fprintf(ftrace, "RC - VopCodeShapeTextIntraCom(): ---> CODING WITH: %d \n",QP);
#endif
for (i = 0; i < 6; i++)
direction[i] = 0;
/* 给3D矩阵分配空间,矩阵是用来跟踪为DC或AC判断预测服务的预测值 */
DC_store = (Int ***)calloc(MB_width*MB_height, sizeof(Int **));
for (i = 0; i < MB_width*MB_height; i++)
{
DC_store[i] = (Int **)calloc(6, sizeof(Int *));
for (j = 0; j < 6; j++)
DC_store[i][j] = (Int *)calloc(15, sizeof(Int));
}
Bits_Reset(bits);
vop_type = PCT_INTRA;
// 对宏块循环
for (j = 0; j < num_lines/MB_SIZE; j++)
{
for (i = 0; i < num_pixels/MB_SIZE; i++)
{
DQUANT = 0;
COD = 0;
bits->no_intra++;
// 对宏块编码
// curee rec_curr qcoeff是输出参数
CodeMB(curr, rec_curr, NULL, i*MB_SIZE, j*MB_SIZE,
num_pixels, QP+DQUANT, MODE_INTRA, qcoeff);
m =0;
DC_store[j*MB_width+i][0][m] = qcoeff[m]*cal_dc_scaler(QP+DQUANT,1);
DC_store[j*MB_width+i][1][m] = qcoeff[m+64]*cal_dc_scaler(QP+DQUANT,1);
DC_store[j*MB_width+i][2][m] = qcoeff[m+128]*cal_dc_scaler(QP+DQUANT,1);
DC_store[j*MB_width+i][3][m] = qcoeff[m+192]*cal_dc_scaler(QP+DQUANT,1);
DC_store[j*MB_width+i][4][m] = qcoeff[m+256]*cal_dc_scaler(QP+DQUANT,2);
DC_store[j*MB_width+i][5][m] = qcoeff[m+320]*cal_dc_scaler(QP+DQUANT,2);
for (m = 1; m < 8; m++)
{
DC_store[j*MB_width+i][0][m] = qcoeff[m];
DC_store[j*MB_width+i][1][m] = qcoeff[m+64];
DC_store[j*MB_width+i][2][m] = qcoeff[m+128];
DC_store[j*MB_width+i][3][m] = qcoeff[m+192];
DC_store[j*MB_width+i][4][m] = qcoeff[m+256];
DC_store[j*MB_width+i][5][m] = qcoeff[m+320];
}
for (m = 0; m < 7; m++)
{
DC_store[j*MB_width+i][0][m+8] = qcoeff[(m+1)*8];
DC_store[j*MB_width+i][1][m+8] = qcoeff[(m+1)*8+64];
DC_store[j*MB_width+i][2][m+8] = qcoeff[(m+1)*8+128];
DC_store[j*MB_width+i][3][m+8] = qcoeff[(m+1)*8+192];
DC_store[j*MB_width+i][4][m+8] = qcoeff[(m+1)*8+256];
DC_store[j*MB_width+i][5][m+8] = qcoeff[(m+1)*8+320];
}
CBP = FindCBP(qcoeff,Mode,64);
/* 进行DC/AC预测,适当地量化系数的值*/
if (GetVopIntraACDCPredDisable(curr) == 0)
{
// 做 DC/AC 预测
ACpred_flag = doDCACpred(qcoeff, &CBP, 64, i, j, DC_store,
QP+DQUANT, MB_width,
direction,GetVopMidGrey(curr));
}
else
ACpred_flag = -1;
switched = IntraDCSwitch_Decision(Mode,
GetVopIntraDCVlcThr(curr),
QP);
if (switched)
CBP = FindCBP(qcoeff,MODE_INTER,64);
if (DQUANT) Mode=MODE_INTRA_Q;else Mode=MODE_INTRA;
QP+=DQUANT;
CBPY = CBP >> 2;
CBPY = CBPY & 15; /* last 4 bits */
CBPC = CBP & 3; /* last 2 bits */
Bits_CountMB_combined (DQUANT, Mode, COD, ACpred_flag, CBP,
vop_type,
bits, mottext_bitstream,/*MB_transp_pattern*/NULL);
/* 增加变量 intra_dcpred_diable */
MB_CodeCoeff(bits, qcoeff, Mode, CBP, 64,
GetVopIntraACDCPredDisable(curr),
NULL, mottext_bitstream,
/*MB_transp_pattern*/NULL, direction,
1 /*通过GetVopErrorResDisable(curr)得到*/,
0 /*通过GetVopReverseVlc(curr)得到*/,
switched,
0 /*表示当前视频对象平面交替扫描控制变量是0 curr->alternate_scan*/);
}
}
/* Free allocated memory for 3D matrix */
for (i = 0; i < MB_width*MB_height; i++)
{
for (j = 0; j < 6; j++)
free(DC_store[i][j]);
free(DC_store[i]);
}
free(DC_store);
free ((Char*)qcoeff);
}
主要调用了CodeMB对宏块进行编码
/***********************************************************CommentBegin******
*
* -- CodeMB -- Code, 用substraction和增加的操作来编码并重建宏块
*
* 输入参数 :
* Int x_pos 宏块的x位置
* Int y_pos 宏块的y位置
* UInt width Vop绑定的框的宽 (unpadded size)
* Int QP 量化器参数
* Int Mode 宏块编码的模式
*
* 输出参数 :
* Vop *curr 当前的Vop,未编码
* Vop *rec_curr 当前Vop,已编码和重建了
* Vop *comp 当前Vop,运动 compensated
* Int *qcoeff 系数块 (384 * sizeof(Int))
*
***********************************************************CommentEnd********/
Void CodeMB(Vop *curr, Vop *rec_curr, Vop *comp, Int x_pos, Int y_pos, UInt width,
Int QP, Int Mode, Int *qcoeff)
{
Int k;
Int fblock[6][8][8];
Int coeff[384];
Int *coeff_ind;
Int *qcoeff_ind;
Int* rcoeff_ind;
Int x, y;
SInt *current, *recon, *compensated = NULL;
UInt xwidth;
Int iblock[6][8][8];
Int rcoeff[6*64];
Int i, j;
Int type; /* luma = 1, chroma = 2 */
// Int *qmat;
SInt tmp[64];
Int s;
int operation = curr->prediction_type;
/* This variable is for combined operation.
If it is an I_VOP, then MB in curr is reconstruct into rec_curr,
and comp is not used at all (i.e., it can be set to NULL).
If it is a P_VOP, then MB in curr is reconstructed, and the result
added with MB_comp is written into rec_curr.
- adamli 11/19/2000 */
Int max = GetVopBrightWhite(curr);
/* This variable is the max value for the clipping of the reconstructed image. */
coeff_ind = coeff;
qcoeff_ind = qcoeff;
rcoeff_ind = rcoeff;
for (k = 0; k < 6; k++)
{
switch (k)
{
case 0:
x = x_pos;
y = y_pos;
xwidth = width;
current = (SInt *) GetImageData (GetVopY (curr));
break;
case 1:
x = x_pos + 8;
y = y_pos;
xwidth = width;
current = (SInt *) GetImageData (GetVopY (curr));
break;
case 2:
x = x_pos;
y = y_pos + 8;
xwidth = width;
current = (SInt *) GetImageData (GetVopY (curr));
break;
case 3:
x = x_pos + 8;
y = y_pos + 8;
xwidth = width;
current = (SInt *) GetImageData (GetVopY (curr));
break;
case 4:
x = x_pos / 2;
y = y_pos / 2;
xwidth = width / 2;
current = (SInt *) GetImageData (GetVopU (curr));
break;
case 5:
x = x_pos / 2;
y = y_pos / 2;
xwidth = width / 2;
current = (SInt *) GetImageData (GetVopV (curr));
break;
default:
break;
}
// 块预测
BlockPredict (current, x, y, xwidth, fblock[k]);
}
for (k = 0; k < 6; k++)
{
s = 0;
for (i = 0; i < 8; i++)
for (j = 0; j < 8; j++)
tmp[s++] = (SInt) fblock[k][i][j];
#ifndef _MMX_
fdct_enc(tmp);
#else
fdct_mm32(tmp);
#endif
for (s = 0; s < 64; s++)
coeff_ind[s] = (Int) tmp[s];
if (k < 4) type = 1;
else type = 2;
/* For this release, only H263 quantization is supported. - adamli */
BlockQuantH263(coeff_ind,QP,Mode,type,qcoeff_ind,
GetVopBrightWhite(curr),1);
BlockDequantH263(qcoeff_ind,QP,Mode,type,rcoeff_ind,1, 0, GetVopBitsPerPixel(curr));
for (s = 0; s < 64; s++)
tmp[s] = (SInt) rcoeff_ind[s];
#ifndef _MMX_
idct_enc(tmp);
#else
Fast_IDCT(tmp);
#endif
s = 0;
for (i = 0; i < 8; i++)
for (j = 0; j < 8; j++)
iblock[k][i][j] = (Int)tmp[s++];
coeff_ind += 64;
qcoeff_ind += 64;
rcoeff_ind += 64;
if (Mode == MODE_INTRA||Mode==MODE_INTRA_Q)
for (i = 0; i < 8; i++)
for (j = 0; j < 8; j ++)
iblock[k][i][j] = MIN (GetVopBrightWhite(curr), MAX (0, iblock[k][i][j]));
switch (k)
{
case 0:
case 1:
case 2:
continue;
case 3:
recon = (SInt *) GetImageData (GetVopY (rec_curr));
if (operation == P_VOP) compensated = (SInt *) GetImageData (GetVopY (comp));
BlockRebuild (recon, compensated, operation, max, x_pos, y_pos, width, 16, iblock[0]);
BlockRebuild (recon, compensated, operation, max, x_pos + 8, y_pos, width, 16, iblock[1]);
BlockRebuild (recon, compensated, operation, max, x_pos, y_pos + 8, width, 16, iblock[2]);
BlockRebuild (recon, compensated, operation, max, x_pos + 8, y_pos + 8, width, 16, iblock[3]);
continue;
case 4:
recon = (SInt *) GetImageData (GetVopU (rec_curr));
if (operation == P_VOP) compensated = (SInt *) GetImageData (GetVopU (comp));
BlockRebuild (recon, compensated, operation, max,
x_pos/2, y_pos/2, width/2, 8, iblock[4]);
continue;
case 5:
recon = (SInt *) GetImageData (GetVopV (rec_curr));
if (operation == P_VOP) compensated = (SInt *) GetImageData (GetVopV (comp));
BlockRebuild (recon, compensated, operation, max,
x_pos/2, y_pos/2, width/2, 8, iblock[5]);
continue;
}
}
return;
}
(4、2)形状纹理编码函数VopShapeMotText()。该函数的功能是进行运动形状编码。
它在Vop_code函数中按P帧形成编码时被调用的。
* 输入参数 :
* Vop *curr : 当前要进行编码的视频对象平面
* Vop *rec_prev: 先前已重建的视频对象平面
* Image *mot_x : 运动向量的x轴坐标值
* Image *mot_y : 运动向量的y轴坐标值
* Image *MB_decisions: 包含了每一个宏块的编码模式
* Int f_code_for: 运动向量的搜索范围 1/2 pel: 1=32,2=64,...,7=2048
* Image* AB_SizeConversionDecisions:视频对象平面转为视频宏块过程中的块大小
* Image* AB_first_MMR_values : 最大宏块阀值
* Int intra_dcpred_disable : 不允许帧内DC判断预测的控制变量
* VolConfig *vol_config : 设置信息
* Int rc_type : 数据传输率控制类型
*
* 输出参数 :
* Vop *rec_curr : 当前重建的视频对象平面
* Image *mottext_bitstream : 编码后的比特流
* Bits *bits : 数据统计信
Void VopShapeMotText (Vop *curr, Vop *comp,
Image *MB_decisions, Image *mot_x, Image *mot_y,
Int f_code_for,
Int intra_acdc_pred_disable,
Vop *rec_curr,
Image *mottext_bitstream
)
{
Int Mode=0;
Int QP = GetVopQuantizer(curr);
Int* qcoeff=NULL;
Int i, j;
Int CBP;
Int COD;
Int CBPY, CBPC;
Int MB_in_width, MB_in_height, B_in_width, mbnum, boff;
SInt p;
SInt *ptr=NULL;
Float *motx_ptr=NULL, *moty_ptr=NULL;
Int num_pixels;
Int num_lines;
Int vop_type=PCT_INTER;
Int ***DC_store=NULL;
Int m, n;
Int ACpred_flag=-1;
Int direction[6];
Int switched=0;
Int DQUANT=0;
Bits nbits, *bits;
bits = &nbits;
qcoeff = (Int *) malloc (sizeof (Int) * 384);
num_pixels = GetImageSizeX(GetVopY(curr));
num_lines = GetImageSizeY(GetVopY(curr));
MB_in_width = num_pixels / MB_SIZE;
MB_in_height = num_lines / MB_SIZE;
B_in_width = 2 * MB_in_width;
for (i = 0; i < 6; i++) direction[i] = 0;
#ifdef _RC_DEBUG_
printf("RC - VopShapeMotText(): ---> CODING WITH: %d \n",QP);
#endif
/* 给3D矩阵分配空间矩阵时用来跟踪为DC或者AC判断预测服务的预测值*/
DC_store = (Int ***)calloc(MB_in_width*MB_in_height,
sizeof(Int **));
for (i = 0; i < MB_in_width*MB_in_height; i++)
{
DC_store[i] = (Int **)calloc(6, sizeof(Int *));
for (j = 0; j < 6; j++)
DC_store[i][j] = (Int *)calloc(15, sizeof(Int));
}
Bits_Reset (bits);
vop_type = PCT_INTER;
ptr = (SInt *) GetImageData(MB_decisions);
motx_ptr = (Float *) GetImageData(mot_x);
moty_ptr = (Float *) GetImageData(mot_y);
for (j = 0; j < num_lines/MB_SIZE; j++)
{
for (i = 0; i < MB_in_width; i++)
{
switched=0;
p = *ptr; // 获得此宏块的编码模式
DQUANT = 0;
/* 用默认的预测值对DC判断进行填充 */
for (m = 0; m < 6; m++)
{
DC_store[j*MB_in_width+i][m][0] = GetVopMidGrey(curr)*8;
for (n = 1; n < 15; n++)
DC_store[j*MB_in_width+i][m][n] = 0;
}
switch (p)
{
case MBM_INTRA:
Mode = (DQUANT == 0) ? MODE_INTRA : MODE_INTRA_Q;
bits->no_intra++;
break;
case MBM_INTER16:
Mode = (DQUANT == 0) ? MODE_INTER : MODE_INTER_Q;
bits->no_inter++;
break;
case MBM_INTER8:
Mode = MODE_INTER4V;
bits->no_inter4v++;
DQUANT = 0; /* 这里不能为8X8模式改变QP值*/
break;
default:
printf("invalid MB_decision value :%d\n", p);
exit(0);
}
// 对宏块进行编码
CodeMB (curr, rec_curr, comp, i*MB_SIZE, j*MB_SIZE,
num_pixels, QP + DQUANT, Mode, qcoeff);
mbnum = j*MB_in_width + i;
boff = (2 * (mbnum / MB_in_width) * B_in_width
+ 2 * (mbnum % MB_in_width));
CBP = FindCBP(qcoeff,Mode,64);
if ((CBP == 0) && (p == 1) && (*(motx_ptr +boff) == 0.0)
&& (*(moty_ptr +boff) == 0.0))
{
COD = 1; /* 跳过的宏块 */
BitstreamPutBits(mottext_bitstream, (long) (COD), 1L);
bits->COD ++;
*ptr = SKIPP;
Mode = MODE_INTER;
}
else
{
COD = 0; /* 已编码的宏块 */
if ((Mode == MODE_INTRA) || (Mode == MODE_INTRA_Q))
{
/* 存储以后预测所需要的量化系数值 */
m =0;
DC_store[j*MB_in_width+i][0][m] = qcoeff[m]*cal_dc_scaler(QP+DQUANT,1);
DC_store[j*MB_in_width+i][1][m] = qcoeff[m+64]*cal_dc_scaler(QP+DQUANT,1);
DC_store[j*MB_in_width+i][2][m] = qcoeff[m+128]*cal_dc_scaler(QP+DQUANT,1);
DC_store[j*MB_in_width+i][3][m] = qcoeff[m+192]*cal_dc_scaler(QP+DQUANT,1);
DC_store[j*MB_in_width+i][4][m] = qcoeff[m+256]*cal_dc_scaler(QP+DQUANT,2);
DC_store[j*MB_in_width+i][5][m] = qcoeff[m+320]*cal_dc_scaler(QP+DQUANT,2);
for (m = 1; m < 8; m++)
{
DC_store[j*MB_in_width+i][0][m] = qcoeff[m];
DC_store[j*MB_in_width+i][1][m] = qcoeff[m+64];
DC_store[j*MB_in_width+i][2][m] = qcoeff[m+128];
DC_store[j*MB_in_width+i][3][m] = qcoeff[m+192];
DC_store[j*MB_in_width+i][4][m] = qcoeff[m+256];
DC_store[j*MB_in_width+i][5][m] = qcoeff[m+320];
}
for (m = 0; m < 7; m++)
{
DC_store[j*MB_in_width+i][0][m+8] = qcoeff[(m+1)*8];
DC_store[j*MB_in_width+i][1][m+8] = qcoeff[(m+1)*8+64];
DC_store[j*MB_in_width+i][2][m+8] = qcoeff[(m+1)*8+128];
DC_store[j*MB_in_width+i][3][m+8] = qcoeff[(m+1)*8+192];
DC_store[j*MB_in_width+i][4][m+8] = qcoeff[(m+1)*8+256];
DC_store[j*MB_in_width+i][5][m+8] = qcoeff[(m+1)*8+320];
}
if (intra_acdc_pred_disable == 0)
ACpred_flag = doDCACpred(qcoeff, &CBP, 64, i, j,
DC_store,
QP+DQUANT, MB_in_width,
direction,GetVopMidGrey(curr));
else
ACpred_flag = -1; /* Not to go into bitstream */
}
switched = IntraDCSwitch_Decision(Mode,
GetVopIntraDCVlcThr(curr),
QP);
if (switched)
CBP = FindCBP(qcoeff,MODE_INTER,64);
CBPY = CBP >> 2;
CBPY = CBPY & 15; /* 取最后 4 bits */
CBPC = CBP & 3; /* 取最后 2 bits */
Bits_CountMB_combined (DQUANT, Mode, COD, ACpred_flag, CBP,
vop_type, bits,
mottext_bitstream,/*MB_transp_pattern*/NULL);
Bits_CountMB_Motion( mot_x, mot_y, NULL,
MB_decisions, i, j, f_code_for, 0 /*quarter_pel*/,
mottext_bitstream,
1 /*GetVopErrorResDisable(curr)*/, 0,
(Int **)NULL, 0 /*GetVopShape(curr)*/);
MB_CodeCoeff(bits, qcoeff, Mode, CBP, 64,
intra_acdc_pred_disable,
NULL, mottext_bitstream,
/*MB_transp_pattern*/NULL, direction,
1/*GetVopErrorResDisable(curr)*/,
0/*GetVopReverseVlc(curr)*/,
switched,
0 /*curr->alternate_scan*/);
}
ptr++; // 取下一宏块的编码模式
} /* for i loop */
} /* for j loop */
/* 释放已分配的3D矩阵 */
for (i = 0; i < MB_in_width*MB_in_height; i++)
{
for (j = 0; j < 6; j++)
free(DC_store[i][j]);
free(DC_store[i]);
}
free(DC_store);
free ((Char*)qcoeff);
}
(4、3) DC/AC 系数预测函数MB_CodeCoeff()。 该函数的功能是进行DC/AC系数预测。