基于directShow,打造全能播放器系列之一

基于directShow,打造全能播放器系列之一

分类: VC++ directShow 151人阅读 评论(0) 收藏 举报

总前言:我打算写一个能实现全能播放的播放器,功能比较简单,也算是抛砖引玉吧,因为内容较多,所以打算写三篇,这是开篇,欢迎大家吐槽

简易播放器的实现

本文的编写环境:visual studio 2008 ,基于MFC based DLG 的应用程序
前言:我写这个系列博客的目的,是想让大家知道,播放器的实现,其实没有想像的那么难,只是掌握了一点的方法,自己完全可以实现,当然出于容易讲解的目的,我会将代码写的尽量简洁,当然,在每个博客的最后都将贴出源代码地址,以供大家,研究学习。

前提:本文并不是假设你从零基础开始就能完全实现的,如果你根本没有了解过directShow,那还是请你从头开始吧,慢慢地了解个个函数的功能,然后再到这里来,因为我会从如何配置VS2005开始一步步的教到你完全写出这个播放器为止,但我并不会每句代码都会讲的很详细,如果有不理解的地方,你可以查看MSDN,directX SDK,找度娘,找谷歌,都是不错的选择。

注意:这篇博客与《DirectShow开发指南》第五章的例子,师从同路,高手可以不看

下面就开始播放器开发的旅程了,准备好了吧,那我们开始了

第一步:配置VS播放器,首先你得先安装directX 9.0开发包,安装好之后,记得编译dx9sdk\Samples\C++\DirectShow\BaseClasses这个目录下的baseclasses工程,然后就是将所需要的文件包含在VS2005配置中,为节省篇幅,这里就不再缀述,可以参看《DirectShow开发指南》P66-P67(开发环境的配置)

第二步:应用程序创建、界面及程序设置
创建一个MFC应用程序,based DLG,命名为Player

(一)配置
1、选择“项目”-》“player属性”打开属性页;
2、选择先在DEBUG模式下,选择“配置属性”-》“链接器”-》“输入”-》“附加依赖项”处添加:
strmbasd.lib uuid.lib winmm.lib Quartz.lib Strmiids.lib
如图:

3、然后将“配置”选项改为“Release”,重复上一操作,即添加相同的依赖项
4、在PlayDlg.cpp和PlayDlg.h的顶部加入directShow所需要的头文件#include<streams.h>

(二)界面设置
界面如下:

说明:
1、其中红框处,是添加的一个Picture Ctrl控件,ID号设置为:IDC_VIDEOWND,将其Type属性改为“Rectangle

2、添加了一个Slider Ctrl,其ID设置为IDC_PROGRESS

3、添加三个按钮,“打开”按钮的ID号为:IDC_BTN_OPEN,“播放”按钮的ID号设置为IDC_BTN_PLAY,“暂停”按钮的ID号设置为“IDC_BTN_PAUSE”,“停止”按钮的ID号设置为:IDC_BTN_STOP

(三)关联变量

1、对Picture Ctrl控件关联CStatic型变量m_VideoWindowPlay;

2、对Slider Ctrl控件关联CSliderCtrl型变量m_Slider;

注意:由于MFC本身的CSliderCtrl会存在很多问题,比如定位不准确,等,所以我们一般不使用这个类,而改成我们自己的类,请到下面的地下下载CNiceSlider

http://download.csdn.net/detail/harvic880925/4554013

然后将下载后的文件加载到工程中,并在PlayerDlg.h的文件中增加#include"NiceSlider.h"

然后将m_VideoWindow前的CStatic改为CNiceSliderCtrl,即

(四)初始化COM组件
因为DirectShowCOM组件,所以我们在使用前要先对其初始化,用完之后,也要手动解除

CPlayerApp类中的InitInstance()函数中,添加初始化代码:

  1. <span style="font-size:14px;">HRESULT hr=CoInitialize(NULL);  
  2. if(FAILED(hr))  
  3. {  
  4.     printf("ERROR-Could not initialize COM libray");  
  5.     return -1;  
  6. }  
  7. </span>  

位置如图:

