admin管理员组

文章数量:1530928

2023年12月27日发(作者:)

文件系统驱动编程基础篇之六——DirectShow

一、 前略

本系列文章为业余编程爱好者而写,仅仅作为初学者的一个借鉴,真正的精华存在于参考资料*中。知识的积累将经历从薄到厚,再从厚到薄的反复过程,为了打下牢固的基础,请读者务必在阅读本文的基础上花费必要的时间完成参考资料。

参考资料*:

1.《Programming Microsoft DirectShow for Digital Video and Television》及其示例代码

2.《DirectShow for DirectX 8.1 SDK C++》

3. Microsoft® Windows® Software Development Kit Update for Windows Vista™ 之相关文档及其示例代码

4.《DirectShow开发快速入门之慨述》

5.《深入解析ATL (ATL Internals Second Edition ——Working with ATL 8)》

6.《COM 组件设计与应用(一)起源及复合文件》(/document/viewdoc/?id=1483)

阅读基础:少量的COM编程的基础知识,了解如何调用COM组件的方法,最好掌握哪怕一丁点的ATL。

本章目的:了解DirectX组件架构,学习编写简单的DirectShow程序。

二、DirectX与DirectShow

首先看下面两段关于DirectX和DirectShow相关内容的摘要:

DirectX的第一个版本作为Windows Games SDK发布于1995年9月,它作为Windows API的一部分用以替换Windows 3.1中的DCI和WinGAPI。ATI的一个开发团队为微软带来了基本的游戏影像技术,微软方面,DirectX由专门的团队负责开发,Eisler 为团队领导,而St. John和Engstrom则成为主程序设计师。

2005年4月,DirectShow从DirectX移除,加入到Microsoft Platform SDK。

Windows系统的架构下,我们不能如Dos般随心所欲的控制硬件,而希望游戏、影像开发人员对内核的理解都达到驱动编程人员的水平,是一件苛求的事情,但是为此将失去广大市场的关键问题必然要产生解决的办法,因此,DirectX或其他不同名的相同技术的出现是件必然的。

有些读者可能会对驱动编程的基础文章涉及DirectX的必要性有所疑问,确实,DirectX并不是学习驱动编程的一个障碍,笔者在初次的学习中也从未意识过要了解这方面的内容。但是换一个角度,如果你看到鸡窝里的五个鸡蛋,我们是不是很有理由猜测这是由五只母鸡组成的五好家庭?因此我们可很牵强的认为DirectX如同内核的外部据点,如果我们拔除了,必然有更大的把握向内核发动总攻。

笔者把DirectX加入本系列文章的另一个重要原因是——DirectX确实有值得炫耀的资本。对于编程人员来说,它的COM接口简直可用“优雅”来评价,当你用着如苦瓜般的IMarshal接口时,不妨来看看什么样的接口值得这样的评价。我挑选了DirectX里一个很有意思的部分——DirecShow来作为突破口,讲述如何“Direct”——直接的控制视频设备。

学习的进阶大略上可分为四个部分:学习调用组件、捕获和编辑影像、学习编写过滤器、掌握媒体格式的高阶部分。限于能力,只介绍前两个部分。读者不要满足于学会使用DirctShow,而是通过DirctShow的具体功能猜测系统驱动的功能,甚至设备的构成,减轻将来主攻驱动时的负担,此即由外而内的学习方法。

笔者将掠人之美,使用参考资料1提供的示例阐明编程的基本思路。

三、DirectShow基础和GraphEdit

在Windows系统中,DirectShow以COM组件的形式存在,下图描绘了DirectShow组件、硬件以及系统组件的关系(Leagcy为传统或遗留,旧的之意)。

现在我们只需了解DirectShow里包含了两大类型的对象:三类“Filter”(源、转换、渲染过滤器),以及由这些过滤器集组成提供特定功能的“Filter Graph”,这个Graph不妨看成是包含Filter的一个容器。

为了从可视化的角度理解DirectShow工作的方式,我们可使用SDK附带的GraphEdit工具。它最基本的功能是利用系统里已经安装的解码器,使用DirectShow组件来播放媒体文件,并直观的显示播放流程。下面演示一下如何播放一个媒体文件。

GraphEdit的界面如图:

从菜单文件——渲染媒体文件„里选择你要播放的文件,这里我们选择一个微软支持的avi格式文件,则界面上出现:

