打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
转载XviDCODEC实现MPEG

摘要:《VisualC++音频/视频处理技术及工程实践》第14章XviDCODEC实现MPEG-4编/解码,本章首先介绍MPEG-4视频编码算法的原理,对MPEG-4编码算法的工程实现XviDCODEC的工作过程做详细的介绍。本节为MPEG-4编/解码的概述。

第14章 XviD CODEC实现MPEG-4编/解码

视频编/解码是视频应用的核心技术,如可视电话、视频监控、DVD、视频点播(VOD)等。ISO制定的MPEG-1编/解码标准在VCD中应用,MPEG-2在DVD、数字电视等高质量图像中应用。为满足多媒体技术的发展及人们更多的技术需求,ISO于1998年制定了MPEG-4音/视频编/解码标准,其码流更低、档级更丰富,如在兼容传统视频编码的基础上增加了基于对象的编码,交互性更强。视频监控DVR应用中以MPEG-4标准为主流,这些应用中有专用芯片(ASIC)、DSP可编程芯片、软件编程CPU实现等技术方案。MPEG-4的工程实现从最早的微软,到后来的DivX、XviD。目前大家移植开发应用的都是XviD,它是公认的最好的开源MPEG-4算法工程。

本章重点

MPEG-4编/解码概述

XviD CODEC编/解码分析

运行XviD CODEC系统

系统效果展示

14.1  MPEG-4编/解码概述

视频编/解码已经是一个非常炽热的行业,有许多公司、企业在基于各种平台ASIC、DSP、FPGA、ARM或普通CPU,如Intel/AMD等,实现视频编/解码应用。视频监控、视频会议、网络视频、数字硬盘录像机、MP4等影音设备、影音文件都无一不使用视频编码和解码。视频编/解码是一个有广大应用前景的行业,未来的3G移动通信就可以实现彼此视频图像的收发。数字电视中的MPEG-2标准、主流多媒体压缩板卡、嵌入式视频编码卡、视频会议等采用了MPEG-4、H.263等,以及国产的AVS、国际的H.264/AVC、微软的WMV9等在视频应用中视频编/解码算法占据了核心地位。

XviD是开源的MPEG-4视频编/解码CODEC,标准C语言开发、部分核心函数采用了MMX/SSE/SSE2媒体汇编指令优化。XviD实现了MPEG-4标准中的ASP(AdvancedSimple Profile),编/解码效率能够在双核Intel CPU1.6GB、1GB内存配置的计算机上,实时运行4路D1的视频编码。下面就介绍XviD实现MPEG-4编码和解码算法的过程。

14.1.1  基于对象的MPEG-4视频编码

MPEG-4(ISO/IEC14496)算法是ISO的运动图像专家组MPEG(Moving Pictures ExpertGroup)于1998年发布的。它设计的初衷是第二代图像编码标准,即对象编码。不过,该算法并不包含对象的分割方法,只是提供了对分割对象后的编码方法。但是,对象分割目前还是一个难点,还没有较好的突破。所以,目前的MPEG-4视频算法应用基本都是基于像素的传统视频编码,即混合编码技术,编码对象不是目标对象而是图像宏块,但算法中仍然有视频编码对象的概念VOP,是把整帧图像作为一个对象进行编码的。

14.1.2  XviD格式文件播放

目前,网络上DivX和XviD格式的电影、影音文件并存。系统只安装DivX5解码器不能播放XviD格式的文件。而只安装XviD,则可以顺利播放DivX 5格式的文件。只是在播放DivX5文件的时候,速度不是很令人满意。XviD在播放DivX 5的文件还不是很完善。虽然XviD目前来讲与DivX5相比,优势不是太明显。但是大家很看好XviD,首先它的源代码绝对公开,这就使得有更多的人投入到XviD的研发之中。另外,由于完全重写DivX的源代码,新的XviD去除了DivX的Bug;目前XviD的开发人员有很多都是当初DivX的研发人员,对DivX的错误了解得非常清楚,重写之后XviD的优势可见一斑。另外,DivX4、DivX5虽然版本不断更新,但是功能并没有提高多少,优势不明显。流行的视频CODEC都支持XviD:Transcode、Mencoder、Mplayer等。

14.2  XviD CODEC编/解码分析

