DirectShow编程

                                      

最近一段时间,在编写DirectShow应用程序时常常遇到一些问题,原因是对DirectShow技术没有较全面地掌握,对各个接口间的关系以及filter与filter之间连接的内部过程等都只是一知半解,除了再仔细地看看DirectShow的基类库源文件之外,觉得也很有必要从头到尾看一遍DirectShow的MSDN文档。在看时顺便有选择地翻译出来,一来以便以后再看时可以轻松点,二来也敦促自己不能不求甚解早早看看了事。在翻译的过程中也加了一些自己的补充,因为觉得某些MSDN章节实在是过于简单还有些模棱两可。

1. DirectShow介绍
    DirectShow是一个windows平台上的流媒体框架,提供了高质量的多媒体流采集和回放功能。它支持多种多样的媒体文件格式,包括ASF、MPEG、AVI、MP3和WAV文件,同时支持使用WDM驱动或早期的VFW驱动来进行多媒体流的采集。DirectShow整合了其它的DirectX技术,能自动地侦测并使用可利用的音视频硬件加速,也能支持没有硬件加速的系统。
    DirectShow大大简化了媒体回放、格式转换和采集工作。但与此同时,它也为用户自定义的解决方案提供了底层流控制框架,从而使用户可以自行创建支持新的文件格式或其它用途的DirectShow组件。
    以下是几个使用DirectShow编写的典型应用:DVD播放器、视频编辑应用、AVI到ASF转换器、MP3播放器和数字视频采集应用。
    DirectShow是建立在组件对象模型(COM)上的,因此当你编写DirectShow应用时,你必须具备COM客户端程序编写的知识。对于大部分的应用,你不需要实现自己的COM对象,DirectShow提供了大部分你需要的DirectShow组件,但是假如你需要编写自己的DirectShow组件,你还需要具备编写COM组件的知识。


1.1. DirectShow支持的格式
    DirectShow是一个开放的框架,因此只要有合适的filter来分析和解码,它可以支持任何格式。DirectShow默认支持以下的文件类型和压缩格式:
    注:打*号的需要Windows Media Format SDK支持
    文件类型:
      Windows Media? Audio (WMA)*
      Windows Media? Video (WMV)*
      Advanced Systems Format (ASF)*
      Motion Picture Experts Group (MPEG)
      Audio-Video Interleaved (AVI)
      QuickTime (version 2 and lower)
      WAV
      AIFF
      AU
      SND
      MIDI
    压缩格式:
      Windows Media Video*
      ISO MPEG-4 video version 1.0*
      Microsoft MPEG-4 version 3*
      Sipro Labs ACELP*
      Windows Media Audio*
      MPEG Audio Layer-3 (MP3) (decompression only)
      Digital Video (DV)
      MPEG-1 (decompression only)
      MJPEG
      Cinepak
    微软自己没有提供MPEG2解码器,一些可用的DirectShow MPEG2硬件或软件解码器是由第三方提供的。
1.2. 常见问题集(摘录)
1.2.1. 一般问题

    *DirectShow支持哪些操作系统?
      DirectShow支持Windows9X、Windows2000、Windows Me和Windows XP。
   
    *使用DirectShow需要多少COM知识?
      应用程序开发者只需要基本的COM组件知识:实例化COM组件、调用接口、管理接口的引用计数。Filter开发者则需要更多。
     
    *有与DirectShow兼容的硬件列表(HCL)吗?
      没有。如果硬件兼容DirectShow,DirectShow会使用它们,如果没有兼容的硬件,DirectShow使用GDI绘制视频,以及使用WaveOut系列多媒体API来播放音频。
     
    *可以使用哪些语言来编写DirectShow应用?
      DirectShow主要为C/C++开发设计。Visual Basic只能使用其中的很小一部分。可以通过MS JScript或VB Script来支持基于脚本的DVD和TV应用。也可能用Delphi来编写,但SDK文档不提供这方面的内容。
     
    *DirectShow会通过托管代码实现吗?
      目前还没有这个计划。DirectX SDK提供了有限的使用音视频回放类的托管回放功能,你可以使用COM interop创建托管代码的DirectShow客户端应用,但是因为性能上的原因,不推荐创建运行在CLR上的filter。

    *DirectShow开发需要什么样的编译器
      任何能够产生COM对象的编译器都可以。
     
    *DirectShow和DirectX的其它组件的关系
      DirectShow和DirectX的其它组件在内部进行联系。DirectShow在硬件的支持下使用DirectSound和DirectDraw。Video Renderer和Overlay Mixer使用DirectDraw 3和DirectDraw5表面(surfaces)。Video Mixing Renderer 7(只支持WINXP)使用DirectDraw7表面。Video Mixing Renderer 9使用最新的(目前是Directx9)Direct3D API函数。即便是某个应用程序包含了DirectX其它组件,你也不必使用其它组件的API去编写它。参考SDK的例子:Texture3D Sample。
     
    *DirectShow与ActiveMovie的关系?
      ActiveMovie是DirectShow原来的名称,现已不再使用,但是一部分API仍保留了"AM"的前缀,比如AM_MEDIA_TYPE和IAMVideoAccelerator。
     
    *DirectShow是限于多媒体应用吗?
      DirectShow默认包含的组件主要是为音视频流设计的,但是,DirectShow框架已经成功地用于其它数据流的解决方案中。
     
    *GraphEdit工具有源码吗?GraphEdit.exe是否可再发布?
      没有源码,不可再发布。
     
    *DMO可以代替DirectShow filter吗?
      在编写编码器、解码器、效果器应用时,鼓励用DMO代替DirectShow filter。在其它的应用中,使用DirectShow filter可能会比较合适。
     
