Chinaunix首页 | 论坛 | 博客
  • 博客访问: 2278690
  • 博文数量: 668
  • 博客积分: 10016
  • 博客等级: 上将
  • 技术积分: 8588
  • 用 户 组: 普通用户
  • 注册时间: 2008-05-29 19:22
文章分类

全部博文(668)

文章存档

2011年(1)

2010年(2)

2009年(273)

2008年(392)

分类:

2009-08-14 10:53:31

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信息,能够从码流中解析到,然后再重新申请图像空间。

阅读(877) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~