XviD Video CODEC实现的是MPEG-4的SP(Simple Profile)和ASP(AdvancedSimple Profile)标准,CODEC包括视频编码算法和视频解码算法。XviDCODEC于2001年创立,是MPEG-4视频编码算法的实现。早期的版本(0.9.x)实现了MPEG-4SP版本的编码和解码,XviD 1.0版本及其子版本实现了MPEG-4ASP视频压缩,包括所有高级编码工具,如码流控制、B帧编码、1/4像素运动补偿和全局运动补偿GMC等。即将开始研发的XviD2.0增加了对MPEG-4/AVC(Advanced VideoCoding)等更高档级的编/解码支持,编码压缩性能相比早期版本将大幅提升。

XviDCODEC编/解码算法的工作流程基本都包含3个过程:初始化CODEC、使用CODEC、销毁CODEC。初始化CODEC过程完成CODEC的创建句柄、分配必须的内存空间;循环使用CODEC完成视频图像的编码、MPEG-4码流的解码;销毁CODEC完成初始化的反工作。XviDCODEC使用标准C语言编程,同时CODEC的核心模块图像块类型转换复制、DCT变换、系数量化、SAD计算、运动补偿MC、CBP计算等都实现了多个CPU:X86的32/64位、ia64等的汇编优化。

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

XviDCODEC以动态库和静态库的形式供应用程序使用。这里以静态库为对象,展现视频编码和解码的设计过程。通过代码分析深入掌握视频编/解码的工作原理。XviD的MPEG-4算法的视频编码和解码在同一个工程中。视频编码使用一个函数,根据传入的参数决定初始化、编码和销毁编码器,视频解码函数调用与之类似。

1.MPEG-4视频编码

XviD的MPEG-4视频编码支持ASP@L5档级,并且绝大部分的视频应用不超过720像素576像素的分辨率,尽管XviD最高可支持4096像素 4096像素分辨率的视频编码。而实际上在视频监控应用中,CIF(352像素288像素)分辨率更常用。另外,尽管XviDCODEC支持高档应用:B帧编码、1/4精度像素和全局运动补偿GMC。但是在使用如DSP硬件编码时,这些功能是简化了的,即实现MPEG-4的SP档级。而在PC上开发XviDMPEG-4算法时,这些高档应用可以考虑打开,但是大量额外的计算量与获得的编码性能并不成比例。所以在后面的介绍中及在实际的应用中也确实如此,以MPEG-4的SP档级为主要介绍对象。

MPEG-4视频编码是典型的混合编码技术,即使用变换、量化和编码三步骤对图像数据或图像差值作处理。I帧(关键帧KeyFrame)是帧内编码,即对图像数据做编码,利用了图像的空间冗余;P帧编码主要是利用前向预测,对宏块差值编码,利用了图像的时间冗余。统计来看,P帧编码是主流,因为I帧有一定的间隔。P帧编码中的技术核心是搜索与当前宏块的最佳匹配块,即运动估计,搜索的窗口越大,宏块越匹配,但随之运算量也就越大。运动估计技术(MotionEstimation,ME)是任何视频编码算法的核心。这里以图像大小为352像素288像素、格式为I420、编码I、P帧为例介绍MPEG-4视频编码流程,如图14-1所示。

每个模块的功能简要介绍如下。

从文件中读取一帧图像,大小为352像素×288像素×3/2(包含了亮度和色度)。

以宏块为单位进行编码,一帧图像中所有宏块编码完成,也就完成了对当前帧的编码。

对当前宏块进行运动估计(如果是I帧,则不用),根据SAD值决定匹配块。

对8 8块进行编码,按照其在宏块中排列的顺序进行。首先将当前宏块同参考宏块作差值,然后再对差值进行编码(I帧不用)。

对当前编码块(或者经过运动补偿的差值)进行DCT变换。

对DCT变换后的系数进行量化。

反量化模块是量化模块的逆过程。

反DCT模块是DCT模块的逆过程。

AC/DC预测,即在编码I帧时,对当前宏块的第一行或者第一列系数同它周围的某一块作一个差值,进一步增加零系数,降低比特率。

反DCT后的宏块作运动补偿得到当前宏块的重建值,称为重建宏块。