1.2.2. 程序编写问题
    *如何设置编译环境,需要哪些头文件和库?
      参考"设置编译环境"章节
     
    *GraphEdit列示了很多没有文档支持的filter,它们都是些什么?
      GraphEdit枚举了所有作为filter类型注册在系统中的filter,包括由第三方应用程序安装的filter,以及其它微软技术如Windows Media或NetMeeting安装的,另外,一些DirectShow filter被用来做硬编码或硬解码驱动的外壳。Microsoft H.263 Video Codec用于NetMeeting,不再被DirectShow支持。
   
    *如何知道DirectShow已经被安装?
      调用CoCreateInstance创建一个Filter Graph Manager实例,如果成功,表示DirectShow已经被安装,下面是一个例子:

      IGraphBuilder *pGraph;

       HRESULT hr = CoCreateInstance(CLSID_FilterGraph,
             NULL, CLSCTX_INPROC_SERVER,
             IID_IGraphBuilder, (void **) &pGraph);


           
    *如果不通过属性设置页来更改filter的设置?
      当然是通过filter提供的接口罗。如果没有提供,就没有办法啦
     
    *DirectShow能通知应用程序当前回放位置吗?
      不提供回调来通知位置,需要使用一个计时器定时调用IMediaSeeking::GetCurrentPosition方法来得到当前回放位置。
     
    *filter运行在哪个特权级别下?
      运行在Ring 3特权级别下,某些流控制驱动(如音视频采集驱动)运行在Ring 0特权级别下。
     
    *需要一个Kernel调试器吗?
      这依据具体的项目。安装DirectX调试运行时库(DirectX debug runtime library)意味着安装调试驱动(Debug driver)和其它核心组件(kernel mode component),因此如果你的应用程序在其中的某个组件中产生了一个调试断言(debug assert),你的机器就会自动重启除非你拥有一个kernal调试器。
     
    *DEFINE_GUID宏是怎么工作的?
      使用DEFINE_GUID宏可以让你通过包含同一个头文件来定义GUID值而不必使用extern关键词。比如,你的工程中有三个源文件:src1.cpp,src2.cpp,src3.cpp,它们都使用一个相同的GUID值,而为了保证一致性,这个GUID只能在你的工程中定义一次,这时,其它的源文件必须定义外部引用来使用它。用了DEFINE_GUID,你可以使用在所有源文件中包含同一个头文件,在头文件中这样定义GUID:

    DEFINE_GUID(CLSID_MyObject,
         0x00000000, 0x0000, 0x0000, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);


            这个例子中GUID为0,实际编程中请用Guidgen工具来产生一个GUID,在其中的一个源文件中,在你的头文件前包含initguid.h,如:

  // Src1.cpp
 #include
 #include "MyGuids.h"
 
 // Src2.cpp
 #include "MyGuids.h"

 // Src3.cpp
 #include "MyGuids.h"


      
 在没有包含Initguid.h的地方,DEFINE_GUID宏创建外部引用来使用GUID值,在包含Initguid.h的地方,DEFINE_GUID重定义DEFINE_GUID宏以产生GUID的定义。
 如是没有在任何地方添加Initguid.h,你会得到一个链接错误:"unresolved external symbol." ,如果同样的GUID包含Initguid.h两次,会得到编译错误"redefinition; multiple initialization."要解决这些问题,请确认Initguid.h只包含一次。同样的,不要包含Initguid.h到预编译头文件中去,因为预编译头文件会被每个源文件包含。                                       

2. 开始DirectShow旅程
    这个章节的内容主要是编写DirectShow应用所需的一些基本概念,可以把它当作一个高级介绍,理解这些内容只需具备一般的编程和有关多媒体的知识。
