DirectShow SDK笔记【关于DirectShow(1)】

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 所支持的部分硬件、软件组件之间的关系如图。

    如同所示, 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 及结构如图所示:
    如图所示,每个 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的内存池重新使用。 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值