這個章節的內容主要是編寫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);
|
如上所示,類標識符(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. // Create the filter graph manager and query for interfaces. hr = pGraph->QueryInterface(IID_IMediaControl, (void **)&pControl); // Build the graph. IMPORTANT: Change this string to a file on your system. // Note: Do not use INFINITE in a real application, because it |