QuickTime Component实例(一)

引 言

  此处介绍的QuickTime Component在原理上和拙文《Mac OS的Component技术简介》中的描述没有冲突,只是作为一种特例,加上QuickTime本身的一些特性,组织形式上有些区别,这些区别不是本质的。本文引用的实例是Apple官方网站上的示例程序:Electric Image Component。它是用于处理Electric Image(文件扩展名为EIM)的一个典型的QuickTime的Component(插件)。
  这个例子有两大优点:
  一、内容全面,包括静态图像的导入导出插件Image Importer和Image Exporter,静态图像的编解码插件Image Codec,活动图像导入导出插件Movie Importer和Movie Exporter。
  二、提供的示例是跨平台的,通过在Windows下安装QuickTime SDK可以编译测试(一般使用VC 6.0或VC .NET),生成名为"ElectricImageWindows.qtx"的插件,供Windows下的基于QuickTime的应用程序使用。

第一节 Image Importer和Image Exporter

  在本例程中,Image Importer和与之密切配合的Image Decompressor分别对应EI_GraphicsImport和EI_ImageDecompressor两个文件夹的源程序。这两部分可以从整个插件组中剥离出来独立工作,它们完成对静态图像的读取和解码。和完整的插件包一样,在Windows下编译生成插件,在NT系统下复制到"%SystemRoot%/System32/QuickTime"子目录下,就能用QuickTime 6 for Windows播放程序(可在Apple的官方网站免费下载)[1]打开Electric Image静态图像文件。
  需要注意的是,一般Image Importer数据导入后,是经过Decompressor绘制到显示端,这不同于导出压缩数据是直接在Image Exporter里进行。而在Decompressor中是对一个缓冲进行操作。事实上Decompressor是Still Image Codec的一个特例,在本例中,只实现了DrawBand,它正完成了上述的解码绘制的工作。而BandCompress和BandDecompress两个request handler(一称selector)均没有实现,它们完成的是码流的压缩和解压,这样编解码工作就能方便其他Comoponent调用。
  压缩数据,一般从静止图像文件中读取,由QuickTime默认的程序导入。在Image Importer中一个重要的request就是GetDataOffsetAndSize,它的handler向调用者返回图像有效数据的起始偏移和大小。它从基组件的同名request handler中得到偏移和大小,这一般分别就是文件的起始偏移(0)和文件大小,然后根据图像格式需要进行修改。而这个request不仅供一般调用者使用,在Decompressor中也将以此处指定的起始偏移规定的范围为准进行操作。Image Importer中另一个重要的request是GetImageDescription,在这个request的handler中要完成对ImageDescription结构的生成,这在Decompressor解码中也是必须的。
  Decompressor解码过程主要在名为DrawBand的request handler中进行,需要注意的是,QuickTime并不一定将全部的压缩数据都导入内存,而是只先导入指定尺寸的数据。所以数据馈送必须不断调用API函数InvokeICMDataUPP完成,用户可以指定每次馈送数据的大小,但不能低于上述的指定尺寸。
  Image Exporter相对简单,在其中的名为DoStandaloneExport的request handler中完成主要的导出工作,包括编码,此处编码的输入和输出均完全是对已分配内存的操作。

第二节 Movie Importer和Movie Exporter

  Movie Importer完成非QuickTime标准MOV规格的电影(视频或音频)数据的导入和整理。对于QuickTime标准规格的影片事实上并不需要通过Importer的支持而能直接为QuickTime所播放。QuickTime规格以Movie-Track-Media-Sample为主要层次结构的电影控制结构为核心。标准规格文件保存了这个控制结构的数据并能直接被QuickTime解析,从而不需要Importer;非标准的自定义文件格式(包括其他组织定义的如AVI、WMV、MPG、RM等)需要Importer完成解析并相应地建立上述控制结构而后递交QuickTime方能完成导入。这样对非标准文件格式,Exporter则调用QuickTime API获取源端播放音视频数据。由于它们已在QuickTime内部播放,因而显然也是以这种控制结构组织的。随后Exporter将数据根据格式需要输出,从而可形成一个非标准的电影文件,从而完成Importer的逆向工作。
  当然如选择以MAC为主要平台,更一般的需要是制作标准规格的MOV文件。而区别于各标准MOV格式的是其内部编码方式,这也就是我们制作Component的意义所在。根据上述讨论,很显然,需要在Exporter端承担更多的Importer端的工作即建立控制结构并落实到最终的输出中,这些也主要通过调用QuickTime API得以实现。
  值得注意的是对于Exporter端的编码工作本身,QuickTime支持了编码器编码(调用Codec组件)、直接编码(Standalone方式)和可能的转码(Transcode方式,对于特定支持的导入导出组合,采用不经过解码的直接变换),可满足各种需要。本处的Electric Image Component无论是Graphics Exporter还是Movie Exporter均采用Standalone方式,所以编码工作在Exporter代码中可见。

第三节 Media Handler

  Media Handler是一种重要的QuickTime Component,它主要涉及媒体播放时的具体操作。从前面的叙述可知,Importer并不对播放作任何处理,只是做数据的收集,所以播放过程就交给各种Media对应的Media Handler进行处理。QuickTime针对大多数媒体定义了相应的Media Handler,所以用户很少会有必要编写一个Media Handler,即所谓的Derived Media Handler。例外的情况主要有:
  一、要面对一种新型的Media类型,当然,这是一种数据类型,但是必须是基于时间的(Time-based),这其实是QuickTime的处理对象的抽象定义。例如:Flash、某些有帧间相互关系的媒体类型等。
  二、对一些特殊的传输介质的不同处理。例如某些网络协议,存储结构等,但在这种情况下首先应该改写另一种QuickTime的Component——Data Handler。
  三、对一个已有的默认处理不满意。例如笔者遇到的需要对数据文件进行截取的情况。
  Media Handler在QuickTime的架构中是出于Toolbox之下,Data Handler之上。他供ToolBox调用以实现播放,而数据访问则通过Data Handler。
  编写Derived Media Handler一般在Open的处理中要设置Base Media Handler,一般它的Subtype设为BaseMediaType。笔者发现对于派生Video类的Media Handler,如果选择VideoMediaType为Subtype的Handler作Base Handler,会绕开本身的处理,其原因还不明。
  Media Handler中最主要的request是Idle,它在QuickTime认为适当的时候就会调用。几乎所有的操作都是在响应这个request的函数中完成。对于处理Video的Media Handler而言,最一般的情况就是在这个函数中解码并显示(一般调用QuickDraw)规定时间位置的帧。
  在Media Handler中可以得到大量的播放参数,所以有极大的灵活度。如播放环境,影片数据等都是透明的,甚至可以获取合适的参数,绕开Data Handler,直接用C标准函数对影片文件进行操作。

  (完)

注释:
[1] 某些常用的通用播放器如MPC由于并不使用Windows下的QuickTime架构,而只是采用了自身的QuickTime插件播放MOV格式影片,所以它们与此处的所有讨论完全无关。

posted @ 2005-05-29 15:15  quanben  阅读(213)  评论(0编辑  收藏  举报