第一部分:背景知识
DirectShow是微软公司提供的一套在Windows平台上进行流媒体处理的开发包,与DirectX开发包一起发布。它经过DirectX 6.0中的DirectX Media发展而来,集成了DirectX家族中的其他成员(DirectDraw、DirectSound等),可以说是一位“集大成者”。            
DirectShow能做些什么? DirectShow为多媒体流的捕捉和回放提供了强有力的支持。运用DirectShow,可以很方便地从支持WDM驱动模型的采集卡上捕获数据,并且进行相应的后期处理乃至存储到文件中。它广泛地支持各种媒体格式,包括Asf、Mpeg、Avi、Dv、Mp3、Wave等等,使得多媒体数据的回放变得轻而易举。另外,DirectShow直接支持DVD的播放,视频的非线性编辑,以及与数字摄像机的数据交换。更值得一提的是,DirectShow提供的是一种开放式的开发环境,每个功能模块都采取COM组件方式,称为Filter,开发者也可以开发自己的功能Filter来扩展DirectShow的应用。
按照功能来划分,Filter分为3类:Source Filter, Transform Filter, Rendering Filter。前者负责获取数据,数据源可以是文件、数字摄像机等,然后将数据往下传输;中间者负责数据的格式转换,比如数据流的分离与合成、编码解码等,然后把数据继续往下传输;后者负责数据的去向——给声卡、显卡进行播放或者输出到文件存储。
图片传不上,WHO KNOWS HOW TO OPERATE?
上图是DSHOW播放视频文件典型的Filter Graph。对应流程是:
1 先从一个文件中读取AVI数据,形成字节流。
2 检查AVI数据流的头格式,然后通过AVI分割Filter将视频流和音频流分开。
3 解码视频流,根据压缩格式的不同,选取不同的Decoder Filter。
4 通过Render Filter重画视频图像。
5 将音频流送到声卡进行播放,一般采用缺省的DirectSound Device Filter。
每一个Filter都与其他的一个或两个Filter相连接。两个Filter相连接的连接点也是COM对象,称为Pin。Filter通过Pin将数据从一个Filter传递到另一个Filter中,从而可以使数据在由Filter组成的Filter Graph中流动。
创建Filter使用一个普通的Win32 DLL项目。Filter文件的扩展名为.ax,但也可以是.dll。Filter是COM组件,所以在使用前一定要注册。Filter的注册程序为regsvr32.exe。如果带上命令行参数/u,表示注销;如果带上是/s,表示不弹出任何注册/注销成功与否的提示对话框。
关于前景:Media Foundation是DSHOW的最终替代品,但不是马上,而会在未来2年内逐步替代。Media Foundation的第一个版本会在Windows Vista上发布,目前功能主要集中在内容保护上面。Media Foundation比DSHOW提供更灵活、更安全的架构。DSHOW在应用中呈现出来的缺点,多多少少都会在 Media Foundation中得到解决。不过,Media Foundation需要有一个发展的过程,在未来2-3年内,可能会出现 Media Foundation和DSHOW并存的局面,DSHOW很强大,Media Foundation暂时也无法完全替代DSHOW。无论怎么样,以前开发的DSHOW程序仍然能够运行,微软只是建议新开发的多媒体程序采用Media Foundation架构。
第二部分:工作流程
       工作目标:在用户级计算机上用Windows Media Player可以播放MXF格式的JPEG2000视频文件。
Ⅰ    了解MXF文件结构:
前段时间做的AVI Codec封装的工作涉及到读写AVI文件,这对分析MXF文件结构有帮助。
Ⅱ    整理思路:
①如果只做一个Source Filter,那就要求在Source Filter直接完成对JPEG2000的解码,Source Filter的Output Pin送出去的数据就是解码后的无压缩图象数据。
       ②如果要写Source Filter和Transform Filter,也是可以的。这时Source Filter只需读取文件中的内容,然后把数据推给Transform Filter。Transform Filter作JPEG2000的解码工作。
     综合起来,虽然第一种方案的扩展性差一点,但实现起来简单一点。选择的一种方案。