然后添加在CPlayerApp类中添加ExitInstance()函数,在其中添加::CoUninitialize();以解除使用


第三步:实战

(一)变量定义、初始化及实例化
1、变量定义:

  1. <span style="font-size:14px;">  IGraphBuilder * m_Graph;   //GraphBuilder对象,实现整个Graph的构建及执行等  
  2.     IMediaControl * m_MediaControl; //主要用来媒体控制,Run()、Pause()、Stop()等  
  3.     IMediaEventEx * m_Event;     //主要用来关联消息接收及处理对象、实现消息处理,跟写WIN32 SDK程序差不多,需要自己捕捉消息,然后自定义处理函数  
  4.     IBasicVideo * m_BasicVideo;  //视频控制  
  5.     IBasicAudio * m_BasicAudio;  //音频控制  
  6.     IVideoWindow * m_VideoWindow;  //主要用来指定播放窗口,定义全屏,等  
  7.     IMediaSeeking * m_Seeking;   //主要用来媒体定位</span>  

以上只是对各变量功能作了下简单的讲解,如果想具体了解,可以查看SDKMSDN

2、初始化

CPlayerDlg::OnInitDialog()在添加初始化信息:

  1. <span style="font-size:14px;">  m_Graph=NULL;  
  2.      m_MediaControl=NULL;  
  3.      m_Event=NULL;  
  4.      m_BasicVideo=NULL;  
  5.      m_BasicAudio=NULL;  
  6.      m_VideoWindow=NULL;  
  7.      m_Seeking=NULL;  
  8.     this->m_Slider.SetRange(0,1000);  
  9.     this->m_Slider.SetPos(0);</span>  

3、实例化

在CPlayerDlg类中,添加一个函数Create();专门用来实例化各个变量

[csharp] view plain copy
  1. <span style="font-size:14px;">bool CPlayerDlg::Create()  
  2. {  
  3.     if(!m_Graph)  
  4.     {  
  5.         HRESULT hr=S_OK;  
  6.         if(SUCCEEDED(::CoCreateInstance(CLSID_FilterGraph,NULL,CLSCTX_INPROC_SERVER,IID_IGraphBuilder,(void * *)&m_Graph)))  
  7.         {  
  8.             hr |=this->m_Graph->QueryInterface(IID_IMediaControl,(void **)&this->m_MediaControl);  
  9.             hr |=this->m_Graph->QueryInterface(IID_IBasicAudio,(void **)&this->m_BasicAudio);  
  10.             hr |=this->m_Graph->QueryInterface(IID_IMediaEventEx,(void **)&this->m_Event);  
  11.             hr |=this->m_Graph->QueryInterface(IID_IBasicVideo,(void **)&this->m_BasicVideo);  
  12.             hr |=this->m_Graph->QueryInterface(IID_IVideoWindow,(void **)&this->m_VideoWindow);  
  13.             hr |=this->m_Graph->QueryInterface(IID_IMediaSeeking,(void **)&this->m_Seeking);  
  14.   
  15.             if(this->m_Seeking)  
  16.             {  
  17.                 m_Seeking->SetTimeFormat(&TIME_FORMAT_MEDIA_TIME);//将时间设置为以100ns为单位的格式  
  18.             }  
  19.             return SUCCEEDED(hr);  
  20.         }  
  21.         m_Graph=0;  
  22.     }  
  23.     return false;  
  24. }</span>  

(二)针对更接口函数的封装

