簡單的用directshow播放一些視頻和音頻流的程式

來源:互聯網
上載者:User

這是一個基於對話方塊的程式。原始碼參照了微軟的directshow 的例子程式和一本英文介紹如何編寫directshow程式的書裡面的程式碼。

首先,因為要播放視頻流,所以,要在對話方塊上給視頻流的播放留個地方,這樣就先弄個picture control控制項到對話方塊上,將其ID設為IDC_VIDEO_WINDOW,並且讓它的type是Rectangle,並且讓一個類型為CStatic的名為m_VideoWindow的變數和它相關聯,這樣,就可以通過m_VideoWindow控制IDC_VIDEO_WINDOW了。也就是:
void CplayMediaDlg::DoDataExchange(CDataExchange* pDX)
{
 CDialog::DoDataExchange(pDX);
 DDX_Control(pDX, IDC_VIDEO_WINDOW, m_VideoWindow);
}
這可以通過用classvizard添加代碼的方式實現,也可以自己手工添加代碼。

由於directshow用到了com技術,所以,即使僅要編寫directshow的應用程式,也要對元件物件模型有些瞭解。

通過自動的方式播放視頻流和音頻流是比較簡單的,下面是一些原始碼,並且其中會有些講解。

// playMediaDlg.h : 標頭檔
//

#pragma once
#include "afxwin.h"

#include <Dshow.h>
#pragma comment(lib,"Strmiids.lib")

// CplayMediaDlg 對話方塊
class CplayMediaDlg : public CDialog
{
// 構造
public:
 CplayMediaDlg(CWnd* pParent = NULL); // 標準建構函式

// 對話方塊資料
 enum { IDD = IDD_PLAYMEDIA_DIALOG };

 protected:
 virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 支援

// 實現
protected:
 HICON m_hIcon;

 // 產生的訊息映射函數
 virtual BOOL OnInitDialog();
 afx_msg void OnSysCommand(UINT nID, LPARAM lParam);
 afx_msg void OnPaint();
 afx_msg HCURSOR OnQueryDragIcon();
 DECLARE_MESSAGE_MAP()
public:
 CStatic m_VideoWindow;
 afx_msg BOOL OnEraseBkgnd(CDC* pDC);
 afx_msg void OnBnClickedOpen();
 // Graph builder interface,這是用於控制對filter graph建立等等的介面,其中有相應的函數
 IGraphBuilder* pGraph;
 // Media control interface,這是用於控制流程的run,stop,pause的介面,裡面有相應的函數
 IMediaControl* pControl;
  afx_msg void OnBnClickedPlay();
 afx_msg void OnBnClickedPause();
 afx_msg void OnBnClickedStop();
 afx_msg void OnClose();
 // 用於控制顯示在什麼地方的介面,要把視頻流顯示出來就要用到這個介面中的函數
 IVideoWindow* m_pVidWin;
 // 記錄按下了幾次pause按鈕
 int m_pausePress;
 afx_msg void OnBnClickedSave();
};

在playMediaDlg.cpp中是具體的函數代碼。

// playMediaDlg.cpp---------------------------------------

CplayMediaDlg::CplayMediaDlg(CWnd* pParent /*=NULL*/)
 : CDialog(CplayMediaDlg::IDD, pParent)
 , pGraph(NULL)
 , pControl(NULL)
 , m_pVidWin(NULL)
 , m_pausePress(0)
{
 m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}