码流合并模块,形成最后的视频编码码流。码流由码流头开始,然后是具体的帧。在编码之前,模块首先向输出流文件中写入码流头信息。然后写入第一帧的内容,每一帧同样由帧头信息开始,帧头信息同当前的编码内容是紧密相关的,后面的帧数据必须完全符合它规定的一些特性,譬如当前帧的量化值、运动补偿的搜索范围等。接下来是具体的帧数据,并且是按照宏块组织的。宏块内容包含当前宏块的编码信息,例如当前宏块是否编码、编码类型等,接着是运动矢量的数据,最后是具体的6个块的数据。

XviDCODEC为了检测运行平台的CPU,在编/解码器初始化前,给开发者提供了可以指定平台的接口,另外程序也能够自动检测CPU。然后针对不同的平台初始化核心模块的函数指针,编码和解码前都要做该项工作。

   //………………
xvid_gbl_init.cpu_flags = 0; //程序自动检测
xvid_global(NULL, XVID_GBL_INIT, &xvid_gbl_init,NULL);
//………………
int xvid_global(void *handle, int opt, void *param1, void*param2)
{
switch(opt) {
case XVID_GBL_INIT:   //初始化内部的函数指针、VLC码表和图像空间转换函数
return xvid_gbl_init((xvid_gbl_init_t*)param1);
case XVID_GBL_INFO:  //返回编码器的一些信息通知给主机
return xvid_gbl_info((xvid_gbl_info_t*)param1);
case XVID_GBL_CONVERT:  //颜色转换
return xvid_gbl_convert((xvid_gbl_convert_t*)param1);
default :
return XVID_ERR_FAIL;
}
}

上述代码在调用初始化编码前调用一次,主要是初始化内部的函数指针,如通过检测CPU平台,使用MMX或SSE指令优化的函数初始化函数指针,创建VLC熵编码的码表等。主要函数为xvid_gbl_init。

XviD的MPEG-4编码算法实现以一个函数及其3个不同的参数传递来完成。


#define XVID_ENC_CREATE 
#define XVID_ENC_DESTROY 1 
#define XVID_ENC_ENCODE 

extern int xvid_encore(void *handle, int opt, void *param1, void *param2);

编码器的入口函数xvid_encore的实现如下:
int xvid_encore(void *handle, int opt, void *param1, void*param2)
{
switch (opt) {
caseXVID_ENC_ENCODE:   //XviD编码一帧图像
return enc_encode((Encoder *) handle,(xvid_enc_frame_t *)
param1,(xvid_enc_ stats_t *) param2);
caseXVID_ENC_CREATE:   //XviD创建编码器
return enc_create((xvid_enc_create_t *) param1);
case XVID_ENC_DESTROY:  //XviD销毁编码器
return enc_destroy((Encoder *) handle);
default:
return XVID_ERR_FAIL;
}
}

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

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

下面就分别详细介绍这3个函数的功能和实现过程。

1)XviD创建编码器

创建MPEG-4编码器,首先创建编码器实例句柄,然后在该句柄下执行对编码器的参数配置、图像参数获取和空间申请等工作,这样在多路编码时,可以通过句柄来控制每一路的编码。