1、针对IMediaControl类的函数封装,封装Run(),Stop,Pause()函数,及IsRun(),IsStop(),IsPause()函数的实现,代码简单,不再详述

  1. <span style="font-size:14px;">bool CPlayerDlg::IsRunning()  
  2. {  
  3.     if(m_Graph&&this->m_MediaControl)  
  4.     {  
  5.         OAFilterState FilterState=State_Stopped;  
  6.         HRESULT hr=this->m_MediaControl->GetState(10,&FilterState);  
  7.         if(SUCCEEDED(hr))  
  8.         {  
  9.             if(FilterState==State_Running)  
  10.             {return true;}  
  11.         }  
  12.     }  
  13.     return false;  
  14. }  
  15. bool CPlayerDlg::Run()  
  16. {  
  17.     if(m_Graph&&this->m_MediaControl)  
  18.     {  
  19.         if(!IsRunning())  
  20.         {  
  21.             if(SUCCEEDED(this->m_MediaControl->Run()))  
  22.             {  
  23.                 return true;  
  24.             }  
  25.         }else  
  26.         {  
  27.             return true;  
  28.         }  
  29.     }  
  30.     return false;  
  31. }  
  32.   
  33. bool CPlayerDlg::IsStopped()  
  34. {  
  35.     if(m_Graph&&this->m_MediaControl)  
  36.     {  
  37.         OAFilterState FilterState=State_Stopped;  
  38.         if(SUCCEEDED(this->m_MediaControl->GetState(10,&FilterState)))  
  39.         {  
  40.             if(FilterState==State_Stopped)  
  41.             {  
  42.                 return true;  
  43.             }  
  44.         }  
  45.     }  
  46.     return false;  
  47. }  
  48. bool CPlayerDlg::Stop()  
  49. {  
  50.     if(m_Graph&&this->m_MediaControl)  
  51.     {  
  52.         if(!this->IsStopped())  
  53.         {  
  54.             if(SUCCEEDED(this->m_MediaControl->Stop()))  
  55.             {  
  56.                 return true;  
  57.             }  
  58.         }else  
  59.         {  
  60.             return true;  
  61.         }  
  62.     }  
  63.     return false;  
  64. }  
  65. bool CPlayerDlg::IsPaused()  
  66. {  
  67.     if(m_Graph&&this->m_MediaControl)  
  68.     {  
  69.         OAFilterState FilterState=State_Stopped;  
  70.         if(SUCCEEDED(this->m_MediaControl->GetState(10,&FilterState)))  
  71.         {  
  72.             if(FilterState==State_Paused)  
  73.             {  
  74.                 return true;  
  75.             }  
  76.         }  
  77.     }  
  78.     return false;  
  79. }  
  80. bool CPlayerDlg::Pause()  
  81. {  
  82.     if(m_Graph&&this->m_MediaControl)  
  83.     {  
  84.         if(!this->IsPaused())  
  85.         {  
  86.             if(SUCCEEDED(this->m_MediaControl->Pause()))  
  87.             {  
  88.                 return true;  
  89.             }  
  90.         }else  
  91.         {  
  92.             return true;  
  93.         }  
  94.     }  
  95.     return false;  
  96. }</span>  

2、针对IMediaSeeking类的函数封装,主要是针对GetDurationGetCurrentPositionSetPositions函数的封装,主要是用来计算当前S lider控件中托动点的位置用的,这里我只是封装了几个我们播放用的函数,其实IMediaSeeking还有其它的一些函数,也是很好的,大家可以查看下SDK,比如设置播放速率什么的,实现慢放、快放等,这些就靠大家去研究吧

  1. <span style="font-size:14px;">bool CPlayerDlg::GetDuration(double * outDuration)  
  2. {  
  3.     if (m_Seeking)  
  4.     {  
  5.         LONGLONG length = 0;  
  6.         if (SUCCEEDED(m_Seeking->GetDuration(&length)))  
  7.         {  
  8.             *outDuration = ((double)length) / 10000000.;  
  9.             return true;  
  10.         }  
  11.     }  
  12.     return false;  
  13. }  
  14. //这里要简单的做一个说明,因为我们在Create()函数中,已经设定了时间格式为TIME_FORMAT_MEDIA_TIME,即是以ns为播放单位的  
  15. //也即IMediaSeeking::GetDuration()返回给我们的值是以ns的单位的,我们要化成以秒为单位,要除以的次方  
  16. bool CPlayerDlg::GetCurrentPosition(double *outPosition)  
  17. {  
  18.     if(m_Graph&&this->m_Seeking)  
  19.     {  
  20.         LONGLONG position=0;  
  21.         if(SUCCEEDED(this->m_Seeking->GetCurrentPosition(&position)))  
  22.         {  
  23.             *outPosition=((double)position) / 10000000.;  
  24.             return true;  
  25.         }  
  26.     }  
  27.     return false;  
  28. }  
  29. //原理与上一个函数类似,不再缀述  
  30. bool  CPlayerDlg::SetCurrentPosition(double Position)  
  31. {  
  32.     if(m_Graph&&this->m_Seeking)  
  33.     {  
  34.         LONGLONG pos=10000000*Position;//首先转换为正规时间格式  
  35.         HRESULT hr=this->m_Seeking->SetPositions(&pos,AM_SEEKING_AbsolutePositioning|AM_SEEKING_SeekToKeyFrame,0,AM_SEEKING_NoPositioning);  
  36.       
  37.         if(SUCCEEDED(hr))  
  38.         {  
  39.             return true;  
  40.         }  
  41.     }  
  42.     return false;  
  43. }  
  44. //这里主要是将秒为单位的时间,转化为IMediaSeeking可以识别的ns为单位的时间,也就是上面两个函数的反操作</span>  