BOOL CplayMediaDlg::OnInitDialog()
{
 CDialog::OnInitDialog();

 // 將/“關於.../”功能表項目添加到系統功能表中。

 // IDM_ABOUTBOX 必須在系統命令範圍內。
 ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
 ASSERT(IDM_ABOUTBOX < 0xF000);

 CMenu* pSysMenu = GetSystemMenu(FALSE);
 if (pSysMenu != NULL)
 {
  CString strAboutMenu;
  strAboutMenu.LoadString(IDS_ABOUTBOX);
  if (!strAboutMenu.IsEmpty())
  {
   pSysMenu->AppendMenu(MF_SEPARATOR);
   pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
  }
 }

 // 設定此對話方塊的表徵圖。當應用程式主視窗不是對話方塊時,架構將自動
 //  執行此操作
 SetIcon(m_hIcon, TRUE);   // 設定大表徵圖
 SetIcon(m_hIcon, FALSE);  // 設定小表徵圖

 // TODO: 在此添加額外的初始化代碼

 // Since we're embedding video in a child window of a dialog,
    // we must set the WS_CLIPCHILDREN style to prevent the bounding
    // rectangle from drawing over our video frames.
    //
    // Neglecting to set this style can lead to situations when the video
    // is erased and replaced with the default color of the bounding rectangle.
    m_VideoWindow.ModifyStyle(0, WS_CLIPCHILDREN);

 // Initialize the COM library.
    HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
 if (FAILED(hr))
 {
  AfxMessageBox(TEXT("cannot initialize the com"));
 }

    //定義一些介面
 // Create the Filter Graph Manager and query for interfaces.
//用CoCreateInstance也是一種獲得介面的方法
 hr = CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER,
      IID_IGraphBuilder, (void **)&pGraph);
 if(FAILED(hr))
 {
  AfxMessageBox(TEXT("cannot create filter graph manager"));
 }

 // Use IGraphBuilder::QueryInterface (inherited from IUnknown)
 // to get the IMediaControl interface.
 hr = pGraph->QueryInterface(IID_IMediaControl, (void **)&pControl);
 if(FAILED(hr))
 {
  AfxMessageBox(TEXT("Can not obtain the media control interface"));
  pGraph->Release();
  pGraph=NULL;
 }

  //for display
 hr = pGraph->QueryInterface(IID_IVideoWindow, (void**)&m_pVidWin);
 if(FAILED(hr))
 {
  AfxMessageBox(TEXT("can not obtain the video window interface"));
  pControl->Release();
  pControl=NULL;
  pGraph->Release();
  pGraph=NULL;
   }

 return TRUE;  // 除非設定了控制項的焦點,否則返回 TRUE
}

上面的函數其實也就是在建立對話方塊的時候獲得介面,下面的函數是開啟檔案(音頻或者視頻檔案)進行播放的函數。

void CplayMediaDlg::OnBnClickedOpen()
{
 // TODO: 在此添加控制項通知處理常式代碼
 //建立filter graph

 HRESULT hr;
 CFileDialog dlg(TRUE);
 if (dlg.DoModal()==IDOK)
 {

  // To build the filter graph, only one call is required.
  // We make the RenderFile call to the Filter Graph Manager
  // to which we pass the name of the media file.
  //下面的幾行轉換很重要,其實也就是把ASCII碼轉換成Unicode
  WCHAR wFileName[MAX_PATH];
        MultiByteToWideChar(CP_ACP, 0, dlg.GetPathName(), -1, wFileName,
                        MAX_PATH);

//這裡其實就是讓directshow自動根據要播放的檔案的格式,產生filter graph
hr=pGraph->RenderFile((LPCWSTR)wFileName,NULL);
  if(FAILED(hr))
  {
   AfxMessageBox(TEXT("can not open the file"));
  }

  //用於設定顯示的位置的代碼,這裡就是進行顯示的設定
  HRESULT hr = m_pVidWin->put_Owner((OAHWND) m_VideoWindow.GetSafeHwnd());
  if (SUCCEEDED(hr))
  {
   // The video window must have the WS_CHILD style
   hr = m_pVidWin->put_WindowStyle(WS_CHILD);
   // Read coordinates of video container window
   RECT rc;
   m_VideoWindow.GetClientRect(&rc);
   long width =  rc.right - rc.left;
   long height = rc.bottom - rc.top;
   // Ignore the video's original size and stretch to fit bounding rectangle
   hr = m_pVidWin->SetWindowPosition(rc.left, rc.top, width, height);
   m_pVidWin->put_Visible(OATRUE);
  }

 }

}