每一个DirectShow组件都以矩形框表示,Input、Output针(pin)作为每个过滤器的输入或输出。通常情况下,源(左上角的ffi_full_)、渲染过滤器(Video Renderer和Default DirectSound Device)是必须的,转换过滤器根据实际情况可选。从菜单图表——插入过滤器„可以看到所有可用的过滤器。

上图隐含了DirctShow的“智能选择”功能,即如果你提供了任何两个逻辑关联的渲染器(如MPC – Avi Splitter和Video Renderer),DirectShow会自动查找之间的可用组件,形成一个正常的播放流程。读者如果想真正掌握DirectShow,应该学会手工添加、连接合适的过滤器而不仅仅依赖于“智能选择”功能。

由图可知,avi文件被播放前,经过分离器的分离,形成视频和音频两个部分,它们再选择合适的解码器,最后分别送到系统默认的渲染过滤器上进行渲染,影像就显现于屏幕上了。

点一下界面上绿色三角的播放键,则可以欣赏我们的影片了。

这一切出乎意料的简单,这使我们不由产生了这么一个想法——DirctShow的初级应用也是非常简单的,事实正是如此。在进价的第一部分,我们学习如何通过调用组件,完成一个媒体文件的播放。

四、进价之一——组件的调用

本篇将要接触到下表组件的调用,详细的信息可参考Msdn上的说明,这些基础工作留给有心的读者来完成吧:

IID

IBaseFilter

ICreateDevEnum

IEnumMoniker

IEnumPins

IFileSinkFilter

IFilterGraph

IfilterGraph2

IGraphBuilder

IMediaControl

IMediaEvent

IMediaEventEx

IMediaSeeking

IMoniker

CREATE CLSID

CLSID_VideoMixingRenderer9,…

CLSID_SystemDeviceEnum

CLSID_AudioInputDeviceCategory

CLSID_FilterGraph

REMARK

primary interface for DirectShow filters

creates an enumerator for a category of filters

enumerate the components of a moniker or to enumerate the monikers in a table of monikers

The filter graph manager uses this interface when it connects filters. Applications can use it to

retrieve pins on a filter

write media streams to a file

provides methods for building a filter graph

extends the IFilterGraph and IGraphBuilder interfaces, which contain methods for building

filter graphs

inherited from IfilterGraph

provides methods for controlling the flow of data through the filter graph

contains methods for retrieving event notifications and for overriding the Filter Graph

Manager's default handling of events

contains methods for seeking to a position within a stream, and for setting the playback rate.

contains methods that allow you to use a moniker object, which contains information that

uniquely identifies a COM object

IPropertyBag

IPersistStream

IPin

IPropertyBag

IStream

IStorage

IVMRFilterConfig9

IVMRMixerControl9

Provides an object with a property bag in which the object can save its properties persistently

provides methods for saving and loading objects that use a simple serial stream for their

storage needs

The filter graph manager uses this interface to connect pins and perform flushing operations

Provides an object with a property bag in which the object can save its properties persistently.

The IStream interface lets you read and write data to stream objects

supports the creation and management of structured storage objects

configure the VMR's operating mode and video rendering mechanisms

enables an application to manipulate the incoming video streams on the VMR-9

controls how the VMR-9 renders a video stream within a container window IVMRWindowlessControl9

编程可任选Microsoft或CodeGear公司的编译器。如果使用vs,可以在微软站点下载并安装最新的SDK,如果使用C++Builder,可通过互联网下载DirectX 9 SDK for Borland C++ Builder。本篇以vs2005作为默认编译器,示例选用参考资料1的DSRender、DSBuild和PIP9。

(一) DSRender

DSRender模拟了GraphEdit打开一个媒体文件播放的行为,代码很简单,仅仅用到了IGraphBuilder,IMediaControl,IMediaEvent三个接口提供的方法,播放窗口为系统创建,我们不能设置到自定义窗口。

代码里值得一提的是保存上图配置的函数SaveGraphFile,涉及了IStorage和IPersistStream两个接口,同时涉及了复合文件(Compound file

storage object)这一个概念。复合文件仿佛一个包含若干文件、子文件夹的文件夹,是若干普通文件、子复合文件的组合。通常使用的ReadFile、WriteFile函数以字节指针形式操纵单一文件,而复合文件里的文件却是以流对象(Stream Object)的形式存在,并强调了流对象的嵌套行为。从这种角度来看,前者象结构化编程的C,后者象面向对象编程的C++。可阅读Msdn里的相关文章更深入的理解复合文件。

