DirectX組件之---開始DirectShow旅程

2. 開始DirectShow旅程
    這個章節的內容主要是編寫DirectShow應用所需的一些基本概念,可以把它當作一個高級介紹,理解這些內容只需具備一般的編程和有關多媒體的知識。
2.1. 設置DirectShow開發的編譯環境
    這節內容描述瞭如何來編譯DirectShow應用。你可以使用命令行形式來編譯一個工程,也可以在Microsoft Visual Studio集成環境下(包含VC++)實現。
    頭文件:
    所有的DirectShow應用都需要Dshow.h這個頭文件,某些DirectShow接口需要附加的頭文件,參考接口的說明視具體情況定。
    庫文件:
    DirectShow使用以下庫文件:
    Strmiids.lib 輸出類標識(CLSID)和接口標識(IID),所有DirectShow應用均需此庫。
    Quartz.lib   輸出AMGetErrorText函數,如果不調用此函數,此庫不是必需的。
    有了以上這些頭文件和庫文件,你已經可以編寫DirectShow應用了,但是微軟建議使用DirectShow基類庫來編寫filter,這樣可以大大減少程序編寫的工作量。要使用DirectShow基類庫,需要先編譯它,基類庫位於SDK的Samples\Multimedia\DirectShow\BaseClasses文件夾下,包含兩個版本的庫:發佈版(retail version)Strmbase.lib和調試版(debug version)Strmbasd.lib。具體參見"創建DirectShow Filter"一節。
2.2. DirectShow應用程序編程簡介
    這節介紹DirectShow用到的一些基本術語和概念,看完這節後,你將能夠編寫你的第一個DirectShow應用程序。
    Filter和Filter Graph
    一個DirectShow應用程序是由一個個稱爲filter的軟件構件組合而成的,filter執行一些多媒體流的操作,如:讀文件、從視頻採集設備中獲得視頻、將不同的格式的流解碼如MPEG1、將數據送到圖形卡或聲卡中去。
    Filter接收輸入併產生輸出。舉個例子,一個解碼MPEG1視頻流的filter,輸入MPEG1格式的視頻流,輸出一系列未壓縮的視頻幀。
    在DirectShow中,應用程序要實現功能就必須將這些filter鏈接在一起,因而一個filter的輸出就變成了另一個filter的輸入。這一系列串在一起的filter稱爲filter graph。例如,下圖就顯示了一個播放avi文件的filter graph:

    File Source(Async) filter從硬盤中讀取avi文件;AVI Splitter filter分析文件並將其分解成兩個流:一個壓縮的視頻流和一個音頻流;AVI Decompressor filter將視頻幀解碼,Video Renderer filter將解碼後的視頻幀通過DirectDraw或GDI顯示出來;Default DirectSound Device filter使用DirectSound播放音頻流。
    應用程序沒有必要對這些數據流進行管理,而是通過一個叫Filter Graph Manager這個上層組件來控制這些filter。應用程序調用上層API如"Run"(通過graph移動數據)或"Stop"(停止移動數據)。如果你需要對數據流作更多的操作,你可以通過COM接口直接進入filter。Filter Graph Manager同樣也輸出事件通知給應用程序。
    Filter Graph的另一個用途是將filter連在一起創建一個filter graph。
    編寫一個DirectShow應用程序大體需要三個步驟:
    1.創建一個Filter Graph Manager的實例
    2.使用Filter Graph Manager創建一個filter graph,此時,需要已經具備所有必需的filter。
    3.使用Filter Graph Manager控制filter graph和通過這些filter的流,在這個過程中,應用程序會收到Filter Graph Manager發送的事件。
    完成這些後,應用程序需發佈這個Filter Graph Manager和所有的filter。
