原文地址:
DirectShow技术是DirectX推出的建立在DirectDraw和DirectSound组件基础之上的多媒体应用程序开发工具包,它提供对多媒体数据流的高质量捕获和回放,代表着未来多媒体应用程序开发的方向。详细介绍了DirectShow的系统组成,并讨论了利用DirectShow在Visual C++ 6.0平台上开发简单媒体播放器的关键步骤。
DirectX简介
DirectX是Microsoft公司为游戏和其他高性能多媒体应用所提供的一套底层应用程序编程接口。这些接口包括对二维和三维图形,声效和音乐,输入设备以及多玩家网络游戏等的支持。目前DirectX的最高版本是DirectX 9.0。 1 DirectX的组成 DirectX 9.0由下列组件构成: (1)DirectX Graphics:该组件组合DirectX旧版本中的DirectDraw和Direct3D两个组件,使其成为一个适用于所有图形程序的单独的应用程序接口。其中的Direct3D扩展(D3DX)应用程序库简化了多数图形程序的工作。 (2)DirectInput:支持各种输入设备,完全支持力反馈技术。 (3)DirectPlay:支持多玩家网络游戏。 (4)DirectSound:支持用于播放和捕获音频波形的高性能音频应用软件的开发。 (5)DirectMusic:为音乐音轨以及基于波表、MIDI(Musical Instrument Devices Interface)或其他由DirectMusic Producer创作的非音乐音轨,提供了一套完整的解决方案。 (6)DirectShow:提供对多媒体数据流的高质量捕获和回放。 (7)DirectSetup:一个简单的应用程序接口,提供DirectX组件的自动安装。 (8)DirectX Media Objects:提供对数据流对象的读写支持,包括视频和音频的编解码器及其效果。 2 COM简介 DirectX的功能都是以COM组件的形式提供的。COM是组件对象模型(Component Object Model)的简写,它是一种协议,是对象连接和嵌入(Object Linking and Embedding)的基础。COM通常以动态链接库(DLL)的形式存在,它是建立在二进制规范上的对象。COM定义并实现了软部件(如应用程序、数据对象、控件及服务)机制,并把他们统称为“对象”。每个软部件对象由数据以及访问数据的函数组成,访问软部件对象数据的函数集合称为“接口”。在应用程序看来COM是一个黑箱,可调用COM提供的方法但不知道它的具体实现。在使用DirectShow编程时,用户创建的自定义组件必须以COM形式实现,所以必须知道如何实现COM,而一般的应用程序只需要了解COM的接口和用法就可以了。DirectShow的系统组成 DirectShow技术是建立在DirectDraw和DirectSound组件基础之上的,它通过DirectDraw对显卡进行控制以显示视频,通过DirectSound对声卡进行控制以播放声音。 DirectShow可提供高质量的多媒体流的捕获和回放功能;支持多种媒体格式,包括ASF(Advanced Systems Format),MPEG(Motion Picture Experts Group),AVI(Audio-Video Interleaved),MP3(MPEG Audio Layer-3)和WAV声音文件;可以从硬件上捕获媒体数据流;可以自动检测并使用视频和音频加速硬件。因此,DirectShow可以充分发挥媒体的性能,提高运行速度,可以简化媒体播放、媒体间的格式转换和媒体捕获等工作。同时,它还具有极大的可扩展性和灵活性,可以由用户自己创建组件,并将这个组件加入DirectShow结构中以支持新的格式或特殊的效果。应用程序与DirectShow组件以及DirectShow所支持的软硬件之间的关系如图1所示。 图1 DirectShow系统框图 |
1、过滤器(filter) 由图1可以看到,过滤器是DirectShow最基本的组成元件。过滤器是一个COM组件,是完成DirectShow处理过程的基本单元。DirectShow提供了一组标准的过滤器供应用程序使用,程序开发者也可以创建自定义的过滤器来扩充DirectShow的功能,但必须是以COM形式建立的。DirectX为用户提供了DirectShow基类库(DirectShow Base Class Library),用户自定义的过滤器都可以从基类库提供的基类和接口派生出来。过滤器主要分为以下几种类型: (1)源过滤器(source filter):源过滤器引入数据到过滤器图表中,数据来源可以是文件、网络、照相机等。不同的源过滤器处理不同类型的数据源。 (2)变换过滤器(transform filter):变换过滤器的工作是获取输入流,处理数据,并生成输出流。变换过滤器对数据的处理包括编解码、格式转换、压缩解压缩等。 (3)提交过滤器(renderer filter):提交过滤器在过滤器图表里处于最后一级,它们接收数据并把数据提交给外设。 (4)分割过滤器(splitter filter):分割过滤器把输入流分割成多个输出。例如,AVI分割过滤器把一个AVI格式的字节流分割成视频流和音频流。 (5)混合过滤器(mux filter):混合过滤器把多个输入组合成一个单独的数据流。例如,AVI混合过滤器把视频流和音频流合成一个AVI格式的字节流。 过滤器的这些分类并不是绝对的,例如一个ASF读过滤器(ASF Reader filter)既是一个源过滤器又是一个分割过滤器。 在DirectShow里,一组过滤器称为一个过滤器图表(filter graph)。过滤器图表用来连接过滤器以控制媒体流,它也可以将数据返回给应用程序,并搜索所支持的过滤器。过滤器有三种可能的状态:运行、停止和暂停。暂停是一种中间状态,停止状态到运行状态必定经过暂停状态。暂停可以理解为数据就绪状态,是为了快速切换到运行状态而设计的。在暂停状态下,数据线程是启动的,但被提交过滤器阻塞了。通常情况下,过滤器图表中所有过滤器的状态是一致的。 2、引脚(pin) 过滤器可以和一个或多个过滤器相连,连接的接口也是COM形式的,称为引脚。过滤器利用引脚在各个过滤器间传输数据。每个引脚都是从Ipin这个COM对象派生出来的。每个引脚都是过滤器的私有对象,过滤器可以动态的创建引脚,销毁引脚,自由控制引脚的生存时间。引脚可以分为输入引脚(Input pin)和输出引脚(Output pin)两种类型,两个相连的引脚必须是不同种类的,即输入引脚只能和输出引脚相连,且连接的方向总是从输出引脚指向输入引脚。 过滤器之间的连接(也就是引脚之间的连接),实际上是连接双方媒体类型(Media Type)协商的过程。连接的大致过程为:如果调用连接函数时已经指定了完整的媒体类型,则用这个媒体类型进行连接,成功与否都结束连接过程;如果没有指定或不完全指定了媒体类型,则进入下面的枚举过程——枚举欲连接的输入引脚上所有的媒体类型,逐一用这些媒体类型与输出引脚进行连接(如果连接函数提供了不完全媒体类型,则要先将每个枚举出来的媒体类型与它进行匹配检查),如果输出引脚也接受这种媒体类型,则引脚之间的连接宣告成功;如果所有输入引脚上枚举的媒体类型,输出引脚都不支持,则枚举输出引脚上的所有媒体类型,并逐一用这些媒体类型与输入引脚进行连接,如果输入引脚接受其中的一种媒体类型,则引脚之间的连接宣告成功;如果输出引脚上的所有媒体类型,输入引脚都不支持,则这两个引脚之间的连接过程宣告失败。过滤器与引脚连接如图2所示。
图2 过滤器和引脚连接示意图 |
本节介绍基于DirectShow开发简单媒体播放器的关键步骤。
1、初始化DirectShow 由于DirectShow的组件都是以COM形式存在的,因此首先要调用CoInitializeEx函数来初始化COM库,嵌入所有的动态链接库和资源。否则,所有对QueryInterface的调用都会失败。 2、创建过滤器图表管理器接口 首先申明并初始化所需的接口:// DirectShow interfacesIGraphBuilder *pGB = NULL;IMediaControl *pMC = NULL;IMediaEventEx *pME = NULL;IVideoWindow *pVW = NULL;IBasicAudio *pBA = NULL;IBasicVideo *pBV = NULL;IMediaSeeking *pMS = NULL;IMediaPosition *pMP = NULL;IVideoFrameStep *pFS = NULL;然后实例化一个过滤器图表管理器,并查询各接口:// Get the interface for DirectShow's GraphBuilderCoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, IID_IGraphBuilder, (void **)&pGB);// QueryInterface for DirectShow interfacespGB->QueryInterface(IID_IMediaControl, (void **)&pMC);pGB->QueryInterface(IID_IMediaEventEx, (void **)&pME);pGB->QueryInterface(IID_IMediaSeeking, (void **)&pMS);pGB->QueryInterface(IID_IMediaPosition, (void **)&pMP);// Query for video interfaces, which may not be relevant for audio filespGB->QueryInterface(IID_IVideoWindow, (void **)&pVW);pGB->QueryInterface(IID_IBasicVideo, (void **)&pBV);// Query for audio interfaces, which may not be relevant for video-only filespGB->QueryInterface(IID_IBasicAudio, (void **)&pBA); |
3、创建过滤器图表 应用DirectShow创建过滤器图表时,用户完全不需要操心系统使用了哪一类过滤器以及过滤器是怎样连接的。只要调用IGraphBuilder::RenderFile函数,就可以建成一个完整的过滤器图表。
// Have the graph builder construct its the appropriate graph automaticallypGB->RenderFile(wFile, NULL); |
创建成功后,过滤器图表就可以用来播放多媒体文件了。DirectShow调用IMediaControl::Run函数来播放媒体文件。
// Run the graph to play the media filepMC->Run(); |
4、使用DirectShow的事件响应机制 DirectShow的事件响应机制是过滤器图表管理器与用户进行交互的接口,DirectShow处理的可以是一些事先可以预期的事件,比如数据流的结束;也可以是一些无法预期的错误。有的事件可以由过滤器图表管理器自己处理,但如果过滤器图表管理器自己无法处理这些事件,它就把事件的通知放在事件队列里。用户程序就可以通过IMediaEventEx接口得到事件,并对它做出相应的处理。 5、清除DirectShow 在程序结束时必须调用Release函数释放DirectShow的接口指针,并调用CoUninitialize函数来卸载COM库,释放所有的动态链接库和资源。 结束语 应用DirectX的组件DirectShow进行多媒体应用程序的开发需了解多方面的知识,但在很多应用中利用DirectShow的特性可以减少工作量并能获得非常高的运行效率。在Visual C++ 6.0的开发环境中利用DirectShow开发的简单媒体播放器,具有随机播放、暂停和调整播放速率等功能,且可以播放多种媒体文件,播放效果非常流畅。因此,基于DirectShow开发多媒体应用程序的方法简单高效,是一种值得推荐的方法。