MPEG-4编/解码设计与剖析(6)

来源:互联网 发布:黑客牛还是程序员厉害 编辑:程序博客网 时间:2024/05/01 06:43

2.MPEG-4视频解码

视频解码工作相对于视频编码,运算量大大降低,在一般情况下,后者是前者的1/3。大家都知道,MPEG-4视频算法是可兼容传统视频帧的面向对象编码的。但是目前视频对象分割是难点,因此几乎所有的MPEG-4视频应用都是基于传统的矩形视频帧来处理的。只不过MPEG-4把整个图像画面看做一个对象,即视频对象平面VOP(Video Object Plane)。

如图14-6所示是VOP解码功能框图,频码流包含I-VOP、B-VOP、P-VOP纹理信息和运动信息。帧间(Inter)和帧内(Intra)解码处理单位仍然是16 16的宏块。其中Intra宏块是图像数据经过IDCT和AC/DC预测得到的。Inter块是块数据间的残差作IDCT,然后加回到比较块,比较块的位置是根根码流中的运动向量MV(1个或4个)的值来确定的。运动向量的精度有1/2像素、1/4像素,这根据码流中的信息来决定,当然有可能指向参考帧的边界外。

XviD CODEC解码算法也提供了对CPU平台的汇编优化支持。检测CPU、初始化核心模块函数指针的代码完全编码工作与相同,请参考前面的小节。

 (点击查看大图)图14-6  VOP解码功能框图XviD的MPEG-4解码算法实现以一个函数及其3个不同的参数传递来完成。
/*-------------------------解码器操作-----------------------------------*/
#define XVID_DEC_CREATE  0 /* 创建解码器实例;0代表成功*/
#define XVID_DEC_DESTROY 1 /* 销毁解码器实例;0代表成功*/
#define XVID_DEC_ENCODE  2 /* 解码一帧图像:返回已经解码的字节数(>=0)*/
/*---------------------------编码器入口函数---------------------------------*/
extern int xvid_decore(void *handle, int opt, void *param1, void *param2);
解码器的入口函数xvid_decore的实现如下:
int xvid_decore(void *handle, int opt, void *param1, void *param2)
{
switch (opt) {
case XVID_DEC_CREATE:           /*创建解码器实例*/
return decoder_create((xvid_dec_create_t *) param1);
case XVID_DEC_DESTROY:          /*销毁解码器实例*/
return decoder_destroy((DECODER *) handle);
case XVID_DEC_DECODE:           /*解码一帧图像*/
return decoder_decode((DECODER *) handle,      /*解码器句柄*/
(xvid_dec_frame_t *) param1, /*解码帧结构体*/
(xvid_dec_stats_t*) param2); /*解码状态结构体*/
default:
return XVID_ERR_FAIL;
}
}

上述代码是解码器的所有功能函数,创建解码器实例、使用解码器解码图像、销毁解码器。在应用层,解码器可以有多个,这就通过解码句柄handle来控制不同的解码器。创建解码器decoder_create、销毁解码器decoder_destroy均只调用一次。循环调用decoder_decode解码图像帧。

1)创建解码器实例

创建解码器实例,即解码器句柄。解码器的所有动作和使用配置都是通过该句柄来控制完成的,XviD CODEC视频解码器可以同时解码多路视频。