2.1. 设置DirectShow开发的编译环境
    这节内容描述了如何来编译DirectShow应用。你可以使用命令行形式来编译一个工程,也可以在Microsoft Visual Studio集成环境下(包含VC++)实现。
    头文件:
    所有的DirectShow应用都需要Dshow.h这个头文件,某些DirectShow接口需要附加的头文件,参考接口的说明视具体情况定。
    库文件:
    DirectShow使用以下库文件:
    Strmiids.lib 输出类标识(CLSID)和接口标识(IID),所有DirectShow应用均需此库。
    Quartz.lib   输出AMGetErrorText函数,如果不调用此函数,此库不是必需的。
    有了以上这些头文件和库文件,你已经可以编写DirectShow应用了,但是微软建议使用DirectShow基类库来编写filter,这样可以大大减少程序编写的工作量。要使用DirectShow基类库,需要先编译它,基类库位于SDK的Samples/Multimedia/DirectShow/BaseClasses文件夹下,包含两个版本的库:发布版(retail version)Strmbase.lib和调试版(debug version)Strmbasd.lib。具体参见"创建DirectShow Filter"一节。

2.2. DirectShow应用程序编程简介
    这节介绍DirectShow用到的一些基本术语和概念,看完这节后,你将能够编写你的第一个DirectShow应用程序。
    Filter和Filter Graph
    一个DirectShow应用程序是由一个个称为filter的软件构件组合而成的,filter执行一些多媒体流的操作,如:读文件、从视频采集设备中获得视频、将不同的格式的流解码如MPEG1、将数据送到图形卡或声卡中去。
    Filter接收输入并产生输出。举个例子,一个解码MPEG1视频流的filter,输入MPEG1格式的视频流,输出一系列未压缩的视频帧。
    在DirectShow中,应用程序要实现功能就必须将这些filter链接在一起,因而一个filter的输出就变成了另一个filter的输入。这一系列串在一起的filter称为filter graph。例如,下图就显示了一个播放avi文件的filter graph:
avi play filter graph
    File Source(Async) filter从硬盘中读取avi文件;AVI Splitter filter分析文件并将其分解成两个流:一个压缩的视频流和一个音频流;AVI Decompressor filter将视频帧解码,Video Renderer filter将解码后的视频帧通过DirectDraw或GDI显示出来;Default DirectSound Device filter使用DirectSound播放音频流。
    应用程序没有必要对这些数据流进行管理,而是通过一个叫Filter Graph Manager这个上层组件来控制这些filter。应用程序调用上层API如"Run"(通过graph移动数据)或"Stop"(停止移动数据)。如果你需要对数据流作更多的操作,你可以通过COM接口直接进入filter。Filter Graph Manager同样也输出事件通知给应用程序。
    Filter Graph的另一个用途是将filter连在一起创建一个filter graph。
    编写一个DirectShow应用程序大体需要三个步骤:
    1.创建一个Filter Graph Manager的实例
    2.使用Filter Graph Manager创建一个filter graph,此时,需要已经具备所有必需的filter。
    3.使用Filter Graph Manager控制filter graph和通过这些filter的流,在这个过程中,应用程序会收到Filter Graph Manager发送的事件。
    完成这些后,应用程序需发布这个Filter Graph Manager和所有的filter。
