About DirectShow

本节描述DirectShow的整体结构。本节包含的内容比较丰富,我们可能不需要知道所有的这些知识。因此,我们首先应该选择浏览全部的内容,然后根据实际应用程序的需要查看Using DirectShow的内容。如果有关于DirectShow结构的特殊问题,可以再回过来参考本节的内容。

About DirectShow

本节描述DirectShow的整体结构。本节包含的内容比较丰富,我们可能不需要知道所有的这些知识。因此,我们首先应该选择浏览全部的内容,然后根据实际应用程序的需要查看Using DirectShow的内容。如果有关于DirectShow结构的特殊问题,可以再回过来参考本节的内容。

1. DirectShow System Overview

1.1 The Challenge of Multimedia

进行多媒体编程主要存在如下几个挑战:

    ·多媒体流包含大量的数据,这些数据又要求进行快速的处理

    ·音频、视频必须同步,以使它们在相同时间开始、停止,以相同的频率播放

    ·数据来源多,包括本地文件,计算机网络,电视广播和视频摄像机

    ·数据源的格式多,比如音频、视频交叉存取的(AVI),高级流格式(ASF),运动图像专家组(MPEG)和数字视频(DV)。

    ·程序员不能预先知道在用户终端系统存在的硬件设备

1.2 The DirectShow Solution

DirectShow就是被设计来解决这些挑战的。DirectShow通过把应用程序从复杂的数据传输、硬件区别和同步隔离起来。它的主要目标是简化在Windows平台创建数字媒体程序的任务。

为了达到流递音频、视频所需的流量,DirectShow在可能的任何时候使用DirectDraw和DirectSound。这些提交数据的数据有效的利用了用户的声卡和图形卡。DirectShow通过把媒体数据包装为带时间戳的Samples实现回放的同步。为了处理可能不同的数据源、格式和硬件设备,DirectShow使用模块化结构,通过这种结构应用程序可以混合、匹配不同的软件组件,这种组件称为Filters.

    DirectShow提供Filters支持捕捉和基于WDM的调频设备,Filters还支持旧的VFW捕捉卡,为音频压缩管理(ACM)编写的编解码器以及视频压缩管理器(VCM)接口。

应用程序、DirectShow组件、以及DirectShow所支持的部分硬件、软件组件之间的关系如图。

clip_image001

如同所示,DirectShow与各种不同的设备进行通信,并控制它们。包括本地文件系统,TV Tuner,视频捕捉卡,VFW编解码器,视频显示(通过DirectDraw或者GDI),以及声卡(通过DirectSound)。因此,DirectShow把应用程序与这些设备的复杂性隔离开来。DirectShow还为某些文件格式提供了本地的压缩、解压Filters.

2. The Filter Graph and Its Components

本条款描述DirectShow的主要组件。主要目的是为应用程序开发人员和编写自定义DirectShow Filter的开发人员做些介绍。应用程序开发人员通常可忽略DirectShow的一些低级细节。但是,阅读此节仍是好主义,可以对DirectShow的结构有一个大概理解。

2.1 About DirectShow Filters

    DirectShow基于模块化结构,每个处理阶段都由称为Filter的COM对象完成。DirectShow提供了一系列标准Filter用于应用程序开发,开发者也可以开发自己的Filter来扩展DirectShow的功能。为了举例说明,这里是一个播放AVI视频文件所需要的步骤,连同完成每步的Filters:

    ·从原始文件读取数据为字节流(File Source Filter)

    ·检测AVI头,把字节流分析为单独的视频帧和音频Samples(AVI Splitter Filter)

    ·解码视频帧(各种解码Filters, 取决于压缩格式)

    ·画视频帧(Video Renderer Filter)

    ·把音频Samples发送到声卡(默认的DirectSound设备Filter)

这些Filters及结构如图所示:

clip_image002