函数enc_create()流程如图14-2所示。

 
图14-2 enc_create()流程图
根据该流程,具体代码如下。
int  enc_create(xvid_enc_create_t * create)
{
Encoder *pEnc;

pEnc = (Encoder *) xvid_malloc(sizeof(Encoder), CACHE_LINE);
if (pEnc == NULL)  return XVID_ERR_MEMORY;
memset(pEnc, 0, sizeof(Encoder));            

pEnc->mbParam.profile = create->profile;
pEnc->mbParam.global_flags = create->global; 

pEnc->mbParam.width = create->width;      
pEnc->mbParam.height = create->height;
pEnc->mbParam.mb_width = (pEnc->mbParam.
width + 15) / 16;   
pEnc->mbParam.mb_height = (pEnc->mbParam.
height + 15) / 16;

pEnc->mbParam.edged_width = 16 * pEnc->
mbParam.mb_width + 2 * EDGE_SIZE;
pEnc->mbParam.edged_height = 16 * pEnc->
mbParam.mb_height + 2 * EDGE_SIZE;
pEnc->mbParam.fincr = MAX(create->fincr,
0);               
pEnc->mbParam.fbase = create->fincr
<= 0 ? 25 : create->fbase; 
pEnc->mbParam.frame_drop_ratio = MAX
(create->frame_drop_ratio, 0);

pEnc->mbParam.iMaxKeyInterval = create->max_
key_interval <= 0 ? (10 * (int)pEnc->mbParam.fbase) /
(int)pEnc->mbParam.fincr : create->max_key_interval;

pEnc->current = xvid_malloc(sizeof(FRAMEINFO), CACHE_LINE);
pEnc->reference = xvid_malloc(sizeof(FRAMEINFO), CACHE_LINE);
if (pEnc->current == NULL || pEnc->reference == NULL)
goto xvid_err_memory1;

pEnc->current->mbs =
xvid_malloc(sizeof(MACROBLOCK) * pEnc->mbParam.mb_width *
pEnc->mbParam.mb_height, CACHE_LINE);
pEnc->reference->mbs =
xvid_malloc(sizeof(MACROBLOCK) * pEnc->mbParam.mb_width *
pEnc->mbParam.mb_height, CACHE_LINE);
if (pEnc->current->mbs == NULL || pEnc->reference->mbs == NULL)
goto xvid_err_memory2;

image_null(&pEnc->current->image);
image_null(&pEnc->reference->image);
image_null(&pEnc->vInterH);
image_null(&pEnc->vInterV);
image_null(&pEnc->vInterHV);

if (image_create(&pEnc->current->image, pEnc->mbParam.
edged_width,pEnc->mbParam. edged_height) < 0)
goto xvid_err_memory3;

if(image_create(&pEnc->reference->image,pEnc->mbParam.
edged_width,pEnc-> mbParam.edged_height)< 0)
goto xvid_err_memory3;

if (image_create(&pEnc->vInterH, pEnc->mbParam.
edged_width, pEnc->mbParam. edged_height) < 0)
goto xvid_err_memory3;

if (image_create(&pEnc->vInterV, pEnc->mbParam.
edged_width, pEnc->mbParam. edged_height) < 0)
goto xvid_err_memory3;

if (image_create(&pEnc->vInterHV, pEnc->mbParam.
edged_width, pEnc->mbParam. edged_height) < 0)
goto xvid_err_memory3;

pEnc->queue_head = 0;
pEnc->queue_tail = 0;
pEnc->queue_size = 0;
pEnc->queue = xvid_malloc((1) * sizeof(QUEUEINFO),CACHE_LINE);

if(image_create(&pEnc->queue[0].image,pEnc->mbParam.
edged_width,pEnc-> mbParam.edged_height) < 0)
goto xvid_err_memory5;

pEnc->mbParam.m_stamp = 0;
pEnc->current->stamp = 0;
pEnc->reference->stamp = 0;
pEnc->iFrameNum = 0;            
create->handle = (void *) pEnc; 
return 0;                          

xvid_err_memory5:
image_destroy(&pEnc->queue[0].image,pEnc->mbParam.
edged_width,pEnc->mbParam.edged_height);
xvid_free(pEnc->queue);

image_destroy(&pEnc->current->image, pEnc->mbParam.
edged_width,pEnc->mbParam. edged_height);
image_destroy(&pEnc->reference->image, pEnc->mbParam.
edged_width,pEnc->mbParam. edged_height);
image_destroy(&pEnc->vInterH, pEnc->mbParam.
edged_width,pEnc->mbParam.edged_ height);
image_destroy(&pEnc->vInterV, pEnc->mbParam.
edged_width,pEnc->mbParam.edged_ height);
image_destroy(&pEnc->vInterHV, pEnc->mbParam.
edged_width,pEnc->mbParam.edged_ height);
xvid_err_memory2:

xvid_free(pEnc->current->mbs);
xvid_free(pEnc->reference->mbs);
xvid_err_memory1:

xvid_free(pEnc->current);
xvid_free(pEnc->reference);

xvid_free(pEnc);
xvid_err_memory3:
create->handle = NULL;
return XVID_ERR_MEMORY;
}

上述代码完成编码器的创建、编码参数的初始化、工作空间的申请等。编码器的初始化函数可以不用过多地考虑优化,因为这个函数只调用一次,不像编码函数多次循环地调用。

本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
视频会议十大开源项目排行
【新提醒】有关BluffTitler 软件导入视频问题 (教程)
常用解码器大全 - 后期合成 - vfxart-中国特效联盟|打造国内专业技术交流平台 -...
视频编码与封装方式详解
常见视频播放问题解决方案集锦
视频剪辑概念_toutong360的个人空间_闪吧新社区
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服