DirectShow是一个 windows平台上的流媒体框架,原名为ActiveMovie,现在一部分API仍保留了"AM"的前缀,比如AM_MEDIA_TYPE和 IAMVideoAccelerator。
direcshow架构见下图,相关头文件和库dshow.h、quartz.dll
接口:
IFilterGraph 过滤通道接口
IFilterGraph2 增强的IFilterGraph
IGraphBuilder 最为重用的COM接口,用于手动或者自动构造过滤通道Filter Graph Manager
IMediaControl 用来控制流媒体,例如流的启动和停止暂停等,播放控制接口
IMediaEvent 播放事件接口 ,该接口在Filter Graph发生一些事件时用来创建事件的标志信息并传送给应用程序
IMediaEventEx 扩展播放事件接口
IMediaPosition 播放的位置和速度控制接口(控制播放位置只能为设置时间控制方式)
IMediaSeeking 另一个播放的位置和播放速度控制接口,在位置选择方面功能较强.设置播放格式,多种控制播放方式.常用的有:
(1)TIME_FORMAT_MEDIA_TIME单位100纳秒。(2)TIME_FORMAT_FRAME按帧播放
IBasicAudio 声音控制接口
IBasicVideo 图像控制接口(波特率,宽度,长度等信息)
IVideoWindow 显示窗口控制接口 (有关播放窗口的一切控制,包括caption显示,窗口位置控制等)
ISampleGrabber 捕获图象接口(可用于抓图控制)
IVideoFrameStep 控制单帧播放的接口
播放流程:
大体说来,一般使用DirectShow接口编程无非3个步骤,初始化接口,利用接口中的控制函数使用控制操作,最后释放接口。
流程图中关键函数的作用如下所示。
CoInitialize() :初始化COM运行环境。
CoCreateInstance(…,pGraph) :用指定的类标识符创建一个Com对象。在该播放器中类标识符为“CLSID_FilterGraph”,用于创建IGraphBuilder。
pGraph->QueryInterface(…,pControl) :通过QueryInterface()查询某个组件是否支持某个特定的接口。在这里查询IMediaControl接口。
pGraph->QueryInterface(…,pEvent) :同上。在这里查询IMediaEvent接口。
pGraph->RenderFile("xxx.mkv"):为指定的文件智能的构建一个Filter Graph,支持的输入是Unicode字符串
pControl->Run() :开始运行Filter Graph中的所有Filter。
pEvent->WaitForCompletion() :等待Filter Graph处理完所有数据。
CoUninitialize():释放CoInitialize()初始化的COM运行环境。
该流程图中包含如下变量:
IGraphBuilder *pGraph:继承自IFilterGraph,用于构建Filter Graph。
IMediaControl *pControl:提供和播放控制有关的一些接口。
IMediaEvent *pEvent:用来处理Filter Graph发出的事件。
IBaseFilter *pF_source:源Filter。
IFileSourceFilter* pFileSource:源Filter的暴露的接口,用于设置输入文件的路径。
IBaseFilter *pF_demuxer:解复用Filter。
IBaseFilter *pF_decoder:解码Filter。
IBaseFilter *pF_render:渲染Filter。
IPin *pOut:输出Pin。
IPin *pIn:输入Pin。
IPin **pPin:内部变量Pin。
该流程图大体上可以分成以下步骤:
(1) 初始化DirectShow
包括以下几个步骤:
a) CoInitialize():初始化COM运行环境。
b) CoCreateInstance(…,pGraph):用指定的类标识符创建一个Com对象。在这里创建IGraphBuilder。
c) pGraph->QueryInterface(…,pControl):通过QueryInterface()查询某个组件是否支持某个特定的接口。在这里查询IMediaControl接口。
d) pGraph->QueryInterface(…,pEvent):同上。在这里查询IMediaEvent接口。
(2) 添加Source Filter
包括以下几个步骤:
a) CoCreateInstance(…,pF_source):创建Source Filter。
b) pGraph->AddFilter(pF_source,…):将Source Filter加入Filter Graph。
c) pF_source->QueryInterface(…,pFileSource):查找Source Filter的IFileSourceFilter接口。
d) pFileSource->Load(L"xxx.mpg",pF_source):调用IFileSourceFilter的Load()方法加载视频文件。
(3) 添加Demuxer Filter
包括以下几个步骤:
a) CoCreateInstance(…,pF_demuxer):创建Demuxer Filter。
b) pGraph->AddFilter(pF_demuxer,…):将Demuxer Filter加入Filter Graph。
(4) 添加Decoder Filter
包括以下几个步骤:
a) CoCreateInstance(…,pF_decoder):创建Decoder Filter。
b) pGraph->AddFilter(pF_decoder,…):将Decoder Filter加入Filter Graph。
(5) 添加Render Filter
包括以下几个步骤:
a) CoCreateInstance(…,pF_render):创建Render Filter。
b) pGraph->AddFilter(pF_render,…):将Render Filter加入Filter Graph。
(6) 连接Source Filter和Demuxer Filter
调用了一个函数connect_filters()用于连接2个Filter。
connect_filters()的执行步骤如下:
a) 调用get_unconnected_pin()从源Filter中选择一个没有链接的输出Pin。
b) 调用get_unconnected_pin()从目的Filter中选择一个没有链接的输入Pin。
c) 连接这两个Pin
get_unconnected_pin()的执行步骤如下:
a) 枚举Filter上的Pin。
b) 遍历这些Pin,查找符合输出方向(通过IPin的QueryDirection()方法),而且没有在使用的Pin(通过IPin的ConnectedTo()方法)。
(7) 连接Demuxer Filter和Decoder Filter
过程同上。
(8) 连接Decoder Filter和Render Filter
过程同上。
(9) 开始播放
包括以下步骤:
pControl->Run():开始运行Filter Graph中的所有Filter。
pEvent->WaitForCompletion():等待Filter Graph处理完所有数据。
上述步骤可以理解为在GraphEdit软件中分别按照步骤添加以下控件。其中(1)、(2)、(3)、(4)为先添加的4个Filter,(5)、(6)、(7)为Filter之间的连接线。
注意Render()方法和RenderFile()方法区别
RenderFile()是指定一个文件路径后,自动构建整个Filter Graph,相对来说更加简单些;而Render()方法则是首先要创建一个Source Filter之后,才可以自动构建整个Filter Graph。
可以通过修改源文件首部的宏定义ADD_MANUAL来设定是否手动添加Filter,如下所示。
//'1':Add filters manually
//'0':Add filters automatically
#define ADD_MANUAL 1
接口关系
可以看出从上到下他们之间顺序的排列如下所示:
ICreateDevEnum-->IEnumMoniker-->IMoniker-->IBaseFilter-->IEnumPins-->IPin-->IEnumMediaTypes-->AM_MEDIA_TYPE
该流程图中涉及到以下接口:
ICreateDevEnum *pSysDevEnum:设备列举接口。
IEnumMoniker *pEnumCat:Moniker(别名)枚举接口。
IMoniker *pMoniker:Moniker(别名)接口。
IPropertyBag *pPropBag:存储属性值的接口。
IBaseFilter *pFilter:Filter接口。
IEnumPins * pinEnum:Filter枚举接口。
IPin * pin: Pin接口。
PIN_INFO pinInfo:存储Pin的信息的结构体。
IEnumMediaTypes *mtEnum:MediaType枚举接口。
AM_MEDIA_TYPE *mt:描述媒体类型的结构体。
【初始化】
CoInitialize():初始化COM运行环境。
CoCreateInstance(…,pSysDevEnum):用指定的类标识符创建一个Com对象。在该示例中类标识符为“IID_ICreateDevEnum”,用于创建ICreateDevEnum。
【Filter的枚举】
pSysDevEnum->CreateClassEnumerator(…,pEnumCat):通过ICreateDevEnum查询IEnumMoniker枚举接口,枚举指定类型目录下的设备Moniker(别名)。
pEnumCat->Next(…,pMoniker):通过IEnumMoniker查询下一个IMoniker接口。
pMoniker->BindToStorage(…,pPropBag):通过IMoniker查询IPropertyBag接口(用于获取Filter信息)。
pPropBag->Read("FriendlyName"):通过IPropertyBag获取“FriendlyName”属性的值。
pMoniker->BindToObject(…,pFilter):通过IMoniker查询IBaseFilter接口(用于获取Filter,注意和BindToStorage()区别)。
【Pin的枚举】
pFilter->EnumPins(pinEnum):通过IBaseFilter查询IEnumPins枚举接口。
pinEnum->Next(…,pin):通过IEnumPins查询下一个IPin接口。
pin->QueryPinInfo(PinInfo):通过IPin获取Pin的信息。
【MediaType的枚举】
pin->EnumMediaTypes(&mtEnum):通过IPin查询IEnumMediaTypes枚举接口。
mtEnum->Next(…, &mt):通过IEnumMediaTypes查询下一个AM_MEDIA_TYPE。
GuidToString(mt->majortype):把AM_MEDIA_TYPE的GUID转换成字符串(方便输出)。
【释放】
CoUninitialize():释放CoInitialize()初始化的COM运行环境。