3、针对IVideoWindow类的函数封装,这个就稍微有点难度了,不再是仅仅的对一个函数的封装了,这里是真正的自己实现,我们实现的函数有SetDisplayWindow(用于设置播放窗口),SetFullScreen(用于将视频设置为全屏)、GetFullScreen(获取当前全屏状态),具体实现代码如下:

  1. <span style="font-size:14px;">bool CPlayerDlg::SetFullScreen(bool inEnabled)  
  2. {  
  3.     if(m_Graph&&this->m_VideoWindow)  
  4.     {  
  5.         if(SUCCEEDED(this->m_VideoWindow->put_FullScreenMode(inEnabled ? OATRUE : OAFALSE)))  
  6.         {  
  7.             return true;  
  8.         }  
  9.     }  
  10.     return false;  
  11. }  
  12. //这里同样是一条函数的封装,我想三目运算符,大家应该还记得吧,这里就不讲了哦  
  13. bool CPlayerDlg::GetFullScreen(void)  
  14. {  
  15.     if (m_VideoWindow)  
  16.     {  
  17.         long  fullScreenMode = OAFALSE;  
  18.         m_VideoWindow->get_FullScreenMode(&fullScreenMode);  
  19.         return (fullScreenMode == OATRUE);  
  20.     }  
  21.     return false;  
  22. }  
  23. //这个函数难度不大,应该没什么可讲的吧  
  24. bool CPlayerDlg::SetDisplayWindow(HWND HWindow)  
  25. {  
  26.     if(this->m_VideoWindow)  
  27.     {  
  28.         this->m_VideoWindow->put_Visible(OAFALSE);//首先隐藏窗口  
  29.   
  30.         if(HWindow)  
  31.         {  
  32.             this->m_VideoWindow->put_Owner(OAHWND(HWindow));  
  33.   
  34.             CRect wndRect;  
  35.             ::GetClientRect(HWindow,&wndRect);//一定要注意要用WND坐标,而不能桌面坐标  
  36.             this->m_VideoWindow->put_Left(0);  
  37.             this->m_VideoWindow->put_Top(0);  
  38.             this->m_VideoWindow->put_Height((long)wndRect.Height());  
  39.             this->m_VideoWindow->put_Width((long)wndRect.Width());  //定义显示窗口位置  
  40.   
  41.             this->m_VideoWindow->put_WindowStyle(WS_CHILD|WS_CLIPCHILDREN|WS_CLIPSIBLINGS);//WS_CLIPSIBLINGS表示重绘时,只重绘这一个窗口,其它窗口不发生重绘。  
  42.                                                                                             //WS_CLIPCHILDREN表示此窗口不允许被其它窗口覆盖  
  43.             this->m_VideoWindow->put_MessageDrain((OAHWND)HWindow); //设置接收鼠标键盘的窗口  
  44.   
  45.             this->m_VideoWindow->put_Visible(OATRUE);  
  46.   
  47.             return true;  
  48.         }  
  49.     }  
  50.     return false;  
  51. }  
  52. //这个函数应该算是封装里面有点重量级的了,我们传进来所要设为显示窗口的句柄,首先用this->m_VideoWindow->put_Owner(OAHWND(HWindow));  
  53. //将视频窗口隐藏,不然当反应速度变慢时,将会产生闪烁,下面就是获取当前GetClientRect()句柄的窗口大小信息,然后设定给m_VideoWindow  
  54. //然后用put_MessageDrain()方法,让其接收鼠标键盘信息</span>  

