DirectShow编程资料(写代码必看)

 
Filter Graph Manager接口
IGraphBuilder: 负责 Filter Graph的创建
应用程序通过此接口建立过滤器流水线。主要方法为:RenderFile,自动识别多媒体文件的类型、格式,建立适用于该格式的过滤器流水线。
IMediaControl: 操作Filter Graph 中的多媒体数据流
控制过滤器流水线的运行。主要方法为:Run开始运行;Pause,暂停运行;Stop,停止运行。
IMediaEvent(Ex): 处理 Filter Graph (Event)的事件
应用程序通过此接口获得播放过程中发生的事件,如 EC_COMPLETE(播放完毕)等。主要方法为:SetNotifyWindow,指定处理事件通知的窗口;GetEvent,获得事件。
IVideoWindow: 用于设置多媒体播放窗口的属性
控制视频窗口的属性。主要方法为:put_Owner,指定视频窗口的父窗口;put_FullScreenMode,指定全屏播放模式;SetWindowPosition,指定视频窗口的位置;put_MessagerDrain,指定一个窗口,用于接收视频窗口发出的鼠标等消息。
IMediaSeeking:提供了一些简单的搜索功能
提供了对多媒体数据流的播放位置等属性的精确控制。主要方法为:SetPositons,设置播放的起始和终止位置;GetCurrentPosition,获得当前播放位置。
IFilterMapper2:对注册表中的滤波器进行枚举
IBasicAudio:
控制音频数据流的基本属性:音量和均衡。主要方法:put_Volume、get_Volume,设置或获得音量;put_Balance、get_Balance,设置或获得均衡。

在实现多媒体文件的播放时需要用到上述的接口,这些接口的每个方法只执行一个简单的操作。因此,有必要对这些接口进行封装,见(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   ,并在工程中加入strmiids.lib。

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
           

你可能感兴趣的:(DirectShow学习)