如图所示,每个Filter都与一个或多个Filter相连接。连接点也是一个COM对象,称为Pins。Filters使用PINS把数据从一个Filter移动到下一个。图中的箭头表示数据的流向。在DirectShow,一个Filters的集合称为Filter Graph。

    Filter有三种可能的状态,运行,停止,暂停。当一个Filter运行时,它就处理媒体数据流,当停止时,Filter就不处理数据,暂停状态用来在运行前Cue Data, 在Data Flow in the Filter Graph一节对这些概念有更详细的描述。除非特别的例外,状态改变都是协调贯穿在整个Filter Graph,Graph的所有Filter的状态改变都是统一的。因此,Filter Graph也可说是运行,停止,暂停。

    Filter 一般分为下面几种类型。

    (1)、源Filter(Source Filter):源Filter引入数据到Filter Graph,数据来源可以是文件、网络、照相机或者是任何地方。每个源Filter处理不同类型的数据源。

    (2)、变换Filter(Transform Filter):变换Filter的工作是获取输入流,处理数据,并生成输出流。编码、解码读书变换Filter的例子。

    (3)、提交Filter(Renderer Filter):提交Filter在Filter图表里处于最后一级,它们接收数据并把数据提交给用户。比如,视频Renderer把视频帧画在显示器,音频Renderer把音频数据发送到声卡,File-Writer Filter把数据写入文件。

    (4)、分割Filter(Splitter Filter):分割Filter把输入流分割成多个输出。例如,AVI分割Filter把一个AVI格式的字节流分割成视频流和音频流。

    (5)、混合Filter(Mux Filter):混合Filter把多个输入组合成一个单独的数据流。例如,AVI混合Filter把视频流和音频流合成一个AVI格式的字节流。它进行AVI分割Filter相反的操作。

这些Filters种类的区别并非绝对。比如ASF Reader Filter既是Source Filter又是Splitter Filter.

所有的Filters都暴露了IBaseFilter接口,所有的Pin都暴露了IPin接口。DirectShow也定义了一些其他的接口实现更特殊的功能。

2.2 About the Filter Graph Manager

    Filter Graph Manager也是一个COM对象,用来控制Filter Graph中的所有的Filter,主要有以下的功能:

    ·协调Filters之间的状态改变

    ·建立参考时钟

    ·传递事件到应用程序

    ·提供应用程序建立Filter Graph的方法

下面就这些功能做一个简单的说明。可以本文档的其他地方找到详细说明。

状态改变:Filters的状态改变必须以一种特殊顺序发生。因此,应用程序并不将状态改变的命令直接发给Filter,而是发送给Filter Graph Manager一个简单命令,由Manager将命令分发给Graph中每一个Filters。Seeking也是按同样的方式工作,先由应用程序将Seek命令发送到Filter Graph Manager,然后由其分发给每个Filters。

参考时钟:Graph中的Filter都采用的同一个时钟,称为参考时钟(Reference Clock),参考时钟可以确保所有的数据流同步,视频桢或者音频Sample应该被提交的时间称为Presentation Time。它是相对于参考时钟来确定的。Filter Graph Manager应该选择一个参考时钟,可以选择声卡上的时钟,也可以选择系统时钟。

Graph事件:Filter Graph Manager采用事件队列机制将Graph中发生的事件通知给应用程序,这个机制类似于Windows的消息循环。

Graph构建的方法:Filter Graph Manager给应用程序提供了将Filter添加进Graph的方法,连接Filter的方法,断开Filter连接的方法。

    Filter Graph Manager没有的一个功能就是把数据从一个Filter移动到另一个Filter。这是由Filters自己通过它们的PIN连接完成的。处理过程总是在不同的线程进行。

注意:Filters总是与Filter Graph Manager在同一进程,被进程类服务器加载。在Filters之间,Filters与Filter Graph Manager之间的函数调用都不会存在列举(Marshall)。

2.3 About the Media Type

因为DirectShow是基于模块化的,就需要有一种方式来描述Filter Graph每一个点的数据格式,例如,我们还以播放AVI文件为例,数据以RIFF块的形式进入Graph中,然后被分割成视频和音频流,视频流由一系列视频桢组成,而且还可能是压缩的。解码后,视频流由一系列的非压缩的位图组成,音频流也是同样的处理过程。

2.3.1   Media Types: How DirectShow Represents Format

媒体类型是一种很普遍的,可以扩展的用来描述数字媒体格式的方法,当两个Filter连接的时候,他们会在某种媒体类型达成一致。媒体类型决定了上一级Filter将要给下游的Filter发送什么类型的数据,以及数据的物理布局。如果两个Filters在媒体类型上没有达成一致,那么他们就没法连接起来。

对于某些应用程序,我们不需要担心媒体类型。比如在文件回放中,DirectShow处理了所有的细节。其他类型的应用程序可能需要直接在媒体类型上操作。