(二) DSBuild

DsBuild模拟了手工添加过滤器的行为。稍微有点意思的是GetPin函数,它通过枚举每个过滤器的针脚,检查并返回所查询的输入或输出针脚。过滤器间的针脚相连非常的简单,通过IGraphBuilder接口的Connect方法将上游过滤器的输出针脚和下游过滤器的输入针脚相连即可。

五、进价之二——捕获和编辑影像

李逵的三板斧现在也该抡出最后一板了,倒不是黔驴已经技穷(汗一下„,是吗,有人这样说吗?),笔者相信这已经足够了,经过努力阅读参考资料的读者已经找到了前进的方向,本篇的内容虽然不多,却点到了DirectShow很多基础的知识,如果不把它们从薄读到厚,那么本文还有什么意义呢?

这部分的内容可以细读参考资料1的第4—9章,料想考试时在试卷上写,“本题请老师参考课本第xxx页完成”必定落个红灯的下场,故笔者打算稍微分析一下第9章所介绍的杀手锏——用Video Mixing Renderer(VMR)来生成影像的画中画程序PIP9。

即使包括头文件,Pip9的总代码量也不过1500行,提示和空格真正体现了本站资源《华为编程规范和范例(PDF)》里对注释不少于20%的要求,如果使用可视化控件编程,代码量大约又可减少1/3,兼之很多接口在上两个程序中已经接触过,因此本节的重点放在了画中画的主线——VMR9的操作上。首先我们从整体上把握VMR。

从上图看VMR过滤器类似于可以完成具体功能的集成电路,而不再是一个晶体管。它用于管理多重流媒体的渲染,在VMR9版本下,可以管理多达16个的输入管脚。

需要重点查看的函数为:

1.BlendVideo : ConfigureMultiFileVMR9 -> AddGraphToRot

2.ConfigureMultiFileVMR9 : InitializeWindowlessVMR -> RenderFileToVMR9

上述6个函数完成了画中画的播放效果,涉及的VMR接口为IVMRFilterConfig9、IVMRMixerControl9和IVMRWindowlessControl9。在这里,我们终于可以随心所欲的实现自定义的播放窗口了。

欣赏着稍带回音效果的影像,现在的你是不是觉得暴风影音、快乐影音这些功能强大的播放器不再神秘了呢?

如果仔细观察,将发现在自定义窗口上播放的影像可以很轻易用HyperSnap截取下来,而在默认窗口播放的影像却无法用HyperSnap捕获。兴奋之余,你也许不再满足只拥有如此简单的功能了,你还想获得调整播放速度、进度的自由,播放更多类型的影像文件,那就让我们来修正代码吧。

六、示例代码的修正

代码的修正基于几种基本原因:编译器的改变、bug的改正以及功能上的完善。在本机上实践时,发现调整了头文件、库文件路径后,资料1提供的源代码仍不能通过编译,提示某些符号无法找到的错误,在cpp文件首部添加#pragma comment(lib, "")后解决。

不要指望只有千行有效代码的播放器拥有多强大的功能,所以读者需要阅读相关接口的其他方法,添加常用的控制功能。眼疾手快的读者可能还将大声叫嚷Pip9不支持asf或wmv格式,但SDK里已经提供了相应源代码,你还等什么呢。

七、结语

本文并非以介绍DirectShow的开发为目的,基本的想法是希望读者了解驱动编程被微软披上了多件外衣这一个事实,在前进的路途中,不要被这些美丽的衣裳所迷惑。现在的我们仍无法解释诸如过滤器究竟如何与驱动交互,如何完成自己的过滤器的问题,但从全局来说,这些问题是你以前根本没有意识到的,即问题的深度已经有了一定的提高。我们大可保持一段时间的迷茫,等到水到渠成的时候,你将有充分的理由相信自己不再是一个菜鸟。

本篇作为选学内容,参考完成时间为两星期。了解了COM重要性的读者,可再花费两个月时间来打下COM编程的基础。

本文标签: 过滤器文件播放组件编程