• <ruby id="5koa6"></ruby>
    <ruby id="5koa6"><option id="5koa6"><thead id="5koa6"></thead></option></ruby>

    <progress id="5koa6"></progress>

  • <strong id="5koa6"></strong>
  • Visual C++.NET DirectShow編程

    發表于:2007-05-25來源:作者:點擊數: 標簽:編程DirectShowC++.NETvisual
    DirectShow是微軟公司提供的一套在Windows平臺上進行流媒體處理的 開發 包,與DirectX開發包一起發布。DirectShow為多媒體的捕捉和回放提供了強有力的支持。運用DirectShow我們可以很方便地從支持WDM驅動模型的采集卡上捕獲數據,并且進行相應的后期處理乃至

      DirectShow是微軟公司提供的一套在Windows平臺上進行流媒體處理的開發包,與DirectX開發包一起發布。DirectShow為多媒體的捕捉和回放提供了強有力的支持。運用DirectShow我們可以很方便地從支持WDM驅動模型的采集卡上捕獲數據,并且進行相應的后期處理乃至存儲到文件中。它廣泛地支持各種媒體格式,包括Asf、Mpeg、Avi、Dv、Mp3、Wave等等,使得多媒體數據的回放變得輕而易舉。 另外,DirectShow還集成了DirectX其它部分(比如DirectDraw、DirectSound)的技術,直接支持DVD播放,視頻的非線性編輯,以及與數據攝像機的交換。更值得一提的是,DirectShow提供的是一種開放式的開發環境,我們可以根據自己的需要定制自己的組件。

      本文將對DirectShow的應用進行入門級的介紹。

      入門

      DirectShow使用一種叫做Filter Graph的模型來管理整個數據流的處理過程,參與數據流處理的各個功能模塊稱做Filter,各個Filter在Filter Graph中按一定的順序連成一條流水線協調工作,完成一些相對獨立的功能,如Filter可以完成如下的一些功能:

       讀文件

       從視頻設備中獲取視頻

       對視頻流進行解碼

       將數據送往聲卡或顯卡

      每個Filter都有輸入端和輸出端,例如一個MPEG-1解碼Filter它的輸入是MPEG編碼的流數據,它的輸出端是一解碼過的流數據。DirectShow正是通過將不同的Filter連接在一起完成特定的功能的,我們將這些Filter的連接叫做Filter Graph,如下圖A給出是播放AVI的Filter Graph:


    圖A 播放AVI文件的Graph Filter圖

      上圖中每個模塊分別代表了不同的Filter,媒體文件Filter從硬盤讀取AVI文件,AVI分離Filter將文件分離為音頻流和視頻流,AVI解碼Filter對視頻流進行解碼并送往Video表現Filter,由后者將各幀在顯示器上顯示,默認的DirectSound設備用DirectSound將音頻流輸出。

      我們的應用并不需要對這當中的所以的數據流進行管理,在DirectShow提供一個稱做Filter Graph管理器的高級組件。在我們的應用中只需要調用它的API即可,如Run、Stop等,如果你想對其中的數據流做更進一步的控制,你可以對這些Filter直接通過COM接口進行存取。

      Filter Graph管理器同時也提供了另一個功能:應用程序可以通過管理器控制Filter Graph如何生成。



      DirectShow應用

      從廣義上說,所有的DirectShow應用都必須完成三件事情,如下圖B所示:


    圖B

      1. 生成Filter Graph管理器的一個實例。

      2. 利用Filter Grapth實例生成Filter Graph,具體應該由哪些Filter組成Filter Graph視我們的應用的需要而定。

      3. 通過對Filter Graph管理器的方法調用和來自Filter Graph的消息的響應Filter Graph和數據流進行控制。

      DirectShow是基于COM的,Filter Graph管理器和Filter都是COM對象 ,在開始著手之前你應該對COM有個基本的認識。

      下面讓我們著手開始做一個簡單的DirectShow應用,在這個應用中我們實現這樣的功能:打開一個媒體文件,并對其進行播放。

      設置環境

      在利用DirectShow進行流媒體的處理之前,必須正確安裝DirectX的SDK,DirectX SDK可以到微軟的網站上下載,目前,DirectX最新版本為9.0。

      在正確安裝好DirectX SDK后,我們必須設置DirectX SDK的頭文件和庫文件,使其在Visual Studio的搜索路徑內。對于Visual Studio .NET 2003可如下進行設置:菜單→工具→選項→項目→VC++目錄,在包含文件中加入D:\DXSDK\Include,在庫文件中加入D:\DXSDK\lib(我的SDK的安裝路徑是D:\DXSDK):



      頭文件

    文件名稱 描述
    Dshow.h 所有的DirectShow應用都必須包含

      庫文件

    文件名稱 描述
    Strmiids.lib 此庫文件中導出類標識(CLSID)和接口標識(IID),所有的DirectShow應用都必須包含此文件。
    Quartz.lib 此庫文件中導出函數AMGetErrorText,如果你的程序中調用了此函數,則必須包含此庫文件。


      開始工程

      打開Visual Studio .NET 2003,文件→新建→項目

      1.生成基于MFC的應用程序,名稱PlayWnd。



      2.選擇應用程序類型基于對話框,點擊完成。

      3.設置工程屬性

      項目→PlayWnd屬性→配置屬性→鏈接器→輸入→附加依賴項,添加庫文件Strmiids.lib 和Quartz.lib。

      由于Dshow.h頭文件是在任何DirectShow工程中都要用到的,因此我們stdafx.h加入如下行:

    #include <Dshow.h>

      4.設計對話框,如下:



    IDC_STATIC 控件類型 Static Text
    Caption 媒體文件名:
    IDC_MEDIAFILE_EDIT 控件類型 Edit Control
    IDC_BROWSE_BUTTON 控件類型 Button
    Caption 瀏覽
    IDC_VW_FRAME 控件類型 Picture Control
    Type Rectangle
    IDC_PLAY_BUTTON 控件類型 Button
    Caption 播放
    IDC_PAUSE_BUTTON 控件類型 Button
    Caption 暫停
    IDCANCEL 控件類型 Button
    Caption 關閉

      5.COM的初始化和卸載,

      修改PlayWnd.cpp添加初始化代碼(加入的代碼用粗黑體表示,下同)

    BOOL CPlayWndApp::InitInstance()
    {
     // 如果一個運行在 Windows XP 上的應用程序清單指定要
     // 使用 ComCtl32.dll 版本 6 或更高版本來啟用可視化方式,
     //則需要 InitCommonControls()。否則,將無法創建窗口。


     InitCommonControls();

     //初始化COM接口

     HRESULT hr = CoInitialize(NULL);

     clearcase/" target="_blank" >cc>if (FAILED(hr))
     {
      TRACE("ERROR - Could not initialize COM library.\n");
      return FALSE;
     }


     CWinApp::InitInstance();

     AfxEnableControlContainer();

      修改PlayWnd.cpp添加卸載COM代碼,注意需要對虛函數ExitInstance進行重載

    int CPlayFileApp::ExitInstance()
    {
     // TODO: 在此添加專用代碼和/或調用基類
     //關閉COM


     CoUninitialize();
     return CWinApp::ExitInstance();
    }


      6.定義媒體控制成員變量

      修改PlayWndDlg.h如下:

    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()

     //和媒體控制相關的成員變量

    private:

     IGraphBuilder *m_pGraph; //IGraphBuilder 接口提供了生成Filter Graph相//關的方法
     IMediaControl *m_pMediaControl;
     //IMediaControl 接口提供了控制流經Filter //Graph數據流的相關方法

     IMediaEventEx *m_pEvent;
     //IMediaEventEx 繼承自IMediaEvent,提供了從
     //Filter Graph 管理器獲取事件消息的方法


     IMediaSeeking *m_pMediaSeeking; //IMediaSeeking 提供了控制流的播放位置和播放//速度的方法
     CString m_strMediaFile; //當前播放的媒體文件的名稱
     BOOL m_isPlaying; //當前的播放狀態

    };

      在CPlayWndDlg的構造函數中添加初始化代碼。

    CPlayWndDlg::CPlayWndDlg(CWnd* pParent /*=NULL*/)
    : CDialog(CPlayWndDlg::IDD, pParent)

    {
     m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);

     m_pGraph = NULL;
     m_pMediaControl = NULL;
     m_pEvent = NULL;
     m_pMediaSeeking = NULL;
     m_strMediaFile = "";
     m_isPlaying = FALSE;


    }

      由于一些和窗體控制有關的初始化代碼不能放在構造函數中進行,我們將其放在CPlayWndDlg::OnInitDialog()中,我們必須在此必須對CPlayWndDlg添加WS_CLIPCHILDREN 的Style,因為在我們的應用中把視頻窗體作為CPlayWndDlg的一個子窗體來使用的,這是非常重要的,許多開發人員在剛開始使用DirectShow時,父窗體的Style沒有設置正確,造成視頻不能正確顯示,代碼如下:

    // 設置此對話框的圖標。當應用程序主窗口不是對話框時,框架將自動
    // 執行此操作


    SetIcon(m_hIcon, TRUE); // 設置大圖標
    SetIcon(m_hIcon, FALSE); // 設置小圖標

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

    ModifyStyle(0, WS_CLIPCHILDREN);
    ((CEdit*)GetDlgItem(IDC_MEDIAFILE_EDIT))->SetReadOnly(TRUE);

    return TRUE; // 除非設置了控件的焦點,否則返回 TRUE

    }

      添加相應的清除代碼,重載CPlayWndDlg的DestoryWindow方法,如下:

    BOOL CPlayWndDlg::DestroyWindow()
    {
     // TODO: 在此添加專用代碼和/或調用基類
     
     if(m_pGraph)
      m_pGraph->Release();
     if(m_pMediaControl)
      m_pMediaControl->Release();
     if(m_pEvent)
      m_pEvent->Release();
     if(m_pMediaSeeking)
      m_pMediaSeeking->Release();

     m_pGraph = NULL;
     m_pMediaControl = NULL;
     m_pEvent = NULL;
     m_pMediaSeeking = NULL;

     return CDialog::DestroyWindow();

    }


      7.修改CPlayWndDlg::OnPaint(),由于現在視頻顯示區域必須由我們自己進行重畫:

    void CPlayWndDlg::OnPaint()
    {
     if (IsIconic())
     {
      CPaintDC dc(this); // 用于繪制的設備上下文
      SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);

      // 使圖標在工作矩形中居中
      
      int cxIcon = GetSystemMetrics(SM_CXICON);
      int cyIcon = GetSystemMetrics(SM_CYICON);
      CRect rect;
      GetClientRect(&rect);
      int x = (rect.Width() - cxIcon + 1) / 2;
      int y = (rect.Height() - cyIcon + 1) / 2;

      // 繪制圖標

      dc.DrawIcon(x, y, m_hIcon);

     }
     else
     {
      if(m_isPlaying == FALSE)
      {
       CClientDC dc(GetDlgItem(IDC_VW_FRAME));
       dc.SetBkColor(RGB(0,0,0));
       CRect rc;
       GetDlgItem(IDC_VW_FRAME)->GetClientRect(rc);
     
       //ClientToScreen(rc);

       dc.FillRect(rc, CBrush::FromHandle((HBRUSH)GetStockObject(BLACK_BRUSH)));
       GetDlgItem(IDC_VW_FRAME)->Invalidate();
      }

      CDialog::OnPaint();
     }
    }

      添加瀏覽、播放、暫停、關閉四按鈕的相應事件響應函數,同時在CPlayWndDlg中添加如下四個私有方法:

    void MoveVideoWindow(void);
    void CleanUp(void);
    BOOL Stop(void);
    BOOL Play(void);

      上述方法的實現如下:

    // IDC_VW_FRAME控件Picture Control主要作用是控制Vedio Window的顯示位置
    void CPlayWndDlg::MoveVideoWindow(void)
    {
     IVideoWindow* pVideoWinow = NULL;
     if(m_pGraph)
     {
      m_pGraph->QueryInterface(IID_IVideoWindow, (void **)&pVideoWinow);
      CRect rc;
      GetDlgItem(IDC_VW_FRAME)->GetWindowRect(rc);
      ScreenToClient(rc);
      pVideoWinow->SetWindowPosition(rc.left, rc.top, rc.Width(), rc.Height());
      pVideoWinow->Release();
      pVideoWinow = NULL;
     }
    }

    void CPlayWndDlg::CleanUp(void)
    {
     long levCode;
     IVideoWindow *pVidWin = NULL;

     if(!m_pGraph)
      return;

     m_pGraph->QueryInterface(IID_IVideoWindow, (void **)&pVidWin);
     m_pEvent->WaitForCompletion(INFINITE, &levCode);

     pVidWin->put_Visible(OAFALSE);
     pVidWin->Release();
     m_pMediaSeeking->Release();
     m_pMediaControl->Release();
     m_pEvent->Release();
     m_pGraph->Release();
     m_pMediaSeeking = NULL;
     m_pMediaControl = NULL;
     m_pEvent = NULL;
     m_pGraph = NULL;

     UpdateData(FALSE);
     CClientDC dc(GetDlgItem(IDC_VW_FRAME));
     dc.SetBkColor(RGB(0,0,0));

     CRect rc;

     GetDlgItem(IDC_VW_FRAME)->GetClientRect(rc);
     ClientToScreen(rc);
     dc.FillRect(rc, CBrush::FromHandle((HBRUSH)GetStockObject(BLACK_BRUSH)));
     Invalidate();
    }

    BOOL CPlayWndDlg::Stop(void)
    {
     IVideoWindow *pVidWin = NULL;
     HRESULT hr;

     if(m_pMediaControl)
     {
      LONGLONG pos = 0;
      hr = m_pMediaControl->Stop();
      hr = m_pMediaSeeking->SetPositions(&pos, AM_SEEKING_AbsolutePositioning ,&pos, AM_SEEKING_NoPositioning);

      m_pGraph->QueryInterface(IID_IVideoWindow, (void **)&pVidWin);
      pVidWin->put_Visible(OAFALSE);

      m_isPlaying = FALSE;
      GetDlgItem(IDC_PLAY_BUTTON)->EnableWindow(TRUE);
      GetDlgItem(IDC_PAUSE_BUTTON)->EnableWindow(FALSE);
      pVidWin->Release();
      long levCode;
      m_pEvent->WaitForCompletion(INFINITE, &levCode);
      m_pMediaControl->Release();

      return TRUE;
     }
     return FALSE;
    }

    BOOL CPlayWndDlg::Play(void)
    {
     // 運行

     IVideoWindow *pVidWin = NULL;

     if(m_pGraph)
     {
      m_pGraph->QueryInterface(IID_IVideoWindow, (void **)&pVidWin);
      pVidWin->put_Visible(OATRUE);
      m_pGraph->QueryInterface(IID_IMediaControl, (void **)&m_pMediaControl);
      m_pMediaControl->Run();
      m_isPlaying = TRUE;
      GetDlgItem(IDC_PLAY_BUTTON)->EnableWindow(FALSE);
      GetDlgItem(IDC_PAUSE_BUTTON)->EnableWindow(TRUE);

      return TRUE;
     }
     return FALSE;
    }


      瀏覽、播放、暫停、關閉四按鈕的相應事件響應函數如下:

    void CPlayWndDlg::OnBnClickedBrowseButton()
    {
     CFileDialog dlgFile(TRUE, NULL, NULL,
     OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT,
     "Movie Files (*.avi;*.mpg;*.mpeg) |\
     *.avi;*.mpg;*.mpeg |\
     Audio Files (*.wav;*mp3;*.mpa;*.mpu;*.au) |\
     *.wav;*.mp3;*.mpa;*.mpu;*.au |\
     Midi Files (*.mid;*.midi;*.rmi) |\
     *.mid;*.midi;*.rmi| | ", this);

     if(dlgFile.DoModal() == IDOK)
     {
      m_strMediaFile = dlgFile.GetPathName();
      GetDlgItem(IDC_MEDIAFILE_EDIT)->SetWindowText(m_strMediaFile);
     }
     else
      return;

     CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC, IID_IGraphBuilder, (void **)&m_pGraph);

     HRESULT hr = m_pGraph->RenderFile(CA2W(m_strMediaFile), NULL);
     
     if(FAILED(hr))
     {
      char szMsg[200];
      AMGetErrorText(hr, szMsg, sizeof(szMsg));
      AfxMessageBox(szMsg);
     }

     //指定父窗體

     IVideoWindow* pVidWin = NULL;
     m_pGraph->QueryInterface(IID_IVideoWindow, (void **)&pVidWin);
     pVidWin->put_Owner((OAHWND)m_hWnd);
     pVidWin->put_WindowStyle(WS_CHILD | WS_CLIPSIBLINGS | WS_CLIPCHILDREN);

     CRect rc;
     
     GetDlgItem(IDC_VW_FRAME)->GetWindowRect(rc);
     ScreenToClient(rc);
     pVidWin->SetWindowPosition(rc.left, rc.top, rc.Width(), rc.Height());

     // 注意此處Filter Graph Manager的事件以WM_GRAPHNOTIFY發出(用戶定義的消息).
     
     m_pGraph->QueryInterface(IID_IMediaEventEx, (void **)&m_pEvent);
     m_pEvent->SetNotifyWindow((OAHWND)m_hWnd, WM_GRAPHNOTIFY, 0);

     // 設置Seeking
     
     m_pGraph->QueryInterface(IID_IMediaSeeking, (void **)&m_pMediaSeeking);

    }


    void CPlayWndDlg::OnBnClickedPlayButton()
    {
     Play();
    }

    void CPlayWndDlg::OnBnClickedPauseButton()
    {
     m_pMediaControl->Pause();
     m_isPlaying = TRUE;

     GetDlgItem(IDC_PLAY_BUTTON)->EnableWindow(TRUE);
     GetDlgItem(IDC_PAUSE_BUTTON)->EnableWindow(FALSE);
    }

    void CPlayWndDlg::OnBnClickedCancel()
    {
     // TODO: 在此添加控件通知處理程序代碼

     CleanUp();
     OnCancel();
    }

      8.添加對WM_GRAPHNOTIFY消息,及其響應函數

      在PlayWndDlg添加消息ID定義:

    #define WM_GRAPHNOTIFY WM_USER + 101

      在PlayWndDlg.h中,代碼如下:

    // 實現

    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()

     afx_msg HRESULT OnGraphNotify(WPARAM wParam,LPARAM lParam);

    private:
     
     IGraphBuilder *m_pGraph;
     IMediaControl *m_pMediaControl;
     IMediaEventEx *m_pEvent;

      PlayWndDlg.cpp,如下:

     ON_BN_CLICKED(IDC_BROWSE_BUTTON, OnBnClickedBrowseButton)
     ON_BN_CLICKED(IDC_PLAY_BUTTON, OnBnClickedPlayButton)
     ON_BN_CLICKED(IDC_PAUSE_BUTTON, OnBnClickedPauseButton)
     ON_BN_CLICKED(IDCANCEL, OnBnClickedCancel)
     ON_MESSAGE(WM_GRAPHNOTIFY, OnGraphNotify)

    END_MESSAGE_MAP()

      實現如下:

    HRESULT CPlayWndDlg::OnGraphNotify(WPARAM wParam,LPARAM lParam)
    {
     long levCode, lparam1, lparam2;
     HRESULT hr;

     while (hr = m_pEvent->GetEvent(&levCode, &lparam1, &lparam2, 0), SUCCEEDED(hr))
     {
      hr = m_pEvent->FreeEventParams(levCode, lparam1, lparam2);
      if ((EC_COMPLETE == levCode) || (EC_USERABORT == levCode))
      {
       TRACE("End of the media file!!.\n");
       Stop();

       //CleanUp();

       break;
      }
     }
     return hr;
    }

    原文轉自:http://www.kjueaiud.com

    評論列表(網友評論僅供網友表達個人看法,并不表明本站同意其觀點或證實其描述)
    老湿亚洲永久精品ww47香蕉图片_日韩欧美中文字幕北美法律_国产AV永久无码天堂影院_久久婷婷综合色丁香五月

  • <ruby id="5koa6"></ruby>
    <ruby id="5koa6"><option id="5koa6"><thead id="5koa6"></thead></option></ruby>

    <progress id="5koa6"></progress>

  • <strong id="5koa6"></strong>