下面的三個函數是為一個按鈕控制項添加的訊息響應函數

void CplayMediaDlg::OnBnClickedPlay()
{
 // TODO: 在此添加控制項通知處理常式代碼
    
    //run the graph
   HRESULT hr=pControl->Run();

 }

void CplayMediaDlg::OnBnClickedPause()
{
 // TODO: 在此添加控制項通知處理常式代碼
 HRESULT hr=pControl->Pause();
 if(FAILED(hr))
 {
  AfxMessageBox(TEXT("can not pause the filter graph"));
 }
}

void CplayMediaDlg::OnBnClickedStop()
{
 // TODO: 在此添加控制項通知處理常式代碼
 HRESULT hr=pControl->Stop();
 if(FAILED(hr))
 {
  AfxMessageBox(TEXT("can not stop the filter graph"));
 }

 }

當對話方塊關閉時,要進行些收尾的工作

void CplayMediaDlg::OnClose()
{
 // TODO: 在此添加訊息處理常式代碼和/或調用預設值

 //// Now release everything and clean up.
    pControl->Release();
     pGraph->Release();
  m_pVidWin->Release();
     CoUninitialize();

 CDialog::OnClose();
}

下面的這個函數從一本英文書上修改的,用途是把當前的filter graph儲存起來,這樣,對於調試會比較的方便的,到時候用graph edit看就可以了

void CplayMediaDlg::OnBnClickedSave()
{
 // TODO: 在此添加控制項通知處理常式代碼
 HRESULT hr;
 CFileDialog dlg(TRUE);

 if (dlg.DoModal()==IDOK)
 {
  WCHAR wFileName[MAX_PATH];
        MultiByteToWideChar(CP_ACP, 0, dlg.GetPathName(), -1, wFileName, MAX_PATH);

  IStorage* pStorage=NULL;

   // First, create a document file that will hold the GRF file
  hr = ::StgCreateDocfile(
   wFileName,
   STGM_CREATE|STGM_TRANSACTED|STGM_READWRITE|STGM_SHARE_EXCLUSIVE,
   0, &pStorage);
  if (FAILED(hr))
  {
   AfxMessageBox(TEXT("Can not create a document"));
   return;
  }

        // Next, create a stream to store.
  WCHAR wszStreamName[] = L"ActiveMovieGraph";
  IStream *pStream;
  hr = pStorage->CreateStream(
         wszStreamName,
         STGM_WRITE|STGM_CREATE|STGM_SHARE_EXCLUSIVE,
         0, 0, &pStream);
  if(FAILED(hr))
  {
   AfxMessageBox(TEXT("Can not create a stream"));
   pStorage->Release();
   return;
  }

  // The IpersistStream::Save method converts a stream
        // into a persistent object.
  IPersistStream *pPersist = NULL;
  pGraph->QueryInterface(IID_IPersistStream,
       reinterpret_cast<void**>(&pPersist));
  hr = pPersist->Save(pStream, TRUE);
  pStream->Release();
  pPersist->Release();

  if(SUCCEEDED(hr))
  {
   hr = pStorage->Commit(STGC_DEFAULT);
   if (FAILED(hr))
   {
    AfxMessageBox(TEXT("can not store it"));é
   }
  }
  pStorage->Release();

 }

}

這樣,一個簡單的基於directshow的流媒體播放程式,就算完成了。

感覺上對於編寫directshow的應用程式,各個程式不同的地方在於filter graph不一樣的,不一樣的filter graph所針對的流媒體的類型也是不一樣的,別的如同顯示視頻流的代碼都是一樣的。而這裡,由於是讓directshow自動產生filter graph,所以,只用了一行代碼hr=pGraph->RenderFile((LPCWSTR)wFileName,NULL);很多時候,需要自己組建filter graph,這就會複雜些。並且,這裡當開啟另一個檔案的時候,應該把由原來檔案產生的filter graph刪除掉的,但是,這裡沒有作。並且,還有考慮不太周到的地方。將來改進。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.