2.1 太祖长拳和岳家散手:DirectShow和Media Foundation
随着软件开发的日渐复杂,框架逐渐成为常用的名词,通常它指实现了某种类型规范的软件,利于他人遵循使用。框架通常以组件化、规范化的形态出现,本身既可作为完整的软件,又可借此开发更加复杂的产品,供人使用。音视频领域以其专业性和复杂性,在很早就产生了多种框架,通常包含多达几百种的组件,开发者基于框架可以构造出无数不同类型的软件,帮助整个音视频开发组件化、标准化,令其容易传播,大大促进行业的进步。
DirectShow是最早的也是非常著名的音视频框架之一,简称DShow,由微软公司开发。它的早期名称为ActiveMovie,当时被视为对苹果公司QuickTime软件做出的回应。直到1998年,它被包含在DirectMedia SDK之内发布,并改名为DirectShow,之后随着Windows系统流行于世。
据统计,曾真正服务或影响十亿级用户的音视频框架,只有DirectShow、Media Foundation、Helix、FFMpeg、Android Media、AVFoundation等寥寥几种。DirectShow框架的流行,除Windows助力之外,其优异的模块化设计功不可没,即使以20年后的眼光评估,它也可算作设计精巧,容易上手,便于添加和分享新的功能组件的框架,国内大量的音视频开发启蒙都来自DirectShow。
如果用武侠小说中的武功来比喻,DirectShow因其招式简洁、威力巨大、上限极高,可31以被称作太祖长拳。
DirectShow的设计理念是,开发者创建一个Graph(也可称Pipeline),将所有用到的组件(称作Filter)加入Graph当中,当应用运行时,Graph找到了注册的Filter并为之连接,Filter之间形成一个DAG(有向无环图,见图2-1),开始播放环节。Filter常用于对音视频流的某一步的处理,包括文件读取、渲染等。
图2-1 有向无环图DAG(图片来自Wikipedia)
2.1.1 GraphEdit,DirectShow架构和常见应用的流程
当开发者接触DirectShow时,首先会关注到它的一个工具GraphEdit,这是一个用于建立和测试Graph(图)的可视化工具。在GraphEdit中,可以加入所需的Filter(过滤器),将Input Pin(输入探针)和Output Pin(输出探针)连接起来,如果连接正常,就可以执行播放操作。图2-2描述了播放一个MP3文件时,在GraphEdit中看到的Graph结构,包括读取MP3文件、解封装、解码、默认声卡设备以及它们之间的连接。
图2-2 MP3的播放(图片来自Wikipedia)
DirectShow基于微软的COM技术(Component Object Model),以C++开发,但用户不必自己开发COM对象,只需要使用或继承框架提供的组件即可。默认提供的组件支持ASF、MPEG、AVI、MP3、WAV等多种文件格式或编解码格式。
总而言之,DirectShow提供的Filter类型包括文件读取、视频采摄(Capture)、解码(Decompressor)、渲染(Renderer)等。仍以播放MP3文件为例,其中文件读取Filter负责从硬盘读取文件,并作为bitstream送到后续Filter处理,随后MPEG-I Stream Splitter是一个支持MPEG-I Stream文件格式的Splitter类型Filter,它负责解析MP3的文件格式,将其中的音频以Frame的形式分离出来并交给后续的解码Filter,MPEG Layer-3解码Filter负责将输入的(压缩过的)音频Frame解码为可以直接播放的Raw Data,最后的Filter是在音频设备(通常为声卡)之上的封装,可以将Raw Data直接播放到音箱等设备。在Filter上,定义了输入或输出的Pin,用于连接彼此,Filter可以仅有输入Pin或输出Pin,也可以二者兼具。
这里另外给出一个DirectShow播放AVI文件的范例(见图2-3),从硬盘上读出文件后,数据将被送到名为AVI Splitter的Filter中,这个Filter负责解析AVI文件格式,并将视频和音频流分开,分别送到不同的Filter,其中视频由AVI Decompressor Filter解码,音频由MPEG Layer-3Decoder Filter解码,视频流解码得到的Raw Data在经过一次转换后,被送到Video Renderer Filter(内部封装了显卡驱动),音频流解码的Raw Data被送到Default DirectSound Device(内部调用了声卡)。
图2-3 GraphEdit截图:AVI的播放Graph(图片来自Wikipedia)
可以看到,每个Filter组件负责对数据的特定处理,音视频的采集设备(如摄像机、摄像头、录音笔等)获取到Raw Data后,需要有编码压缩的环节获得编码后的音视频数据,编码后的音视频数据需要封装的步骤才可以形成文件或Stream。在播放端,文件或Stream需要解封装的步骤才能从文件中解析出音视频流,再经过解码的步骤,发送到硬件设备中进行显示和播放。由于这些处理数据的步骤在不同的应用中非常相似,所以被抽象成不同类型的Filter,以下分类与官方分类有所不同,但或许更容易与Gstreamer等其他框架对比。
(1)Src类型
不论任何音视频应用,都需要有输入的音视频数据,既可以是从网络来的视频流,也可以是硬盘上存储的文件,Src类型的Filter将负责将其读入并按顺序传给后续的Filter,因为数据源已知,这一类型的Filter只有输出Pin。
(2)Splitter或Demultiplexer类型
这一步骤又被称作Demux(即解复用),组件也被称作Demuxer,主要用于文件格式的解析,前面我们曾介绍了多种不同的文件格式,音视频数据被按照文件格式的定义存储,这一类型的Filter将解析并将不同的视频或音频数据分离开来,因此这一类型的Filter通常会有一个输入Pin和多个输出Pin,除音视频可能存在多路以外,字幕或EPG信息也可能需要被解析分离。Mux或Muxer类型(即复用的Filter)可以看作Splitter的反面,常有多个输入Pin和一个输出Pin,将音视频及字幕等输入封装成文件或流媒体的格式。
(3)Decoder或Decompressor类型
针对某一编码格式的音视频数据,这种类型的Filter将负责解码(解压缩)成为Raw Data,即可以直接被送到显卡或声卡的数据,同样,也存在Encoder或Compressor类型的Filter,负责将Raw Data压缩成编码后的格式。
此外,还存有Writer类型的Filter,负责将文件或视频流写入指定的存储设备,Convertor类型的Filter,负责将图像或声音格式进行转换,Mixer类型的Filter,可将不同的显示层叠加等。
2.1.2 应用和组件开发
基于DirectShow的应用(见图2-4)需要管理整个Graph,但并不必须管理所有的Filter之间的连接,例如应用可以设置Graph的状态为“PLAY”或“STOP”,其效果是将Graph中的所有Filter的状态设为“PLAY”或“STOP”,当然,通过访问Filter上的COM接口,应用也可以对Filter作精细的控制。通常而言,应用需要负责以下方面。
图2-4 基于DShow开发应用(图片来自MSDN)
·创建Filter Graph Manager(过滤器图管理器)。
·通过Filter Graph Manager建立一个Filter Graph,放入所需的Filter到应用之中。
·应用通过Filter Graph Manager去控制Filter Graph和数据流动,并响应Filter运行过程中产生的事件。
以写一个播放程序为例,核心代码如下。
为在Windows系统下实现一个完整的播放器,通常需要考虑得更多一些,例如实现各个功能,如OpenFile(打开文件)、Play(播放)、Pause(暂停)、Stop(停止)、Seek(跳播)以及管理视频输出的目标设备的接口等,在MSDN上有详细的示范代码。
DirectShow的成功依赖于Graph和Filter的设计概念,对专门的多媒体开发者而言,写出自己的Filter并让其被更多人与程序使用,是扩大影响的理想方式。默认情况下,DirectShow包含了一组基础的Filter类。对于新开发的Filter,应继承某一个基础类(如CTransformFilter)。开发者需要关注以下问题。
·实现Filter所需的各项接口。
·定义Pin上的MediaType和内存分配,实现相应接口。
·定义线程模型,区分在不同线程内的工作以及它们的同步问题。
·实现不同状态变化下对数据的处理,如Pause、Delivery、Receive、Flush等。
·调整和控制数据流动的速率。
·注册和提供新的文件或编码类型。
·支持Filter的属性查询。
并非所有Filter的各个Pin之间都能随意连接,并在Graph中发挥作用,实际上,只有Media type兼容的Filter才可以相互连接,Media type用来描述数据的类型,当Graph试着连接两个Pin时,它们需要协商是用Push还是Pull方式传递数据、Media type是否一致、谁来分配缓冲区等。这些也是开发Filter时所需关注的内容。
由于大部分Filter的功能是处理和传送音视频数据,根据Filter的类型,Push类型的Source Filter会建立一个线程,持续将数据填充到Sample里面(Sample是一片数据,可能是一个音视频帧或是一个数据包),送到下游,Pull类型的Filter则会等待下游来请求下一个Sample,下游的Filter会创建线程来驱动整个流程的运转。
更完整详细的DirectShow文档可参考MSDN。
2.1.3 Media Foundation
在Windows Vista之后,微软推出了新的Media Foundation框架(见图2-5)用于多媒体应用的开发,意图替代DirectShow,在每个版本的Windows中都增加了大量的新组件、新功能,如DRM支持,H.264、H.265编码支持等。Media Foundation既支持与DirectShow相似的以Media Session为主的Pipeline模型(可类比于DirectShow中的Graph),又支持另一种简单的编程模型,仅含有Source Reader、Sink Writer以及Transcode API三部分,以简化使用难度。
图2-5 Media Foundation架构(图片来自MSDN)
在Media Foundation中,微软定义了三个层次,即Control layer(控制层)、Core layer(核心层)与Platform layer(平台层)。其中应用通过Control layer来控制,框架提供的功能大部分暴露在Core layer。Platform layer则提供一些用于Pipeline的核心功能,如异步调用、工作队列等,少数程序可能需要直接访问。
如同其他现代框架,Media Foundation提供命令行工具MFTrace和可视化工具[如TopoEdit(见图2-6)等]给开发者使用,更多详细内容仍可参考MSDN中的介绍。
图2-6 TopoEdit界面(图片来自MSDN)
Media Foundation名声很大,在软件数量、开发环境等方面也属流行,却未吸引太多聚光灯的关注。若按武学招式而论,很像历史地位重要,且军中人人习练,却很少成为主角技能的岳家散手。