媒体类型是通过AM_MEDIA_TYPE结构定义的,此结构包含如下信息:

    ·主类型:它是一个GUID值,定义的全部的数据种类。主类型包括视频、音频、未解析的字节流、MIDI数据等等。

    ·子类型:也是GUID值,进一步定义媒体类型。比如,主类型是视频时,子类型可能是RGB-24、RGB-32和UYVY等等。对于音频,子类型可能是PCM音频、MPEG-1 Payload等等。主类型提供了比子类型更多的信息,但是它还没有定义格式的所有信息。比如,视频子类型没有定义图像大小和帧率。这些通过下面的格式子块说明。

    ·格式子块:它是描述格式细节的数据块。它是从AM_MEDIA_TYPE结构单独分配。AM_MEDIA_TYPE的pbFormat指针指向格式子块。

    pbFormat是一个void*的指针,因为格式块会因为媒体类型的不同而有不同的布局。PCM音频使用WAVEFORMATEX结构。视频块结构包括VIDEOINFOHEADER和VIDEOINFOHEADER2。AM_MEDIA_TYPE的formattype成员是指明在格式块所包含结构类型的GUID。每种结构都有一个GUID。cbForamt成员指定了格式块的大小。总是需要在废弃pbFormat指针前检测它的值。

如果格式块被填充,主类型和子类型的信息可以忽略。但是,在没有完整的格式块时,主类型和子类型提供了一种方便的方法来识别格式。比如我们可指定一个通用的24位RGB格式(MEDIASUBTYPE_RGB24),而不需要知道VIDEOINFOHEADER结构所需要的所有信息,比如图像大小和帧率。

比如,Filter可能使用下面的代码来检测媒体类型:

HRESULT CheckMediaType(AM_MEDIA_TYPE *pmt)

{

    if (pmt == NULL)         return E_POINTER;

    // Check the major type. We're looking for video.

    if (pmt->majortype != MEDIATYPE_Video)

       return VFW_E_INVALIDMEDIATYPE;

    // Check the subtype. We're looking for 24-bit RGB.

    if (pmt->subtype != MEDIASUBTYPE_RGB24)

    return VFW_E_INVALIDMEDIATYPE;

    // Check the format type and the size of the format block.

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

    (pmt->cbFormat >= sizeof(VIDEOINFOHEADER) && (pmt->pbFormat != NULL))

    {

        // Now it's safe to coerce the format block pointer to the

        // correct structure, as defined by the formattype GUID.

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

         return S_OK;

    }

    return VFW_E_INVALIDMEDIATYPE;

}

AM_MEDIA_TYPE结构也包含了一些可选信息。它们可提供一些附加信息。但是Filters并不要求使用它们:

    ·lSampleSize: 如果非0就表示每个Sample的大小。如果是0,表示Sample大小可能随时改变

    ·bFixedSizeSamples: 如果是TRUE,表示lSampleSize值有效,否则应该忽略lSampleSize

    ·bTemporalCompression: 如果是FALSE,表示所有的帧都是关键帧

2.4 About Media Samples and Allocators

    Filters通过Pin的连接来传递数据,数据流是从一个Filter的输出Pin流向相连的Filter的输入Pin。虽然有其他不少的传输机制存在,输出Pin最常用的传递数据的方式是调用输入Pin上的IMemInputPin::Receive方法。

根据的Filter不同,有多种方式来分配媒体数据的内存块,可以在堆上分配,可以在DirectDraw的表面,也可以采用GDI共享内存,还有其他的一些分配机制。内存分配的响应对象被称为Allocator,也是一个COM对象,暴露了IMemAllocator接口。

当两个Pin连接的时候,必须有一个Pin提供Allocator,DirectShow定义了一系列函数调用来确定由哪个Pin提供Allocator. PIN之间还会在Buffer的数量和大小上达成一致。

在数据流开始之前,Allocator会创建一个Buffer池,在数据流动期间,上一级Filter就会将数据填充到Buffers然后传递给下一级Filter。但是,上一级Filter并不是直接将内存Buffer的指针直接传递给下一级的Filter,而是通过一个Media Sample的COM对象,这个COM对象是Allocator创建的用来管理内存Buffer。Media Sample暴露了IMediaSample接口,一个Sample包含如下内容:

    ·指向内部Buffer的指针

    ·时间戳

    ·一些标志

    ·可选的媒体类型

时间戳定义了Presentation Time,Renderer Filter就根据这个时间来安排Render顺序的。标志用来标示数据从前一个Sample后是否中断等等,媒体类型提供了一种中途改变数据格式的方法。通常,一般Sample没有媒体类型,表明它的格式从前一个Sample后没有改变。

当一个Filter正在使用Buffer,它就会保持一个Sample的引用计数,Allocator通过引用计数用来确定是否可以重新使用一个Buffer。这样就防止了覆盖另一个Filter正在使用的Buffer。当所有的Filter都释放了对Sample的引用,Sample才返回到Allocator的内存池重新使用。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值