在实现多媒体文件的播放时需要用到上述的接口,这些接口的每个方法只执行一个简单的操作。因此,有必要对这些接口进行封装,见(VC下利用DirectShow播放多媒体文件.PDF)
创建 Filter Graph Manager接口
以下是创建 Filter Graph Manager接口的例子:
//首先申明并且初始化必需的接口。由于接口的索引值是自动加1 ,所以不要调用
// Iunknow::Addref
IGraphBuilder pGraph = NULL;
IMediaControl pMediaControl = NULL;
IMediaEvent pEvent = NULL;
IVideoWindow pVW = NULL;
IMediaSeeking pMS = NULL;
IFilterMapper2 pMapper = NULL;
// 实例化一个 Filter Graph Manager ,并且查询各接口
CoCreateInstance(CLSID_FilterGraph, NULL , CLSCTX_ INPROC_SERVER,
IID_ IGraphBuilder , (void **) &pGraph) ;
pGraph -> QueryInterface ( IID_IMediaControl , ( void **)&pMediaControl2 ) ;
pGraph ->QueryInterface(IID_IMediaEvent , (void** ) &pEvent) ;
pGraph ->QueryInterface(IID_IVideoWindow, (void**) &pVW) ;
pGraph2>QueryInterface IID_IMediaSeeking, void(**)&pMS ;
CoCreateInstance(CLSID_FilterMapper2 , NULL , CLSCTX _INPROC,
IID_IFilterMapper2 ,(void **) &pMapper) ;
播放MP3音乐
MIDI音乐的问题是对声卡的依赖性过大,好声卡和差声卡的播放效果实在相差太远。WAV音乐虽然绝对足够精确,但占用的空间之大不可小视。MP3恐怕是一个较好的解决方案。值得注意的是,播放MP3并不需要DirectX Audio,需要的是DirectShow。所以,我们要#include
7.7.1 调入MP3文件
下面把初始化DirectShow和调入MP3合起来说说吧。首先,我们要定义三个对象,其中IGraphBuilder*类型的可以认为是媒体播放设备,IMediaControl*类型的变量负责媒体的播放控制,而IMediaPosition*类型的变量负责媒体的播放位置设定。
#define SAFE_RELEASE(o) if(o){o->Release();o=NULL;}
IGraphBuilder* pGBuilder;
IMediaControl* pMControl;
IMediaPosition* pMPos;
CoInitialize(NULL); //初始化COM
//创建各个对象
CoCreateInstance(CLSID_FilterGraph, NULL,
CLSCTX_INPROC, IID_IGraphBuilder, (void**)&pGBuilder);
pGBuilder-> QueryInterface(IID_IMediaControl, (void**)&pMControl);
pGBuilder-> QueryInterface(IID_IMediaPosition, (void**)&pMPos);
CHAR strSoundPath[MAX_PATH]; //存储音乐所在路径
WCHAR wstrSoundPath[MAX_PATH]; //存储UNICODE形式的路径
GetCurrentDirectory(MAX_PATH, strSoundPath);
strcat(strSoundPath, "\\Sounds\\ ");
strcat(strSoundPath, "a.mp3 "); //假设要播放的是Sounds子目录下的a.mp3
MultiByteToWideChar(CP_ACP, 0, strSoundPath, -1,wstrSoundPath, MAX_PATH);
pGBuilder-> RenderFile(wstrSoundPath, NULL); //调入文件
7.7.2 播放MP3文件
播放MP3的方法十分简单:
pMPos-> put_CurrentPosition(0); //移动到文件头
pMControl-> Run(); //播放
7.7.3 停止播放和释放对象
最后,我们要停止播放音乐并释放各个对象:
pMControl-> Stop(); //停止播放
Stop方法使filter graph停止,这个方法非常重要,因为在媒体文件播放结束后filter graph不会自己停止。
//释放对象
SAFE_RELEASE(pMControl);
SAFE_RELEASE(pMPos);
SAFE_RELEASE(pGBuilder);
CoUninitialize(); //释放COM
问题:
Q:如何能知道一个mp3文件播完没有?
A:DSHOW中是使用事件通知机制和应用程序进行通讯,在播放完成后Graph Menager会发送EC_COMPLETE事件通知。
关于事件接收:查看下一篇文章《DirectShow事件通知概述》
具体代码:
先得到IMediaEvent接口实例:
IGraphBuilder* m_pGBuilder;
IMediaEvent* m_pMEvent;
CoCreateInstance(CLSID_FilterGraph,NULL, CLSCTX_INPROC,IID_IGraphBuilder,(void**)&m_pGBuilder);
m_pGBuilder->QueryInterface(IID_IMediaEvent, (void**)&m_pMEvent);
两种方法:
一:使用IMediaEvent接口的GetEvent方法:
可以放到一个线程里去轮询。
if(SUCCEEDED(CPS->m_pMEvent->GetEvent(&event_code, ¶m1, ¶m2, 1)))
{
if(event_code == EC_COMPLETE)
{//播放结束
// frees resources associated with the parameters of an events.
CPS->m_pMEvent->FreeEventParams(event_code, param1, param2);
break;
}else
{
CPS->m_pMEvent->FreeEventParams(event_code, param1, param2);
}
}
二:在run之后等待
long evCode = 0;
pEvent->WaitForCompletion(INFINITE, &evCode);
这个方法在播放期间被阻塞,直至播放结束或超时。
Q:如何进行播放循环?
A:在事件处理函数中处理EC_COMPLETE事件代码,就是把GRAPH定位到时间线的开头(不需要再次调用run方法,因为媒体文件播放完毕后并没有stop)
Q:播放a.mp3完毕后进行stop,然后重新Render进去一个新的B.wma文件,结果出现重叠播放B.wma和a.mp3文件的状况,我已经stop了啊,stop不是释放资源了吗??
A:换文件必须重新构建Filter Graph,所以需要把原来的FG释放再生成一个新的FG,再Render
Q:如何调节播放器的声音?
A:使用IBasicAudio接口
本来这个问题没有任何悬念,但是,事实上并不是简单调用一下IBasicAudio.put_Volume就成了。我的实现代码如下,已在调试中通过,多谢VC+DirectShow+AVS的“上海--阿易”兄的帮助。
int volumes[] = {-10000,-6418,-6147,-6000,
-5892,-4826,-4647,-4540
-4477, -4162,-3876, -3614, -3500,
-3492,-3374,-3261,-3100,-3153,-3048,-2947,-2849,-2755,-2700,
-2663,-2575,-2520,-2489,-2406,-2325,-2280,-2246,-2170,-2095,-2050,
-2023,-1952,-1900, -1884,-1834, -1820, -1800,-1780, -1757,-1695,-1636,-1579,
-1521,-1500,-1464,-1436,-1420, -1408,-1353,-1299,-1246,-1195,-1144,
-1096,-1060, -1049,-1020,-1003,-957,-912,-868, -800, -774,-784, -760, -744,
-705,-667,-630,-610,-594,-570 ,-558,-525,-493,-462,-432,-403,
-375,-348,-322,-297,-285, -273,-250,-228,-207,-187,-176, -168,
-150,-102,-75,-19,-10,0,0};
///
/// 获得、设置音量
///
get
{
if (basicAudio == null) return 0;
int hr = 0, volume = 0;
hr = basicAudio.get_Volume(out volume);
DsError.ThrowExceptionForHR(hr);
foreach (int v in volumes)
if (v >= volume) { volume = v; break; }
return volume;
}
set