4、针对IMediaEventEx类的函数封装,实现对SetNotifyWindow的封装

  1. <span style="font-size:14px;">bool CPlayerDlg::SetNotifyWindow(HWND HWindow)  
  2. {  
  3.     if(this->m_Event&&HWindow)  
  4.     {  
  5.         HRESULT hr=this->m_Event->SetNotifyWindow((OAHWND)HWindow,WM_GRAPHNOTIFY,0);  
  6.         if(SUCCEEDED(hr))  
  7.         {  
  8.             return true;  
  9.         }  
  10.     }  
  11.     return false;  
  12. }</span>  

//定义消息接收的窗口,在其中定义的消息为WM_GRAPHNOTIFY,WM_GRAPHNOTIFY是我们自定义的消息,,

1、在PlayerDlg.h文件中,在顶部添加#define WM_GRAPHNOTIFY  (WM_USER+20)

2、在PlayerDlg.h文件中,并且在头文件中DECLARE_MESSAGE_MAP上面添加afx_msg LRESULT OnGraphNotify(WPARAM inWParam,LPARAM inLParam);位置如图:

3、在PlayerDlg.cpp中如图所示位置在//}}AFX_MSG_MAP下面,添加ON_MESSAGE(WM_GRAPHNOTIFY,OnGraphNotify)

4、最后在PlayerDlg.cpp中增加对响应函数的实现,这里可以先加一个框架,在文章的最后,会贴出具体实现,这里可以先写成

  1. <span style="font-size:14px;">LRESULT CPlayerDlg::OnGraphNotify(WPARAM inWParam, LPARAM inLParam)  
  2. {  
  3.     return true;  
  4. }</span>  

5,针对RenderFile()的封装

  1. <span style="font-size:14px;">bool CPlayerDlg::RenderFile(const char * inFile)  
  2. {  
  3.     if(m_Graph)  
  4.     {  
  5.          WCHAR szFilePath[MAX_PATH];  
  6.          MultiByteToWideChar(CP_ACP, 0, inFile, -1, szFilePath, MAX_PATH);//把ASCII编码转换成UNICODE编码  
  7.   
  8.          if(SUCCEEDED(this->m_Graph->RenderFile(szFilePath,NULL)))  
  9.          {  
  10.              return true;  
  11.          }  
  12.     }  
  13.     return false;  
  14. }</span>  

(三)对添加对按钮的响应

一、添加对“打开”按钮的响应

在其响应函数中添加如下代码

  1. void CPlayerDlg::OnBnClickedBtnOpen()  
  2. {  
  3.     // TODO: 在此添加控件通知处理程序代码  
  4.     CString    strFilter = _T("AVI File (*.avi)|*.avi|");  
  5.     strFilter +=_T( "MPEG File (*.mpg;*.mpeg)|*.mpg;*.mpeg|");  
  6.     strFilter +=_T("Mp3 File (*.mp3)|*.mp3|");  
  7.     strFilter +=_T( "Wave File (*.wav)|*.wav|");  
  8.     strFilter +=_T( "All Files (*.*)|*.*|");  
  9.     CFileDialog dlgOpen(TRUE, NULL, NULL, OFN_PATHMUSTEXIST | OFN_HIDEREADONLY,   
  10.         strFilter, this);  
  11.     if (IDOK == dlgOpen.DoModal())   
  12.     {  
  13.         m_SourceFile = dlgOpen.GetPathName();  
  14.         this->CreateGraph();  
  15.     }  
  16. }  
  17. //主要是添加一个打开对话框,并在关闭之后,保存打开的文件的路径,然后就是构建Graph;  

说明:1、m_SourceFile是定义的一个CStringA 对象,主要是用来保存打开文件的路径,大家自己添加定义和初始化一下吧。

