用DirectShow设计媒体播放器

摘要: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和Direct 3D两个组件,使其成为一个适用于所有图形 程序的单独的应用 程序接口。其中的Direct 3D扩展(D 3DX)应用 程序库简化了多数图形 程序的工作。

  (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 过滤器和引脚连接示意图

  3、媒体类型(Media Type)

  媒体类型是描述数字媒体格式的一种通用的可扩展方式。两个过滤器相连时,必须使用一致的媒体类型,否则这两个过滤器就不能相连。媒体类型能识别上一级过滤器传送给下一级过滤器的数据类型,并对数据进行分类。

  实际在很多应用 程序中,用户根本不需要担心媒体类型的问题,DirectShow会处理好所有的细节。但有些应用 程序需要对媒体类型进行操作。媒体类型一般可以有两种表示:AM_MEDIA_TYPE和CMediaType。前者是一个结构,后者是从这个结构继承过来的类。

  每个AM_MEDIA_TYPE由三部分组成:Major type、Subtype和Format type。这三个部分都使用GUID(全局唯一标识符)来唯一标示。Major type主要定性描述一种媒体类型,这种媒体类型可以是视频、音频、比特数据流或MIDI数据等等;Subtype进一步细化媒体类型,如果是视频的话可以进一步指定是RGB-24,还是RGB-32,或是UYVY等等;Format type则用一个结构更进一步细化媒体类型。

  如果媒体类型的三个部分都指定了某个具体的GUID值,则称这个媒体类型是完全指定的;如果媒体类型的三个部分中有任何一个值是GUID_NULL,则称这个媒体类型是不完全指定的。GUID_NULL具有通配符的作用。

  4、过滤器图表管理器(Filter Graph Manager)

  DirectShow通过过滤器图表管理器来控制过滤器图表中的过滤器。过滤器图表管理器是COM 形式的,它的功能有:协调过滤器间的状态转变;建立参考时钟;把事件(event)传送给应用 程序;为应用 程序提供建立过滤器图表的方法。

  一些常用的过滤器图表管理器接口如下:

  IGraphBuilder:为应用 程序提供创建过滤器图表的方法。

  IMediaControl:提供控制过滤器图表中多媒体数据流的方法,包括运行、暂停和停止。

  IMediaEventEx:继承自IMediaEvent接口,处理过滤器图表的事件。

  IVideoWindow:用于设置多媒体播放器窗口的属性,应用 程序可以用它来设置窗口的所有者、位置和尺寸等属性。

  IBasicAudio:用于控制音频流的音量和平衡。

  IBasicVideo:用于设置视频特性,如视频显示的目的区域和源区域。

  IMediaSeeking:提供搜索数据流位置和设置播放速率的方法。

  IMediaPosition:用于寻找数据流的位置。

  IVideoFrameStep:用于步进播放视频流,可使DirectShow应用 程序,包括DVD播放器一次只播放一帧视频。

  5、过滤器图表中的数据流动

  当用户要创建自定义的过滤器时,就需要了解媒体数据是如何在过滤器图表中传输的。为了在过滤器图表中传送媒体数据,DirectShow过滤器需要支持一些协议,称之为传输协议(transport)。相连的过滤器必须支持同样的传输协议,否则不能交换媒体数据。

  大多数的DirectShow过滤器把媒体数据保存在主存储器中,并通过引脚把数据提交给其它的过滤器,这种传输称为局部存储器传输(local memory transport)。虽然局部存储器传输在DirectShow中最常用,但并不是所有的过滤器都使用它。例如,有些过滤器通过硬件传送媒体数据,引脚只是用来提交控制信息,如IOverlay接口。

  DirectShow为局部存储器传输定义了两种机制:推模式(push model)和拉模式(pull model)。在推模式中,源过滤器生成数据并提交给下一级过滤器。下一级过滤器被动的接收数据,完成处理后再传送给再下一级过滤器。在拉模式中,源过滤器与一个分析过滤器相连。分析过滤器向源过滤器请求数据后,源过滤器才传送数据以响应请求。推模式使用的是IMemInputPin接口,拉模式使用IAsyncReader接口,推模式比拉模式要更常用。
利用DirectShow开发简单媒体播放器

  本节介绍基于DirectShow开发简单媒体播放器的关键步骤。

  1、初始化DirectShow

  由于DirectShow的组件都是以COM形式存在的,因此首先要调用CoInitializeEx函数来初始化COM库,嵌入所有的动态链接库和资源。否则,所有对QueryInterface的调用都会失败。

  2、创建过滤器图表管理器接口

  首先申明并初始化所需的接口:

// DirectShow interfaces
IGraphBuilder *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 GraphBuilder
CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER,
IID_IGraphBuilder, (void **)&pGB);
// QueryInterface for DirectShow interfaces
pGB->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 files
pGB->QueryInterface(IID_IVideoWindow, (void **)&pVW);
pGB->QueryInterface(IID_IBasicVideo, (void **)&pBV);
// Query for audio interfaces, which may not be relevant for video-only files
pGB->QueryInterface(IID_IBasicAudio, (void **)&pBA);

  3、创建过滤器图表

  应用DirectShow创建过滤器图表时,用户完全不需要操心 系统使用了哪一类过滤器以及过滤器是怎样连接的。只要调用IGraphBuilder::RenderFile函数,就可以建成一个完整的过滤器图表。

// Have the graph builder construct its the appropriate graph automatically
pGB->RenderFile(wFile, NULL);

  创建成功后,过滤器图表就可以用来播放多媒体文件了。DirectShow调用IMediaControl::Run函数来播放媒体文件。

// Run the graph to play the media file
pMC->Run();

  4、使用DirectShow的事件响应机制

  DirectShow的事件响应机制是过滤器图表管理器与用户进行交互的接口,DirectShow处理的可以是一些事先可以预期的事件,比如数据流的结束;也可以是一些无法预期的错误。有的事件可以由过滤器图表管理器自己处理,但如果过滤器图表管理器自己无法处理这些事件,它就把事件的通知放在事件队列里。用户 程序就可以通过IMediaEventEx接口得到事件,并对它做出相应的处理。

  5、清除DirectShow

  在 程序结束时必须调用Release函数释放DirectShow的接口指针,并调用CoUninitialize函数来卸载COM库,释放所有的动态链接库和资源。

   结束语

  应用DirectX的组件DirectShow进行多媒体应用程序的开发需了解多方面的知识,但在很多应用中利用DirectShow的特性可以减少工作量并能获得非常高的运行效率。在Visual C++ 6.0的开发环境中利用DirectShow开发的简单媒体播放器,具有随机播放、暂停和调整播放速率等功能,且可以播放多种媒体文件,播放效果非常流畅。因此,基于DirectShow开发多媒体应用程序的方法简单高效,是一种值得推荐的方法。

你可能感兴趣的:(show)