您的位置:首页 > 其它

一个清华学子写的关于directshow的学习心得

2017-01-06 18:30 260 查看
学习DirectShow有一段时间了,把这段学习过程中翻译出来的SDK与大家分享,同时也希望专家们指出我理解上的错误,万分感谢。

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:


 
    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同样为某种文件格式提供与之对应的编解码器。


3.2. Filter Graph和它的组件

    这一节描述了DirectShow的主要组件,为DirectShow应用程序和DirectShow Filter开发者提供一个介绍。应用程序开发者可以忽略掉很多底层部分,但是,了解底层对于理解DirectShow架构还是很有帮助的。

3.2.1. 关于DirectShow Filter

    DirectShow使用一个模块化的架构,每个处理过程都由一个叫做filter的COM对象来实现。DirectShow为应用程序提供了一系列标准的filter,开发者也可以编写自己的filter来扩展DirectShow的功能。下面是播放一个AVI文件的各个步骤:

    *从文件中读取数据并转成字节流(File Source filter)

    *检查AVI头,分析字节流并将它们分离成视频和音频(AVI Aplitter filter)

    *将视频解码(不同的解码filter,取决于不同的压缩格式)

    *将视频显示出来(Video Renderer filter)

    *将音频送入声卡(Default DirectSound Device filter)



    如图所示,每个filter与一个或多个其它的filter相连,其中的连接点也是一个COM对象,称作Pin,filter使用Pin将数据从一个filter转移到另一个,图中的箭头指示了数据流动的方向。在DirectShow中,这一系列连接在一起的filter称作filter
graph。

    Filter可能处于有三种不同的状态:运行、停止和暂停状态。filter在运行状态时处理数据,停止状态时停止处理数据,暂停状态则是表示就绪,可以 开始进入运行状态。除了极个别的情况,一个filter Graph中的所有filter通常都处理同一个状态下,因此,filter graph也可以称其处于运行、停止、暂停状态。

    Filter可以被分成几个大的种类:

    *source filter - filter graph的数据源,这些数据可以来自文件、网络、摄像头或任何其它东西。每一个source filter操纵不同类型的数据源。
    *transform filter
- 接收数据,处理数据并将它送入下一个filter。编码filter和解码filter都属于这个种类。
    *Renderer filter -
处于filter链的未端,接受数据并将其展现给用户。比如,一个视频renderer在显示器上绘制视频图像;一个音频renderer将音频数据送入 声卡;一个写文件filter(file-writer filter)将数据存盘。
    *splitter filter - 分析输入的数据流并将其分解成两路或多路,比如,AVI
splitter分析字节流并将其分解成视频流和音频流。
    *mux filter
- 将多路输入流合并成一路。比如,AVI Mux正好与AVI splitter做相反的工作,它将视频和音频流合成为一个AVI格式的字节流。
    以上的分类并不是绝对的,比如,ASF Reader Filter同时充当了source filter和splitter filter的角色。
    所有的DirectShow filter都提供IBaseFilter接口,所有的Pin也都提供IPin接口。DirectShow也定义了许多其它的接口以实现特定的功能。

3.2.2. 关于Filter Graph Manager

    Filter Graph Manager是一个用以控制filter graph中的filter的COM对象。它提供了许多功能,包括:

    *协调filter之间的状态变化

    *建立参考时钟(reference clock)

    *将事件返回给应用程序

    *提供应用程序建立filter graph的方法

    这里先简单地描述一个这些功能。

    状态变化:filter们的状态变化必须遵照一个特定的次序,因此,应用程序不能将状态变化的命令直接发给filter,而是将一个简单的命令发给 filter graph manager,由它来将命令分发给各个filter。定位命令同样使用这种方式,应用程序发送一个定位命令给filter graph manager,由它来分发。

    参考时钟:在filter graph中的所有filter都使用一个相同的时钟,称为参考时钟(reference clock)。参考时钟保证了所有流的同步。一个视频帧或一段音频样本被播放的时间钞称作呈现时间(presentation time)。呈现时间精确地相对于参考时钟。Filter Graph Manager通常选择的参考时钟是声卡参考时钟或系统时钟。
    Graph事件:filter graph manager使用一个消息队列来通知应用程序发生在filter graph中的事件。
    Graph-buliding 方法:filter graph manager提供给应用程序将filter加入到filter graph中的方法,以及将filter与filter连接或断开连接的方法。
    Filter graph manager不提供操纵在filter之间流动数据的功能,这个功能由filter通过pin连接在一个单独的线程中自行完成。