/*图像分辨率大小发生变化,释放已申请的内存,并重新申请*/
static int decoder_resize(DECODER * dec)
{
/*释放存在的图像空间*/
image_destroy(&dec->cur, dec->edged_width, dec->edged_height);
image_destroy(&dec->refn[0], dec->edged_width, dec->edged_height);
image_destroy(&dec->refn[1], dec->edged_width, dec->edged_height);
image_destroy(&dec->tmp, dec->edged_width, dec->edged_height);
/*所有图像指针清零*/
image_null(&dec->cur);      //当前解码帧的空间y/u/v指针清空
image_null(&dec->refn[0]); //参考帧0的空间y/u/v指针清空
image_null(&dec->refn[1]); //参考帧1的空间y/u/v指针清空
image_null(&dec->tmp);  //tmp临时空间的y/u/v指针清空
xvid_free(dec->last_mbs); //释放宏块结构空间
xvid_free(dec->mbs);      //释放宏块结构空间
dec->last_mbs = NULL;    //指针清空
dec->mbs = NULL;           //指针清空
/*重新申请内存*/
dec->mb_width = (dec->width + 15) / 16;      
 /*图像帧宏块宽度,16的倍数*/
dec->mb_height = (dec->height + 15) / 16;     
 /*图像帧宏块高度,16的倍数*/
dec->edged_width = 16 * dec->mb_width + 2 *
EDGE_SIZE;/*图像边扩展了的图像宽度*/
dec->edged_height = 16 * dec->mb_height + 2
* EDGE_SIZE;/*图像边扩展了的图像高度*/
/*申请图像空间*/
if (   image_create(&dec->cur, dec->edged_width, dec->edged_height)
|| image_create(&dec->refn[0], dec->edged_width, dec->edged_height)
|| image_create(&dec->refn[1], dec->edged_width, dec->edged_height)
|| image_create(&dec->tmp, dec->edged_width, dec->edged_height))
goto memory_error;     //申请失败
/*申请所有宏块的解码信息*/
dec->mbs = xvid_malloc(sizeof(MACROBLOCK) * dec->
mb_width * dec->mb_height, CACHE_LINE);
if (dec->mbs == NULL)  goto memory_error;   //申请失败
memset(dec->mbs, 0, sizeof(MACROBLOCK) * dec->
mb_width * dec->mb_height); /*清空*/
/* 解码B帧时,用到的宏块解码信息 */
dec->last_mbs=xvid_malloc(sizeof(MACROBLOCK)*dec->
mb_width * dec->mb_height, CACHE_LINE);
if (dec->last_mbs == NULL)   goto memory_error; //申请失败
memset(dec->last_mbs, 0, sizeof(MACROBLOCK) *
dec->mb_width * dec->mb_height);
return 0;
memory_error:
/*释放图像空间、结构体,指针置空 */
xvid_free(dec->mbs);
image_destroy(&dec->cur, dec->edged_width, dec->edged_height);
//释放当前解码帧空间
image_destroy(&dec->refn[0], dec->edged_width, dec->edged_height);
//释放参考帧0空间
image_destroy(&dec->refn[1], dec->edged_width, dec->edged_height);
//释放参考帧1空间
image_destroy(&dec->tmp, dec->edged_width, dec->edged_height);
//释放临时帧空间
/*释放解码器句柄*/
xvid_free(dec);
return XVID_ERR_MEMORY;
}
/*创建编码器实例*/
int decoder_create(xvid_dec_create_t * create)
{
DECODER *dec;
dec = xvid_malloc(sizeof(DECODER), CACHE_LINE);
/*创建并初始化解码器句柄,使句柄完
全可控*/
if (dec == NULL)   return XVID_ERR_MEMORY;
memset(dec, 0, sizeof(DECODER));
create->handle = dec;      /*获取解码输入的参数*/
dec->width = create->width;
dec->height = create->height;
image_null(&dec->cur);    /*图像空间指针清零*/
image_null(&dec->refn[0]);
image_null(&dec->refn[1]);
image_null(&dec->tmp);
dec->mbs = NULL;
dec->last_mbs = NULL;
init_postproc(&dec->postproc);  
/*支持图像后处理,如滤波、去块效应*/
dec->frames = 0;                   
/*支持B帧解码,用来保存参考帧的时间*/
dec->time = dec->time_base = dec->last_time_base = 0;
dec->low_delay = 0;
dec->packed_mode = 0;
dec->time_inc_resolution = 1;  /* 直到VOL头更改为其他数据 */
/*创建解码器时,已知图像宽度和高度*/
dec->fixed_dimensions = (dec->width > 0 && dec->height > 0);
if (dec->fixed_dimensions)    return decoder_resize(dec);
else  return 0;
}

上述代码实现创建解码器,把图像空间的指针清空,如果已知图像的分辨率,则申请各种空间。如果不知道分辨率或分辨率设置不正确,则解码器在分析码流的头结构后,获取分辨率再重新申请空间。

在创建解码器时,也可以不输入图像分辨率,因为码流中本身有分辨率width、height信息,能够从码流中解析到,然后再重新申请图像空间。

原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 合金饰品变黑了怎么办 麻醉机fico2升高怎么办 快递被恶意投诉怎么办 顺风快递收件人拒收怎么办 手机联系人没了怎么办 收件人号码错了怎么办 收件人写错了怎么办 快递联系不到收件人怎么办 小孩烫伤有水泡怎么办 被烫伤的疤痕怎么办 微店别人下单后怎么办 月经超了七天怎么办 收件人不在指定地址怎么办 2017款宝来熄火后异响怎么办 我身高一八米怎么办呀 规格型号错了发票怎么办 合同签错了怎么办 柜子背板起泡怎么办啊 显卡红灯不亮怎么办 小米设置全英文怎么办 小米手机变英文怎么办 阿迪贝壳头变黄怎么办 网友问我名字怎么办 护照姓名拼写错误怎么办 cfa报名时填错名字怎么办 机票拼音错了怎么办 evus忘填了怎么办 嘴唇有点歪了怎么办 淡奶油打发出水怎么办 怀孕第二次见红怎么办 孕妇第二次见红怎么办 微信不显示步数怎么办 小说父亲的名字怎么办 苹果手机芯片坏了怎么办 外地手机卡丢了怎么办 名字中点那个点怎么办 信用卡批不下来怎么办 菲律宾旅游签证过期怎么办 百香果施肥多了怎么办 激素脸毛孔变大怎么办 怀孕了下面痒怎么办