2、CreateGraph()是新封装的一个函数,代码及讲解如下

  1. bool CPlayerDlg::CreateGraph()  
  2. {  
  3.     this->DestroyGraph();  
  4.   
  5.     if(this->Create())  
  6.     {  
  7.         this->RenderFile(this->m_SourceFile);  
  8.   
  9.         this->SetDisplayWindow(this->m_VideoWindowPlay.GetSafeHwnd());//设置显示窗口  
  10.         this->SetNotifyWindow(this->GetSafeHwnd());//将当前窗口作为接收消息窗口  
  11.   
  12.         this->Pause();  
  13.         return true;  
  14.   
  15.     }  
  16.     return false;  
  17. }  


 

//这里主要是先用this->Create()创建Graph中的各个变量及初始化操作,然后this->RenderFile(this->m_SourceFile);来渲染文件,也即采用智能链接模式,自动为我们构建播放链路,然后用SetDisplayWindow和SetNotifyWindow来设置播放窗口和消息接收窗口,最后this->Pause();先将视频暂停,等我们按下“播放”按钮时再播放视频。这里有个函数是我们新添加的,即this->DestroyGraph();,它主要实现的功能是释放变量,实现代码如下:

  1. bool CPlayerDlg::DestroyGraph()  
  2. {  
  3.     if(this->m_BasicAudio)  
  4.     {  
  5.         this->m_BasicAudio->Release();  
  6.         this->m_BasicAudio=NULL;  
  7.     }  
  8.     if(this->m_BasicVideo)  
  9.     {  
  10.         this->m_BasicVideo->Release();  
  11.         this->m_BasicVideo=NULL;  
  12.     }  
  13.     if(this->m_Event)  
  14.     {  
  15.         this->m_Event->Release();  
  16.         this->m_Event=NULL;  
  17.     }  
  18.   
  19.     if(this->m_MediaControl)  
  20.     {  
  21.         this->m_MediaControl->Release();  
  22.         this->m_MediaControl=NULL;  
  23.     }  
  24.     if(this->m_Seeking)  
  25.     {  
  26.         this->m_Seeking->Release();  
  27.         this->m_Seeking=NULL;  
  28.     }  
  29.     if(this->m_VideoWindow)  
  30.     {  
  31.         this->m_VideoWindow->put_Visible(OAFALSE);// hide the video window  
  32.         this->m_VideoWindow->put_MessageDrain((OAHWND)NULL);//移除处理鼠标和键盘信息接收窗口  
  33.         this->m_VideoWindow->put_Owner((OAHWND)NULL);//移除视频窗口的拥有者  
  34.         this->m_VideoWindow->Release();  
  35.         this->m_VideoWindow=NULL;  
  36.     }  
  37.   
  38.     if(this->m_Graph) //一定要最后释放m_Graph  
  39.     {  
  40.         this->m_Graph->Release();  
  41.         this->m_Graph=NULL;  
  42.     }  
  43.     return true;  
  44. }  

二、对“播放”按钮的响应

实现代码如下:

  1. void CPlayerDlg::OnBnClickedBtnPlay()  
  2. {  
  3.     // TODO: 在此添加控件通知处理程序代码  
  4.     if(this->m_Graph)  
  5.     {  
  6.         this->Run();  
  7.     }  
  8. }  

三、对“暂停”按钮的响应

  1. void CPlayerDlg::OnBnClickedBtnPause()  
  2. {  
  3.     // TODO: 在此添加控件通知处理程序代码  
  4.     if(this->m_Graph)  
  5.     {  
  6.         this->Pause();  
  7.     }  
  8. }  

四,对“停止”按钮的响应

  1. void CPlayerDlg::OnBnClickedBtnStop()  
  2. {  
  3.     // TODO: 在此添加控件通知处理程序代码  
  4.     if(this->m_Graph)  
  5.     {  
  6.         this->Stop();  
  7.         this->SetCurrentPosition(0);  
  8.     }  
  9. }  

(四)添加进度条推进与拖曳响应