3.2.3. 关于媒体类型(Media Type)

    因为DirectShow是模块化的,因此需要有一个在filter graph各个点之间描述格式的方法。比如说,AVI回放,数据输入时是一个RIFF块的流,然后被分解成视频和音频流。视频流由一个个可能被压缩的视频 帧组成,解压后,视频流又变成了一系列未压缩的位图。音频与视频类似。

    Media Type:DirectShow怎样来描述格式
    Media Type是描述数字媒体格式的常用方式。当两个filter连接时,它们需要协商决定同一个Media Type。Media Type标识了从上一个filter递交到下一个filter或物理层的数据流格式。如果两个filter对Media Type不能协商一致,则不能连接。

    对于某些应用程序,你不必去关心Media type,比如文件回放,DirectShow做了所有有关它的事情。

    Media type使用AM_MEDIA_TYPE结构体来定义,这个结构体包含了以下内容:

    *Major type:主类型,是一个GUID,定义了数据的整体类型,包括了:视频、音频、未分析的字节流、MIDI等。

    *Subtype:子类型,另一个GUID,进一步定义了数据格式。比如,如果主类型是视频,则子类型可以是RGB-24、RGB-32、UYVY等格 式,如果主类型是音频,则可能是PCM或MPEG-1 payload等。子类型提供了比主类型更多的内容,但仍未提供完整的格式定义,比如,子类型没有定义图像尺寸和帧率,这些都将在Format block中被定义。

    *Format block:格式块,定义了具体的格式。格式块是AM_MEDIA_TYPE结构体中一个单独被分配的内存空间,pbFormat成员指向这块内存空间。 因为不同的格式会有不同的格式描述,所以pbFormat成员的类型是void*。比如,PCM音频使用WAVEFORMATEX结构体,视频使用不同的 结构体包括:VIDEOINFOHEADER和VIDEOINFOHEADER2。formattype成员是一个GUID,指定了格式块包含了哪种结构 体,每一种格式的结构体都被分配了GUID。cbFormat成员定义了格式式块的长度。

    当格式块被定义时,主类型和子类型包含的信息就显得有点多余了。其实,主类型和子类型为识别格式提供了一个便利的方法,比方说,你可以指定一个普通的24 位RGB格式(MEDIASUBTYPE_RGB24),而不需去关心VIDEOINFOHEADER结构体中诸如图像尺寸和帧率这些信息。

    下面是一个filter检查媒体类型的例子:

HRESULT CheckMediaType(AM_MEDIA_TYPE *pmt)