2.3. 播放一個文件
    這一章以本節這個有趣的例子來結束,這個例子是一個播放音頻或視頻文件的簡單控制檯程序。程序只有寥寥數行,但卻展示了DirectShow編程的強大能力。
    正如上一節所講的創建DirectShow應用程序的三個步驟,第一步,首先,需要調用CoInitialize來作初始化,然後調用CoCreateInstance創建Filter Graph Manager:
 

    HRESULT hr = CoInitialize(NULL);
    if (FAILED(hr))
    {
        return;
    }


    IGraphBuilder *pGraph;
    HRESULT hr = CoCreateInstance(CLSID_FilterGraph, NULL,
        CLSCTX_INPROC_SERVER, IID_IGraphBuilder, (void **)&pGraph);

     
    如上所示,類標識符(CLSID)是CLSID_FilterGraph。Filter Graph Manager由進程內DLL(in-process DLL)提供,因此參數3,dwClsContext的值爲CLSCTX_INPROC_SERVER。由於DirectShow運行自由線程模式(free-threading model),所以你同樣可以使用COINIT_MULTITHREADED參數來調用CoInitializeEx。
    第二步是創建filter graph,調用CoCreateInstance得到的IGraphBuilder接口包含了大部分創建filter graph的方法。在這個例子中還需要另外兩個接口:IMediaControl和IMediaEvent。
    IMediaControl控制數據流,它包含開啓和停止graph的方法;IMediaEvent包含從Filter Graph Manager獲取事件的方法,在這個例子中,這個接口用來得到回放結束事件。
    所有這些接口由Filter Graph Manager提供,使用得到的IGraphBuiler接口指針來查詢得到。

    IMediaControl *pControl;
    IMediaEvent   *pEvent;
    hr = pGraph->QueryInterface(IID_IMediaControl, (void **)&pControl);
    hr = pGraph->QueryInterface(IID_IMediaEvent, (void **)&pEvent);


    現在你可以創建filter graph了,對於文件回放只需要一個簡單的調用:

   hr = pGraph->RenderFile(L"C:\\Example.avi", NULL);

  
    IGraphBuilder::RenderFile方法創建了一個能夠播放指定文件的filter graph,事實上,原本需要做的一些如創建filter實例及將這些filter連接起來的工作,都由這個方法自動完成了,如果是視頻文件,這個filter graph看起來應該是這個樣子:
    [file source]->[如果是縮格式,這裏是個解碼器]->[Video Renderer]
    要開始回放,調用IMediaControl::Run方法:

      hr = pControl->Run();



    當filter graph運行時,數據經過各個filter最後回放爲視頻或音頻。回放發生在一個單獨的線程中。你可以通過調用IMediaEvent::WaitForCompletion方法來等待回放的結束:

      long evCode = 0;
    pEvent->WaitForCompletion(INFINITE, &evCode);



    這個方法在播放期間被阻塞,直至播放結束或超時。
    當應用程序結束時,需要釋放接口指針並關閉COM庫:

     pControl->Release();
    pEvent->Release();
    pGraph->Release();
    CoUninitialize();

    下面是這個例子的完整代碼:
   
#include <dshow.h>
void main(void)
{
    IGraphBuilder *pGraph = NULL;
    IMediaControl *pControl = NULL;
    IMediaEvent   *pEvent = NULL;

    // Initialize the COM library.
    HRESULT hr = CoInitialize(NULL);
    if (FAILED(hr))
    {
        printf("ERROR - Could not initialize COM library");
        return;
    }

    // Create the filter graph manager and query for interfaces.
    hr = CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER,
                        IID_IGraphBuilder, (void **)&pGraph);
    if (FAILED(hr))
    {
        printf("ERROR - Could not create the Filter Graph Manager.");
        return;
    }

    hr = pGraph->QueryInterface(IID_IMediaControl, (void **)&pControl);
    hr = pGraph->QueryInterface(IID_IMediaEvent, (void **)&pEvent);

    // Build the graph. IMPORTANT: Change this string to a file on your system.
    hr = pGraph->RenderFile(L"C:\\Example.avi", NULL);
    if (SUCCEEDED(hr))
    {
        // Run the graph.
        hr = pControl->Run();
        if (SUCCEEDED(hr))
        {
            // Wait for completion.
            long evCode;
            pEvent->WaitForCompletion(INFINITE, &evCode);

            // Note: Do not use INFINITE in a real application, because it
            // can block indefinitely.
        }
    }
    pControl->Release();
    pEvent->Release();
    pGraph->Release();
    CoUninitialize();
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章