基于DirectShow的多媒体文件音视频的重新压缩
基于DirectShow的多媒体文件音视频的重
新压缩 摘要—————————————一
基f徽软Dil"tSho~'开发甲}}jVisualC4-4-//:发
翻:境,给出f啼弼多俸顿砚顿数据以靳昀编码
进行蕾锈睚缩.并行能为A?文件硌式的C++编变醒 关键词————————————一
过滤器过滤器
表
关于同志近三年现实表现材料材料类招标技术评分表图表与交易pdf视力表打印pdf用图表说话 pdf
过滤器表管理器医缩A?文件
宽带互联网的日益普及和多媒体技术的深入发展为高 清晰,高质量的数字多媒体的应用开拓了广阔的前景,这种 应用的一个典型技术就是高清晰度电视系统.高清晰度电视 可以获得DVD质量的视频和音频效果,带给人超清晰的视觉 和听觉享受.然而,对于节目的提供方而言,由于技术或者 商业的需求.有时必须把一些多媒体文件.如通过数字卫星 系统录制的MPEG-2文件.进行重新的压缩编码或者文件格 式的转换.以便于后续的编辑,组播,传输等处理. 本文介绍了一种基于微软DirectShow技术的.对多媒体 文件数据压缩编码
格式
pdf格式笔记格式下载页码格式下载公文格式下载简报格式下载
进行转换.并将该多媒体文件转化为 具有开放文件格式的结构灵活框架清晰的AVI{AudioVideo
Interleaved)文件格式的开发过程.讨论的重点是格式转换的 编码实现.
一
DirectShow技术
DirectShow是微软多媒体开发平台DirectX家族中的一 口西安交通大学张勇罗静
员,是为了播放各种类型的视频数据,音频数据或其他多媒 体数据而设计的一套编程接口.DirectShow支持包括MPEG,
AVI,MOV,WAV,VOB等格式的多媒体影音数据的播放. 1.DirectShow的技术结构
DirectShow的核心是被称作过滤器{Filter)的插件式
模块系统.它使用一种叫过滤器图表{FilterGraph)的模 型来管理整个数据流的处理过程;参与数据处理的各个功 能模块叫Filter各个Filter在FilterGraph中按一定的顺 序连接成一条"流水线"协同工作.按照功能来分,Filter大 致分为三类:源过滤器{SourceFilters),转换过滤器
{TransformFilters)和表现过滤器{RenderingFilters). SourceFilters主要负责取得数据,数据源可以是文件.因 特网,或者计算机里的采集卡,数字摄像机等.然后将数据 往下传输;TransformFilters主要负责数据的格式转换,传 输;RenderingFilters主要负责数据的最终去向,我们可以 将数据送给声卡,显卡进行多媒体的演示,也可以输出到 文件进行存储.
在DireCtShOW系统之上.我们看到的是应用程序
{Application).应用程序要按照一定的意图建立起相应的Fi1- terGraph.然后通过过滤器图表管理器{FilterGraph Manager)来控制整个数据的处理过程.DirectShow能在Filter Graph运行的时候接收到各种事件.并通过消息的方式发送 到我们的应用程序.这样.就实现了应用程序与DirectShow 系统之间的交互.
87IAdva,nce,dTe.l200515
Selworskmit
2.Filter的连接
DirectShow是建立在COM技术基础上的.每个Filter本 身就是一个COM组件,过滤器带有称为针(Pin)的COM对 象,分为输入针和输出针.输出的Pin把Filter处理后的数据
传送到Filter的外部,而输入Pin则是把Filter外部的数据接 收到Filter中,以便Filter对这些数据进行处理.相邻两个Filter 的输入,输出Pin在验证媒体类型等一系列操作之后完成连 接,建立FilterGraph.FilterGraph通过获取流媒体信息数据, 转换数据和显示数据到输出设备上等步骤来操作流媒体. FilterGraph如图2所示.
3.智能连接技术
DirectShow的FilterGraphManager提供了在创建Filter Graph时的一个有效工具——智能连接技术.该机制能够通 过匹配Filter中的Pin的媒体类型自动构建FilterGraph. 智能连接的基本方法为试连接,它首先用内存中的Fil. ter进行试连接,若不成功,则对当前FilterGraph中的没 有完全连接的Filter试连接,如果仍然不成功.它将使用 IFilterMapper2::EnumMatchingFilters搜索注册表,用 Merit值大于MERIT—DO—NOT—USE的所有Filter进行试 连接,直至连接成功或者失败.程序中可以使用
IGraphBuilder的render,renderFile以及Connect等方法实 现智能连接.
因此,在DirectShow开发时,使用者可以将特定的 Filter手工加入到FilterGraph中.然后应用智能连接构造 整个FilterGraph.智能连接机制很大程度上简化了多媒体 程序设计过程,也为使用第三方开发的Filter提供了很大的 便利.
三格式转换FilterGraph的搭建
为了阐述的方便,下面的开发过程将以把MPEG.2文 件(.MPG)的视频,音频分别重新压缩为DIVX和MP3格 式,并存储为AVI文件为例,介绍FilterGraph的搭建及 控制过程.需要指出的是,在DirectShow集成的一系列 Filter中,与MPEG?2相关的只有两个:MPEG.2Splitter
和MPEG-2Demulitplexer.这两个Filter都是用于对 MPEG?2流的音视频进行分离的Filter.DirectShow并没有 集成MPEG?2的音视频解码器Filter.显然,为了能够对 MPEG?2文件的音视频进行重新压缩,必须首先安装第三 方的MPEG?2的音视频解码器.确定MPEG.2文件在 DirectShOW框架下是能够播放的.这一步骤可以使用 DirectShow提供的可视化工具GraphEdit将一个MPEG.2 文件render并且播放,如果能够成功进行,就可以实现对 它的重新压缩了.
1.音视频编码器的枚举
为了能够使用希望的视频或者音频压缩格式对MPEG.2 文件进行重新压缩,必须能够对本地机器上可用的视频,音 频编码器Filter进行枚举,如果使用了GUI编程,则可以把 这些编码器的名称显示出来以供选择使用.
在对这些Filter进行枚举时,需要知道filter的类型目 录的CLSID.需要用到的音频,视频编码器Filter以及普通 的DirectShOWFilter的类型目录CLSID分别为 CLSID—
AudioCompressorCategory,CLSID—
Video?
CompressorCategory以及CLSID—LegacyAmFilter?
CategOrY.可以在注册表的HKEY—CLASSES _
ROOT~CLSID\CLSID—ActiveCategoriesklnstance下找到系 统中注册的所有的Filter类型目录.
系统枚举的一般过程有以下几个步骤:
(1)使用CoCreatelnstance函数创建系统枚举器组件对象 (CLSIDCLSID—SYstemDeviCe?Enum),并获得 ICreateDevEnum接口.
(2)使用接口方法ICreateDevEnum:
CreateClassEnumerator为指定的Filter注册类型创建一个枚举
器,并获得IEnumMoniker接口.
(3)使用IEnumMoniker接口方法枚举指定类型目录下所
有可用Filter标识.
现代电视技术I88
2005.5I
网络与传囊
(4)调用IMoniker::BindToStorage之后.可以访问设moniker一>BindToObject(0,0,liD—IBaseFilter,
备标识的属性集.得到DisplayName.FriendlyName等.(void")&pCompress);
枚举系统所有视频编码器的代码如下:)
ICreateDevEnumpSysDeVEnum:NULL; IEnumMonikerpEnum=NULL:
IMonikerpMoniker:NULL:
CoCreale}nslance(CLSlD—SyslemDeVIceEnum,NULL, CLSCTX——
INPROC——
SERVERIID——
ICreateDevEnum,
(void)&pSysDevEnum)
pSysDevEnum一>CreateClassEnumerator( CLSlD—VideoCompressorCategory,&pEnum,O):
charfriendlyName[256];
while(S—OK==pEnum一>Next(1,&pMoniker,NULL))
{
IPropertyBagpPropBag=NULL;
pMoniker一>BindToStorage(0,0,IID—IPropertyBag, (void")&pPropBag):
VARIANTvat;
Variantlnit(&var):
pPropBag一>Read(L"FriendlyName",(structtagVARIANT)
&var,0).
WideCharToMultiByte(CP—ACP,0,varbstrVal,一1,
friendlyName,256,NULL,NULL); //AddfiIternamestothecodecIIsl m—codecListAddSng(endIyName): VarianlCIear(&Var):
pPropBag一>Release();
pMoniker一>Release();
)
pSysDevEnum一>Release();
pEnum一>Release();
对于音频的编码器Filter.只需要把pSysDevEnum一> CreateClassEnumeratOr语句中的第一个参数改为
CLSID_AudioCompressorCategory即可.
事实上.为了在FilterGraph中使用某个编码器Filter.只
需要在上述代码的VariantClear(&var);语句的前面加入以下代 码(其中needFilterName为选择使用的编码器Filter的Friendly Name):
WideCharToMultiByte(CP—ACP,0,varbstrVal,一1,
friendlyName,256,NULL,NULL); if(strcmp(friendlyName,needFilterName)=0)
{
89
然后在程序的适当位置把该Filter加入到FilterGraph就
可以了.
2.视频编码器属性设置
当视频编码器Filter被加入到FilterGraph中,为了获得 压缩以后的质量.速率等方面的需求.需要对编码器的相关 参数进行设置.
视频压缩过滤器可以在它的输出引脚支持
IAMVideoCompression接口.使用这个接口可以设置压缩的 属性,比如帧率.压缩质量等待.
首先.调用IBaseFilter:'EnumPins方法找到过滤器的输 出引脚.然后为接口查询引脚.一些过滤器不是所有的接口 都支持,也有的不支持某个压缩属性.为了决定支持的属性 能力.我们调用IAMVideoCompression::Getlnfo来确定.这 个方法返回一些信息:
?一个设置性能的标识:
?一个描述字符串和版本字符串;
?默认的帧速率.质量等参数:
它按照下面的语法调用:
pCompress一>Getlnfo(pszVersion,&cbVersion.pszDesc,
&cbDesc,
&IKeyFrame,&lPFrame.&dblQuality,&lCap);
pszVersion和pszDesc参数是接收版本和描述字符串的宽 字符缓冲区.cbVersion和cbDesc参数接收被请求的缓冲区 大小.IkeyFrame.1PFrame和dblQuality参数获得默认的帧速 率.P帧率和质量.质量是用从0.0到1,0的浮点数来表示的. ICaP参数获得一个被或的能力标识,这个被定义为 CompressionCaps枚举类型.这些参数的任何一个都可以设置 为NULL.例如为了获得版本和描述字符串.第一次调用方 法的时候.第一个和第三个参数为NULL.使用返回值 cbVersion和cbDesc来分配缓冲区.然后再次调用该方法: intcbVersion,cbDesc;//Sizeinbytes,notcharacters!
hr=pCompress一>Getlnfo(0,&cbVersion0.&cbDesc,0,0,0,
O):
if(SuCCEEDED(hr))
(
WCHARpszVersion=newwCHAR[cbVersion/2]:
WCHARpszDesc=newwCHAR[cbDesc/2】:
hr=pCompress一>Getlnfo(pszVersion,0,pszDesc,0,0,0,
AdvancedTelevision
Engineering
2nD,
NeT
1
w
ra
o
n
r
s
k
mit
0,0):
)
iCap参数提供了对IAMVideoCompression方法的过滤器 的支持能力.例如.如果iCaP包含了
CompressionCaps—
CanKeyFrame标志.就可以调用
IAMVideoCompression::get_KeyFrameRate方法来得到关键帧 的速率.调用IAMVideoCompression::put_KeyFrameRate来设
置帧速率.如果iCap的值没有包含这些参数.那么就只能使
用默认的值了.
if(ICap&CompressionCaps_CanKeyFrame) {
hr=pCompress一>get_KeyFmmeRate(&lKeyFrame); if(FAILED(hr)llIKeyFrame<0)
{
IKeyFrame=IDefaultKeyFrame;//0$?'Getlnfo
)
)
可以使用枚举的方法从输出引脚寻找
IAMVideoCompression接口.而当使用IcaptureGraphBuilder2 接口来创建FilterGraph时.可以调用IcaptureGraphBuilder2:: Findlnterface方法来获得IAMVideoCompression接口.取得
了该接口.就可以使用上面的方法取得相关信息了.
如果视频编码Filter实现了IAMVfwCompressDialogs接
口.取得这个接口.就可以显示出该Filter的对话框.在程序
运行时实现视频压缩参数的动态设定.代码如下:
IAMVfwCompressDialogspAMVfwCompressDialogs=NULL;
pCompress一>Querylnterface(&pAMVfwCompressDialogs);
pAMVfwCOmPressDiaIogS一>ShOWDiaIOg(1,this一>
GetSafeHwnd0);
pAMVfwCompressDialogs~~>release0; 这个由编码F'dter提供方实现的对话框.往往能够以简洁的
界面实现方便的参数设定.比如Divx编码器的参数设置界面.
3.建立FilterGraph
一
般而言.我们建立FilterGraph都是创建一个实现了
IGraphBuilder接口的CLSID为CLSID_FilterGraph的组件来
实现的.然而.一个实现了IcaptureGraphBuilder2接口的 CLSID为CLSID—CaptureGraphBuilder2的组件实际上更适合 于重新对一个文件进行压缩.选用它创建FilterGraph将简化 编程过程.
(7)建Graph
首先创建COM组件CaptureGmphBuilder2的一个实例, 现代电视技)Ic
2OO5.5
这将同时自动创建一个FilterGraphManager.此时.这个
FilterGraph是空的.
ICaptureGraphBuilder2pBuild=NULL;CoCreatelnstance
(CLSID—CaptureGraphBuiIder2,NULL,CLSCTX—INPROC, llD_ICaptureGraphBuilder2,(void")&pBuild);
(2)建立渲染段
为了把目标文件写成一个AVI文件.必须在filterGraph 中加入AVIMUXfilter.以及用于写文件的filter.采用
ICaptureGraphBuilder2接口的SetOutputFileName方法.可以 很方便的加入这两个Filter.这个方法的调用参数中应当给出 输出文件的子类型(如AVI,ASF等).以及输出文件名:方 法调用将返回一个指向MUXfilter的指针和一个指向File Writer的指针(可选).其实现代码如下:
IBaseFilterpMux=NULL;
pBuild一>SetOutputFileName( &MEDIASUBTYPE_
Avi,//Filetype.
L"test.avi".//Filename.
&pMux,//Receivesapointertothemultiplexer.
NULL);//Receivesapointertothefilewriter.
这里没有设置filewriter的指针为NULL,是因为不需要使
用这个指针.为了对目标AVI文件的交错格式参数.遵循的
文件索引格式规范(avil.0或avi2.0)参数进行设置.需要取
得MUXfilter上实现的两个接口指针.这两个接口分是
IConfiglnterleaving和IConfigAviMux.取得接口指针以后.即
可进行相关设置.
(3)连接源Filter
为了连接源Filter,首先应当取得FilterGraph的
IGraphBuilder接口指针.通过这个接口去添加源Filter. IGraphBuilderpGraph=NULL;
IBaseFilter*pSrc=NULL;
pBuUd一>GetFiltergraph(&pGraph); pGraph一>AddSourceFilter(L"C:\\test.mpg".L"SourceFilter",
&pSrc);
接着.采用上文提到的音视频编码器的枚举方法获得需
要的编码器Filter~针.并使用IFilterGraph::AddFilter方法加
入FilterGraph.假定采用DIVX5.0.1视频编码器.MPEG
Layer3音频编码器.获得指针分别为pVComp和pAComp.以
下的代码将其加入filtergraph中:
pGraph一>AddFilter(pVComp,Lf|DIVX5.2.1||): pGraph一>AddFilter(pAComp,L|.MPEGLayer3"): 这样.已经加入了所有关键的Filter.filterGraph所需要
的其他Filter.可以使用职能连接技术取得并建立完整的filter f9o
网络与传辅
graph.首先将视频链路搭建完整:PEvent一>SetNotifyWindOW((OAHWND)g— hWnd,
pBuild一>RenderStream(WM—GRAPHNOTIFY,0):
NULL,//Outputpincategory为了响应消息,需要在WindowProc中添加这个消息的
NULL,//Mediatype
pSrc,//Sourcefilter
pVComp,//Compressorfilter pMux);//Sinkfilter(theAVIMux) RenderStream方法将根据需要,在SourceFilter和 CompressorFilter之间加入必要的filter(如解码filter)以 完成链路.该方法的前两个参数指定了用那个源过滤器的 引脚来连接,通过指明引脚的分类和媒体类型来实现.异 步文件源过滤器只有一个输出引脚,所以这些参数要设置 成NULL.后三个参数指定了源过滤器,压缩过滤器,和 Mux过滤器.
可以看出,智能连接自动加入了AVISPlitter和
wMVideo的解码器.此时,音频流还没有接如AVI MUXfilter.再次使用RenderStream方法对音频流进行 智能连接:
pBuild一>RenderStream(NULL,NULL,pSrc,pAComp,pMux);
成功返回以后,完整的FilterGraph已经建立起来, (4)_-q~-件
FilterGraph链路搭建完毕以后,取得Graph的
IMediaControl接口,通过改接口,使用Run()方法,可以 启动filtergraph进入运行状态,开始写文件.
为了获知写文件的进度情况,可以获得MUXfilter的 IMediaSeeking接口,其GetDuration方法和GetCurrentPosition
将分别获得总共的持续时间和当前时间信息,通过这些信息, 可以在GUI界面显示写文件的进度条.
4.事件处理
DirectShow应用程序运行过程中,过滤器状态是会变化 的.有些变化是我们所不关心的,而有些状态事件是我们需
要捕捉并处理的,比如EC—COMPLETE和EC—USERABORT 事件,它们分别代表FilterGraph运行结束和运行中用户中 止,因为在这些事件发生时,必须至少对一些COM组件进行 释放操作(负责将造成内存泄漏).为了实现事件的捕捉,需 要自定义一个消息:
#defineWM—GRAPHNOTIFYWM_APP+1//WM_APP 是一个用户消息的底线标志
取得FilterGraph的IMediaEventEx接口指针pEvent;通 过该接口指针设定FilterGraphManager来给应用程序的主窗 口提交这个消息,即:
处理:
caseWM——
GRAPHNOTIFY:
HandleEVent():
break;
在HandleEvent0处理函数中我们可以调用ImediaEvent: GetEvent方法来从循环中获得事件:
longevCode,param1,param2; HRESULThr;
if(pEvent==NULL)
retum:
while(hr=pEvent一>GetEVent(&eVCode,¶ml,¶m2.
0),SUCCEEDED(hr))
(
hr=pEvent一>FreeEventParams(evCode,paraml,param2);
if((EC—COMPLETE==evCode)II(EC—USERABORT== evCode))
(
CleanUp();
break;
)
)
在CleanUp()中可以调用Release()方法对各个COM 组件进行释放.比如pEVent一>Release()将释放pEvent的资源等. 四结语
本文基于DirectShow9.0开发平台,采用微软VisualC+ +6.0开发工具.给出了完成多媒体音视频以新的编码器重 新压缩并存储为AVI文件格式的编码实现.文中对 DirectShow应用程序开发的详细流程进行了探讨,对改工程 的进一步完善,可以满足多媒体数据格式及文件格式转换的 相关需求.l
I
91lAdvanced—Tse