1、首先要增加一个定时器,所以在OnBnClickedBtnOpen()、OnBnClickedBtnPause()、OnBnClickedBtnStop()函数中,添加如下代码:

  1. if(this->m_SliderTimer==0)  
  2. {  
  3.     m_SliderTimer=this->SetTimer(IDC_PROGRESS,100,NULL);  
  4. }  

//表示如果在点击这个按钮时,如果还没有创建定时器,就创建一个定时器,我们不指定接收消息函数,默认让OnTimer()来处理就可以了,这里有个新变量m_SliderTimer,是UINT 类型的变量,大家自己添加一下定义,记得初始化为0哦,用来保存创建Timer的ID号的。

2、对DLG添加WM_TIMER消息响应

  1. void CPlayerDlg::OnTimer(UINT_PTR nIDEvent)  
  2. {  
  3.     // TODO: 在此添加消息处理程序代码和/或调用默认值  
  4.     if(nIDEvent==this->m_SliderTimer&&this->m_Graph)  
  5.     {  
  6.         double pos=0,duration=1;  
  7.         this->GetCurrentPosition(&pos);  
  8.         this->GetDuration(&duration);  
  9.   
  10.         int newPos = int(pos * 1000 / duration);  
  11.         if(this->m_Slider.GetPos()!=newPos)  
  12.         {  
  13.             this->m_Slider.SetPos(newPos);  
  14.         }  
  15.     }  
  16.     CDialog::OnTimer(nIDEvent);  
  17. }   
  18. //这里没什么函数难度,但主要是个思想,也就是根据当前视频的时间来设定滑动标记在Slider的位置  

3、实现对鼠标拉动进度条时的消息响应

DLG添加对WM_HSCROLL消息的响应OnHScroll()

代码如下:

  1. void CPlayerDlg::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)  
  2. {  
  3.     // TODO: 在此添加消息处理程序代码和/或调用默认值  
  4.     if(pScrollBar->GetSafeHwnd()==this->m_Slider.GetSafeHwnd())  
  5.     {  
  6.         if(this->m_Slider)  
  7.         {  
  8.             double duration=1;  
  9.             double pos=0;  
  10.             pos=this->m_Slider.GetPos();  
  11.             this->GetDuration(&duration);  
  12.             double newPos=duration*pos/1000;  
  13.             this->SetCurrentPosition(newPos);  
  14.         }  
  15.     }else  
  16.     {  
  17.         CDialog::OnHScroll(nSBCode, nPos, pScrollBar);  
  18.     }  
  19. }  
  20. //这段代码的主要思想,就是根据Slider的位置来设定视频的位置  

最后贴出消息响应函数的实现代码:

  1. LRESULT CPlayerDlg::OnGraphNotify(WPARAM inWParam, LPARAM inLParam)  
  2. {  
  3.     if(this->m_Graph&&this->m_Event)  
  4.     {  
  5.         long eventCode=0,eventParam1=0,eventParam2=0;  
  6.         while(SUCCEEDED(this->m_Event->GetEvent(&eventCode,&eventParam1,&eventParam2,0)))  
  7.         {  
  8.             m_Event->FreeEventParams(eventCode,eventParam1,eventParam2);  
  9.   
  10.             switch(eventCode)  
  11.             {  
  12.             case EC_COMPLETE:  
  13.                 OnBnClickedBtnPause();  
  14.                 this->SetCurrentPosition(0);  
  15.                 break;  
  16.             case EC_USERABORT:  
  17.             case EC_ERRORABORT:  
  18.                 OnBnClickedBtnStop();  
  19.                 break;  
  20.             default:  
  21.                 break;  
  22.             }  
  23.         }  
  24.     }  
  25.     return 0;  
  26. }  
  27. //这段代码理解起来应该难度不大,主要就是对消息类型的判断,然后根据不同的消息类型用不同的函数来处理  

实现图如下:

 

写上面的内容实在是太累了,暂且写到这吧,下篇将会对播放器进行功能的稍微补充,解码器的安装配置与GraphEdit.exe的使用,而在最后一篇我打算讲解对于手动连接FILTER,以解决有些格式播放有晃动不清的问题,写的不好,还请大家批评指正谢谢大家的观摩。


  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值