基于DirectShow的WinCE多媒体编程 by斜风细雨QQ:253786989 2012-02-17
(1) 简介
DirectShow是由微软开发的处理多媒体文件的应用程序接口(API),基于COM(组件对象模型)框架。
下图截自MSDN,展示了应用程序与DirectShow以及一些软硬件组件之间的关系。
如图所示,DirectShow主要由Filters和Filter Graph Manager组成。其中Filters有好多种,Source Filters叫做源过滤器,主要作用是从多媒体文件(来自本地或者Internet等)引入源数据流。Transform Filters叫做传输过滤器,作用是对源数据流进行处理,如编解码(包括软解码和硬解码)、转换格式、解压缩等等。Rendering Filters是渲染过滤器,它会在最后将处理完的数据流传送给相关硬件播放,如通过DirectDraw控制显卡显示图形,通过DirectSound控制声卡发出声音等等。除此之外,DirectShow还包含其它很多过滤器,以后再详细了解。
图中Source Filters、Transform Filters、Rendering Filters组合在一起,称为过滤器图。DirectShow就是由这些完成各自分工的Filters前后串联在一起来完成工作。Filter Graph Manager称为“过滤器图管理对象”,主要作用是协调不同的Filters,建立参考时钟供Filters使用,采用队列机制将DirectShow事件传递给应用程序等等。
图中每个过滤器,都包含有称为Pin(引脚)的私有对象,它们派生自IPin。Pin分为两类:输入Pin和输出Pin。如多媒体数据流从输入Pin进入Source Filters,然后从Source Filters的输出Pin与Transform Filters的输入Pin连接。多媒体数据流经过Source Filters的处理之后,通过Pin连接进入Transform Filters。
(2) DirectShow接口
WM/WinCE平台下的DirectShow有几十个接口,下面是常用接口及接口中的常用方法。
a) IGraphBuilder:此接口用来创建filter graph。
其中常用方法RenderFile,用来渲染指定类型的文件。
b) IMediaControl:控制Filter Graph中的多媒体数据流。
其中常用方法如Run开始播放,Pause暂停播放,Stop停止播放。
c) IMediaEvent,IMediaEventEx:处理Filter Graph产生的事件。应用程序通过此接口获得播放过程中发生的事件,如EC_COMPLETE(播放完毕)等等。主要方法如SetNotifyWindow指定处理事件的窗口,GetEvent获取事件,FreeEventParams释放与事件参数相关联的资源。
d) IVideoWindow:用于设置多媒体播放窗口的属性。主要方法如put_Owner指定视频播放窗口的父窗口,put_FullScreenMode指定全屏播放模式,SetWindowPosition指定视频窗口的位置,put_Visible显示或隐藏视频窗口,get_Visible获取当前视频窗口是否可视,put_WindowStyle设置视频窗口风格属性,put_MessageDrain指定一个窗口,从视频窗口接收鼠标和键盘消息。
e) IMediaSeeking:对多媒体数据流的播放位置等属性进行控制。主要方法如SetPositions设置当前播放位置和终止播放位置,GetCurrentPositions获得当前播放位置,SetRate设置播放速率,GetRate获取播放速率等,GetDuration以纳秒为单位获取多媒体文件的总时间长度。
f) IBasicAudio:控制音频的音量和平衡。主要方法如put_Volume设置音量大小,get_Volume获取音量大小,put_Balance设置声道平衡,get_Balance获取声道平衡等。
g) IBasicVideo:控制视频属性。主要方法如get_BitRate获取视频流的比特率,GetVideoSize获取视频尺寸。
(3) WinCE组件
同样的程序,同样的代码。在有些平台上可以正常使用,播放音视频文件,采集摄像头图像等等。但有的平台就不可以。原因就是WinCE系统在定制的时候,每个厂家所添加的Media相关组件不同,所以不同厂家的WinCE平台所具备的多媒体能力也不同。
下面是WinCE7下的Media相关组件:
a) 音频编解码器和渲染器
比如想播放MP3格式的音乐,就必须勾选上MP3 Codec。
b) 视频编解码器和渲染器
c) DirectShow
这里面包括DirectShow的核心组件和filters,DirectShow以filters完成核心功能。如果相关DirectShow组件没有添加的话,那使用该系统所导出的SDK,很可能我们的DirectShow应用程序编译都通不过。即使编译通过,也肯定不能正常工作。
除此之外,还有Media Formats、Media Library、Media Renderer、Windows Media Player等相关组件。有关这些内容可以看Wince7的帮助文档。下面是使用DirectShow需要添加的相关组件的简单介绍:
(4) 枚举系统中注册的filters
利用DirectShow的IEnumRegFilters接口枚举系统中注册的filters,下面是实现代码:
HRESULT hr = 0; IFilterMapper *pMapper = NULL; IEnumRegFilters *pEnum = NULL; REGFILTER *pRegFilter = NULL; ULONG cFetched = 0; // 初始化COM环境 CoInitialize(NULL); // 获取IFilterMapper接口 hr = CoCreateInstance(CLSID_FilterMapper, NULL, CLSCTX_INPROC, IID_IFilterMapper, (void **)&pMapper); if (FAILED(hr)) { MessageBox(TEXT("得到IFilterMapper接口失败!")); return; } // 获取IEnumRegFilters接口 hr = pMapper->EnumMatchingFilters(&pEnum, 0, FALSE, GUID_NULL, GUID_NULL, FALSE, FALSE, GUID_NULL, GUID_NULL); if (FAILED(hr)) { MessageBox(TEXT("得到IEnumRegFilters接口失败!")); return; } // 枚举系统中注册的filters CString strFilters, strTemp; while (pEnum->Next(1, &pRegFilter, &cFetched) == S_OK) { for (int i = 0; i < cFetched; ++i) { strTemp.Format(TEXT("%s\r\n"), (pRegFilter+i)->Name); } strFilters += strTemp; // 释放内存 CoTaskMemFree(pRegFilter); } SetDlgItemText(IDC_FILTERS, strFilters); // 释放接口 pEnum->Release(); pMapper->Release(); // 释放COM环境 CoUninitialize();
某次运行结果如图:
(5) 初始化DirectShow接口
下面的代码演示如何获取(2)中所提到的DirectShow常用接口,如果获取这些接口成功,接下来的工作就是利用这些接口提供的方法来播放、控制视频文件了。编写DirectShow应用程序时需要#include <dshow.h>,并且引用strmiids.lib。如果提示找不到dshow.h,那很可能是使用的sdk不支持,也就是该sdk所对应的wince平台没有添加DirectShow相关组件。
// 第1步:创建IGraphBuilder接口 hResult = CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC, IID_IGraphBuilder, (void **)&m_pGB); if (hResult != S_OK) { return FALSE; } // 第2步:利用IGraphBuilder接口渲染视频文件 hResult = m_pGB->RenderFile(strFileName,NULL); if (hResult != S_OK ) { return FALSE; } // 第3步:得到媒体播放控制接口IMediaControl hResult = m_pGB->QueryInterface(IID_IMediaControl, (void **)&m_pMC); if (hResult != S_OK) { return FALSE; } // 第4步:得到媒体播放位置搜索接口IMediaSeeking hResult = m_pGB->QueryInterface(IID_IMediaSeeking,(void**)&m_pMS); if (hResult != S_OK) { return FALSE; } // 设置查找定位的时间单位 GUID guid_timeFormat = TIME_FORMAT_MEDIA_TIME; m_pMS->SetTimeFormat(&guid_timeFormat); // 第5步:得到媒体事件接口IMediaEventEx hResult = m_pGB->QueryInterface(IID_IMediaEventEx,(void**)&m_pME); if (hResult != S_OK) { return FALSE; } // 第6步:得到IVideoWindow接口 hResult = m_pGB->QueryInterface(IID_IVideoWindow, (void **)&m_pVW); if (hResult != S_OK) { return FALSE; } // 第7步:得到基础视频流接口IBasicVideo hResult = m_pGB->QueryInterface(IID_IBasicVideo, (void **)&m_pBV); if (hResult != S_OK) { return FALSE; } // 第8步:得到基础音频流接口IBasicAudio hResult = m_pGB->QueryInterface(IID_IBasicAudio, (void **)&m_pBA); if (hResult != S_OK) { return FALSE; }
(6) 小结
通过上面的学习,熟悉了DirectShow的相关接口和接口的常用方法之后,就可以编写多媒体应用程序了。可以先从codeproject、pudn等网站上找些别人已经封装好的DirectShow c++ wrapper,仔细看看那些比较成熟的库是如何封装的,是如何初始化并使用那些DirectShow接口的,最后再在其基础上进行修改,以适应自己的需要或者加强它们的功能。这样上手的速度能更快些。
基于DirectShow的WinCE多媒体编程 by斜风细雨QQ:253786989 2012-02-17