{

    if (pmt == NULL) return E_POINTER;

    // 检查主类型,我们需要的是视频

    if (pmt->majortype != MEDIATYPE_Video)

    {

        return VFW_E_INVALIDMEDIATYPE;

    }

    // 检查子类型,我们需要的是24-bit RGB.

    if (pmt->subtype != MEDIASUBTYPE_RGB24)

    {

        return VFW_E_INVALIDMEDIATYPE;

    }

    // 检查format type和格式块的大小.

    if ((pmt->formattype == FORMAT_VideoInfo) &&

         (pmt->cbFormat >= sizeof(VIDEOINFOHEADER) &&

         (pmt->pbFormat != NULL))

    {

        // 现在可以安全地将格式块指针指向正确的结构体。

        VIDEOINFOHEADER *pVIH = (VIDEOINFOHEADER*)pmt->pbFormat;

    

        // 检查pVIH (未展示). 如果正确,返回S_OK.

        return S_OK;

    }

    return VFW_E_INVALIDMEDIATYPE;

}
    AM_MEDIA_TYPE结构体还包含了一些任选项,用来提供附加的信息,filter不需要这些信息:
    *ISampleSize,如果这个字段非零,表示这是每个sample的尺寸,如果是零,则表示sample的尺寸会改变。
    *bFixdSizeSamples,如果这个布尔类型的标记是TRUE,表示ISampleSize有效,否则,你可以忽略ISampleSize。

    *bTemporalCompression,如果这个布尔类型的标记是FALSE,表示所有帧都是关键帧。

3.2.4. 关于媒体样本(Media Sample)和分配器(Allocator)

    Filter通过Pin与Pin之间的连接来递交数据,数据从一个filter的输出Pin转移到另一个filter的输入Pin,除了个别情况,实现这种功能通常的方法是调用输入Pin上的IMemInputPin::Receive方法。
    依靠filter,媒体数据的内存空间可以通过多个途径来分配:在堆上、在DirectDraw表面(surface)、在共享GDI内存或使用其它的分配机制。这个负责分配内存空间的对象称为分配器(Allocator),是一个暴露IMemAllocator接口的COM对象。
    当两个Pin相连时,其中的一个Pin必须提供一个分配器。DirectShow定义了一个方法调用序列来决定到底由哪个Pin来提供分配器。Pin还负责协商分配器创建的缓冲数和每个缓冲的尺寸。
    在数据流开始之前,分配器创建了一个缓冲池。在数据流动过程中,上游filter在缓冲中填入数据并递送给下游filter,但是,上游filter递送给下游filter的并不是原始的缓冲区指针,而是一个称为媒体样本(Media
Sample)的COM对象,它由分配器创建并用来管理缓冲区,暴露IMediaSample接口。一个媒体样本包含:
    *指向下层缓冲区的指针
    *时间戳
    *各种标记
    *可选的媒体类型
    时间戳定义了呈现时间(presentation time),用以让renderer
filter确定播放的合适时机。各种标记可以用来指示很多事情,比如,数据在上一个sample后是否被打段过(如重新定位、掉帧)等。媒体类型为流中 间改变数据格式提供了途径,通常,没有媒体类型的sample,被认为从上一个sample以来数据格式没有被改变过。
    当filter使用一个缓冲时,它保存了sample上的参考计数。分配器使用参考计数来决定什么时候可以重用这个缓冲,这防止了一个filter在写一 个缓冲时另一个filter还在使用这个缓冲,除非所有的filter都释放了这个缓冲,否则sample不会将其返回给分配器的缓冲池。
    

3.2.5. 硬件如何参与Filter Graph

    这一节描述了DirectShow如何与音频和视频硬件交互。
    
     外壳filter(Wrapper
Filter)
    所有的DirectShow filter都是用户模式的软件组件。为了使象视频采集卡这样的内核模式的硬件驱动加入到filter graph中,必须使其象用户模式的filter那样。DirectShow提供外壳filter来完成这个功能,这类filter包括:Audio
Capture filter、VFW Capture filter、TV Tuner filter、TV Audio filter和Analog Video Crossbar filter。DirectShow也提供一个叫KsProxy的filter,它可以实现任何类型的WDM流驱动。硬件商通过提供一个Ksproxy plug-in来扩展KsProxy,以使其支持自己的功能,ksproxy plug-in是一个被KsProxy聚合的COM对象。
    外壳filter通过暴露COM接口来实现设备的功能。应用程序使用这些接口将信息传递给filter,filter再把这些COM调用转化为设备驱动调 用,将信息传递到内核模式下的设备中去,然后返回结果给应用程序。TV
Tuner、TV Audio、Analog Video Crossbar和KsProxy filter都通过IKsPropertySet接口来支持驱动的自定义属性,VFW Capture filter和Audio Capture filter不支持这种方式。
    外壳filter使应用程序可以象控制其它directshow filter一样来控制设备,filter已经封装了与内核驱动通信的细节。
    
    Video for Windows Devices
    VFW Capture filter支持早期的VFW采集卡,当一个设备加入到目标系统中支后,它可以被directshow使用系统设备枚举器(System Device Enumerator)发现并加入到filter
graph中去。
    音频采集(Audio Capture)和混音设备(声卡)(Mixing
Device/Sound Card)
    较新的声卡都有麦克风等设备的插口,而且大多数这类声卡都有板级的混频能力,可单独控制每一个连接设备的音量及高低音。在directshow中,声卡的 输入和混频设备被Audio Capture filter封装。每个声卡都能被系统设备枚举器发现。要查看你的系统中的所有声卡,只需打开GraphEdit,从Audio
Capture Sources一类中选择即可,每个在这个类里的filter都是一个单独的Audio Capture filter。

    WDM流设备

    较新的硬解码设备和采集卡都遵照WDM规范。这些设备和比VFW设备更强大的功能,以及可以应用于多种系统 (winxp,winNT,win2000,win98/me)。WDM视频采集卡支持许多VFW所没有的功能,包括枚举采集的格式、编程控制视频参数 (如对比度、亮度)、编程选择输入端和电视调谐支持。

    为了支持WDM流设备,directshow提供了KsProxy filter(ksproxy.ax)。KsProxy被称为“瑞士军刀",因为它可以做很多不同的事情。filter上pin的数量,以及COM接口的 数量,取决于底层驱动的能力。KsProxy不以"KsProxy"这个名字显示在filter graph中,而是使用一个已在注册表中登记的设备名称。要查看你系统中的WDM设备,可以运行GraphEdit然后从WDM Streaming这个类别中选择。即使你的系统中只有一块WDM卡,这块卡也可能包含多个设备,而每一个设备都表现为一个filter,每个
filter是实际意义上的KsProxy。

    应用程序使用系统设备枚举器在系统中寻找WDM设备moniker,然后调用moniker的BindToObject来实例化。因为KsProxy能够 表现所有类型的WDM设备,因此它必须通过询问驱动来决定哪些属性是驱动所支持的。属性集是一组数据结构的集合,被WDM设备使用,也被诸如MPEG2软
解码filter这样的用户模式filter使用。KsProxy通过暴露COM接口来配置自己,硬件商则通过提供插件来扩展KsProxy,插件暴露硬 件商自定义的一些接口,用以实现特殊的功能。所有这些细节对于应用程序来说都是不可见的,应用程序通过KsProxy控制设备就象控制其它的 DirectShow filter一样。

    

    内核流

    WDM设备支持内核流,在内核流中数据在内核模式下被彻底流化而永远不需要切换到用户模式下去,从而避免了在内核模式和用户模式之间切换的巨大开销,内核 流允许高的比特率而不消耗CPU的时间。基于WDM的filter能够使用内核流将多媒体数据一个硬件设备送入到另一个中去,既可以是在同一块卡中也可以 在不同的卡中,而不需要将数据拷入系统主存。

    从应用程序的视点来看,数据好象是从一个用户模式的filter传到另一个中去,但是实际上,数据根本就没有传到用户模式下过,而是可能支接从内核模式的 设备中传到下一个中去直至被呈现(render)在显卡上。某些情况,比如采集视频到一个文件中去,在某些点上需要将数据从内核模式传入到用户模式,但 是,仍然没有必要将数据拷贝到内存的一个新位置中去。

    应用程序开发者通常只需了解一个内核流的背景知识而不需要深究它的细节。

3.3. 构建Filter Graph

3.3.1. 用于构建Graph的组件

    DirectShow提供了一系列用于构建filter graph的组件,包括:
    *Filter Graph Manager。这个对象用于控制filter
graph,支持IGraphBuilder、IMediaControl和IMediaEventEx等许多接口。所有的directshow应用程序 都需要在某些地方用到这个对象,虽然在有些情况下,是其它的对象为应用程序创建了filter graph manager。
    Capture
Graph Builder
。这个对象为构建filter graph提供附加的方法。它最初是为构建提供视频采集的graph而设计的(这正是它的名字由来),但是对于构建许多另外类型的filter graph也是很有用的。它支持ICaptureGraphBuilder2接口。
    Filter
Mapper和System Device Enumerator
。这些对象用于查找在系统中注册的或代表硬件驱动的filter。
    DVD
Graph Builder
。这个对象构建用以回放和导航DVD的filter graph。它支持IDvdGraphBuilder接口。基于脚本的应用程序能够使用MSWebDVD ActiveX控件来控制DVD回放。
    Video
Control
。WinXP提供这个ActiveX控件,用于操纵directshow中的数据和模拟电视。
    
    智能连接(Intelligent
Connect)

    智能连接这个术语覆盖了一系列Filter Graph Manager用于构建所有或部份filter graph的算法。任何时候,当Filter
Graph Manager需要添加filter来完成graph时,它大致做以下几件事情:
    1.如果有一个filter存在于graph中,而且这个filter有至少一个没有连接的input pin,Filter Graph Manager试着去试用这个filter。
    2.否则,Filter Graph Manager在已注册的filter中寻找连接时可以接受合适的媒体类型的filter。每一个filter都注册有一个Merit值,这个值用以标记 哪个filter最容易被Filter
Graph Manager选中来完成graph。Filter Graph Manager按Merit值的顺序来选择filter,Merit值越大,被选中的机会越大。对于每种流类型(如音频、视频、MIDI),默认的 renderer具有一个很高的Merit值,解码器同样是,专用filter具有低Merit值。
    如果Filter Graph Manager因选择的filter不合适而被困,它会返回来尝试另外的filter组合。

3.3.2 Grap构建概述

    创建一个filter graph,从创建一个Filter Graph Manager实例开始:

     IGraphBuilder* pIGB;

     HRESULT hr = CoCreateInstance(CLSID_FilterGraph,

     NULL, CLSCTX_INPROC_SERVER, IID_IGraphBuilder,(void **)&pIGB);
     Filter
Graph Manager支持下列Graph构建方法:

    *IFilterGraph::ConnectDirect,在两个pin之间进行直接连接,如果连接失败,则返回失败
    *IFilterGraph::Connect,连接两个Pin,如果可能的话,直接连接它们,否则,在中间加入其它的filter来完成连接。
    *IGraphBuilder::Render,从某个输出Pin处开始完成余下的graph构建。该方法会自动在输出pin后面添加必须的filter,直到renderer
filter为止。
    *IGraphBuilder::RenderFile,构建一个完整的文件回放graph。
    *IGraphBuilder::AddFilter,将一个filter添加到graph中。它不连接
filter,并且在调用此方法前,filter必须已经被创建。创建filter可以是用CoCreateInstance方法或使用Filter Mapper或系统设备枚举器(System Device Enumerator)。
    这些方法提供了三种构建graph的途径:
    1.filter
graph manager构建整个graph
    2.filter
graph manager构建部分graph
    3.应用程序构建整个graph
    
    Filter
Graph Manager构建整个graph
    如果你仅仅是想回放一个已知格式的文件,如AVI、MPEG、WAV或MP3,使用RenderFile方法。
    RenderFile方法首先寻找注册在系统中能分析源文件的filter,它使用协议名(如http://),文件扩展名或文件的头几个字节来决定选择哪一个源filter。
    Filter
Graph Manager使用一个迭代过程来完成余下的graph构建。在这个迭代过程中,它逐个列出filter的输出pin上支持的媒体类型,并搜索哪个已注册 的filter的输入Pin接受该媒体类型。它使用一系列的规则来缩小filter的范围并排定优先顺序:
  
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  directshow