Ⅲ    寻找可重用的代码:
       陆其明的《DirectShow实务精选》附录D里有开发可以播放自定义格式视频文件的Source Filter的例子——QQ Source Filter。
Ⅳ    分析QQ Source Filter代码:
需要改动的函数主要是SetFileSource()、FillBuffer()、DllRegisterServer()。前者负责确定要播放的源文件的路径,设置必要参数;中间者在播放过程中读取每帧数据,进行解码后交给Pin;后者识别不同后缀名的文件,将信息写入注册表。此外ChangeStart()也需要做一定改动,它负责进度条拖动后对当前帧的数据定位。至于Pin如何将数据输出给下一级Filter,Filter连接时如何校对媒体类型,状态的转换等等都是可以代码重用的。
Ⅴ    写代码和调试:
       先写了一个小程序对MXF进行遍历和感兴趣参数的提取。有了这个基础后改动以上四个函数的代码量其实并不大,调试花了很多时间,因为无法单步调试和跟踪参数值,只能打印文件来调试。修正了进度条拖动无法有效改变当前帧、播放完成不能再次播放、从支持附加完整头的伪MXF到不加头的真MXF、仅支持小MXF文件等BUG。
Ⅵ    制作发行版。
       就是拷贝解码的dll到system32目录下,然后注册ax。
第三部分:遇到的问题及如何解决
Ⅰ    Q:开发环境配置不成功。
A:尽量使用现有的例子工程,基本上都是库的连接问题,先编译SDK的基类库,其余步骤见陆其明著作。
Ⅱ    Q:读取正确的播放参数后填充VIDEOINFOHEADER信息头仍然不成功。
A:可以参考QQ Source Filter的例子在MXF前加上一个信息头,对应位置的参数按照实际修改,做一个伪MXF,绕过此问题,最后再考虑去除这个头。
Ⅲ    Q:VIDEOINFOHEADER结构里AvgTimePerFrame如何计算?
A:它是每帧持续时间的参数,和帧率相关,但是它以100纳秒为单位,注意换算。
Ⅳ    Q:BITMAPINFOHEADER. tagBITMAPINFOHEADER. biCompression如何设置?
A:它是OutputPin上的MediaType参数,QQ文件是YUY2的,MXF是UYVY。
Ⅴ    Q:编译好的Source Filter如何使用?
A:用regsvr32手动注册或者IDE编译自动注册后,如果工作正常,调试工具Graph Edit里拖入MXF文件后就会自动建立起Filter Graph,在此工具里能播放的话,用WMP打开MXF也就能自动调用Source Filter进行播放了。
Ⅵ    Q:因为开发的是dll类型文件,无法单步调试和跟踪,如何调试?
A:假设已经进入到Graph Edit可以识别MXF文件并自动建立起Filter Graph的阶段了,这时的调试方法主要是靠源代码里写上文件打印输出信息,据此判断是哪里变量值不对或者哪个函数没有按应有的调用次序出现,虽然比较繁琐但这是唯一的途径。
如果Graph Edit无法建立起Filter Graph,那说明是DllRegisterServer()函数有待校正。
Ⅶ    Q:为什么WMP顺序播放完成后无法重新播放?
A:注意Fill Buffer()中对帧序号达到最大值后的操作,置零与返回值。
Ⅷ    Q:为什么进度条拖动后没有刷新当前帧,仍旧从拖动前的帧序号往下播放?
A:注意FillBuffer()和ChangeStart()两个函数的通信机制。
Ⅸ    Q:只能播放文件较小的几个MXF,容量大的放不了。
A:仔细查看两种文件ff4fff51前16字节的JPEG2000 data chunk的标识,发现有区别,遍历并记录各帧的地址偏移量时考虑两种情况即可,事实上是逻辑或。
Ⅹ    Q:为什么制作的安装包在客户级别(未装DirectX SDK)计算机上进行.ax注册时,会提示Load Library出错?
A:制作的安装包需要包含的.dll和.ax应该是Release版本编译出的,而不是Debug版本,同时要包括基类编译出的.lib,推荐使用CreateInstall Free制作安装包。