基於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();專門用來實例化各個變量

  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,以解決有些格式播放有晃動不清的問題,寫的不好,還請大家批評指正謝謝大家的觀摩。


發佈了33 篇原創文章 · 獲贊 7 · 訪問量 14萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章