2.3. 播放一个文件
    这一章以本节这个有趣的例子来结束,这个例子是一个播放音频或视频文件的简单控制台程序。程序只有寥寥数行,但却展示了DirectShow编程的强大能力。
    正如上一节所讲的创建DirectShow应用程序的三个步骤,第一步,首先,需要调用CoInitialize来作初始化,然后调用CoCreateInstance创建Filter Graph Manager:
   

    HRESULT hr = CoInitialize(NULL);
    if (FAILED(hr))
    {
        return;
    }


    IGraphBuilder *pGraph;
    HRESULT hr = CoCreateInstance(CLSID_FilterGraph, NULL,
        CLSCTX_INPROC_SERVER, IID_IGraphBuilder, (void **)&pGraph);

      
    如上所示,类标识符(CLSID)是CLSID_FilterGraph。Filter Graph Manager由进程内DLL(in-process DLL)提供,因此参数3,dwClsContext的值为CLSCTX_INPROC_SERVER。由于DirectShow运行自由线程模式(free-threading model),所以你同样可以使用COINIT_MULTITHREADED参数来调用CoInitializeEx。
    第二步是创建filter graph,调用CoCreateInstance得到的IGraphBuilder接口包含了大部分创建filter graph的方法。在这个例子中还需要另外两个接口:IMediaControl和IMediaEvent。
    IMediaControl控制数据流,它包含开启和停止graph的方法;IMediaEvent包含从Filter Graph Manager获取事件的方法,在这个例子中,这个接口用来得到回放结束事件。
    所有这些接口由Filter Graph Manager提供,使用得到的IGraphBuiler接口指针来查询得到。

    IMediaControl *pControl;
    IMediaEvent   *pEvent;
    hr = pGraph->QueryInterface(IID_IMediaControl, (void **)&pControl);
    hr = pGraph->QueryInterface(IID_IMediaEvent, (void **)&pEvent);


    现在你可以创建filter graph了,对于文件回放只需要一个简单的调用:

   hr = pGraph->RenderFile(L"C://Example.avi", NULL);

   
    IGraphBuilder::RenderFile方法创建了一个能够播放指定文件的filter graph,事实上,原本需要做的一些如创建filter实例及将这些filter连接起来的工作,都由这个方法自动完成了,如果是视频文件,这个filter graph看起来应该是这个样子:
    [file source]->[如果是缩格式,这里是个解码器]->[Video Renderer]
    要开始回放,调用IMediaControl::Run方法:

      hr = pControl->Run();



    当filter graph运行时,数据经过各个filter最后回放为视频或音频。回放发生在一个单独的线程中。你可以通过调用IMediaEvent::WaitForCompletion方法来等待回放的结束:

      long evCode = 0;
    pEvent->WaitForCompletion(INFINITE, &evCode);



    这个方法在播放期间被阻塞,直至播放结束或超时。
    当应用程序结束时,需要释放接口指针并关闭COM库:

    pControl->Release();
    pEvent->Release();
    pGraph->Release();
    CoUninitialize();


    下面是这个例子的完整代码:
   

  #include
void main(void)
{
    IGraphBuilder *pGraph = NULL;
    IMediaControl *pControl = NULL;
    IMediaEvent   *pEvent = NULL;

    // Initialize the COM library.
    HRESULT hr = CoInitialize(NULL);
    if (FAILED(hr))
    {
        printf("ERROR - Could not initialize COM library");
        return;
    }

    // Create the filter graph manager and query for interfaces.
    hr = CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER,
                        IID_IGraphBuilder, (void **)&pGraph);
    if (FAILED(hr))
    {
        printf("ERROR - Could not create the Filter Graph Manager.");
        return;
    }

    hr = pGraph->QueryInterface(IID_IMediaControl, (void **)&pControl);
    hr = pGraph->QueryInterface(IID_IMediaEvent, (void **)&pEvent);

    // Build the graph. IMPORTANT: Change this string to a file on your system.
    hr = pGraph->RenderFile(L"C://Example.avi", NULL);
    if (SUCCEEDED(hr))
    {
        // Run the graph.
        hr = pControl->Run();
        if (SUCCEEDED(hr))
        {
            // Wait for completion.
            long evCode;
            pEvent->WaitForCompletion(INFINITE, &evCode);

            // Note: Do not use INFINITE in a real application, because it
            // can block indefinitely.
        }
    }
    pControl->Release();
    pEvent->Release();
    pGraph->Release();
    CoUninitialize();
}

 

3. 关于DirectShow
3.1. DirectShow体系概述
    多媒体的难题
    处理多媒体有几个主要的难题:
    *多媒体流包含了巨大的数据量,而这些数据都必须非常快地被处理
    *音频和视频必须同步,因此它们必须在同一时间开始或停止,并以同一速率播放
    *数据可能来自很多的源,如本地文件、网络、电视广播和视频摄像机
    *数据有各种各样的格式,如AVI、ASF、MPEG和DV
    *程序员无法预知最终用户使用什么样的硬件设备
    DirectShow的解决方案
    DirectShow被设计成用来解决所有这些难题,它主要的设计目的就是通过将复杂的数据转输、硬件的多样性和同步问题从应用程序中独立出来,从而简化在windows平台上数字媒体应用程序的开发任务。
    要实现数据高效地被处理,需要流化音视频数据,而DirectShow会尽可能地使用DirectDraw和DirectSound,从而高效地将数据送到用户的声音和图形设备中进行播放。同步则是通过在媒体数据中加入时间戳来实现。而DirectShow模块化的架构,使其可以轻松操纵变化多端的源、格式和硬件设备,在这样的架构里,应用程序只需组合和匹配多个filter来实现功能。
    DirectShow提供的filter支持基于WDM的采集和调谐设备,也支持早先的VFW采集卡和为ACM和VCM接口编写的编码器。
    下图显示了应用程序、DirectShow组件和DirectShow支持的硬件和软件组件之间的关系:
       

    如图,DirectShow将应用程序与众多复杂的设备隔离开来,通信和控制这些设备均出DirectShow的filter来完成。DirectShow同样为某种文件格式提供与之对应的编解码器。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值