用DirectShow設計媒體播放器2004-07-31 12:02作者:唐芸 王煜堅 吳鎮揚出處:ahcit責任編輯:方舟
摘要:DirectShow技術是DirectX推出的建立在DirectDraw和DirectSound組件基礎之上的多媒體應用程式開發套件,它提供對多媒體資料流的高品質捕獲和回放,代表著未來多媒體應用程式開發的方向。詳細介紹了DirectShow的系統組成,並討論了利用DirectShow在Visual C++ 6.0平台上開發簡單媒體播放器的關鍵步驟。
關鍵字:DirectShow;COM;過濾器;引腳;過濾器圖表管理器
DirectX簡介
DirectX是Microsoft公司為遊戲和其他高效能多媒體應用所提供的一套底層API。這些介面包括對二維和三維圖形,聲效和音樂,輸入裝置以及多玩家網路遊戲等的支援。目前DirectX的最高版本是DirectX 9.0。
1 DirectX的組成
DirectX 9.0由下列組件構成:
(1)DirectX Graphics:該組件組合DirectX舊版本中的DirectDraw和Direct3D兩個組件,使其成為一個適用於所有圖形程式的單獨的應用程式介面。其中的Direct3D擴充(D3DX)應用程式庫簡化了多數圖形程式的工作。
(2)DirectInput:支援各種輸入裝置,完全支援力反饋技術。
(3)DirectPlay:支援多玩家網路遊戲。
(4)DirectSound:支援用於播放和捕獲音訊波形的高效能音頻應用軟體的開發。
(5)DirectMusic:為音樂音軌以及基于波表、MIDI(Musical Instrument Devices Interface)或其他由DirectMusic Producer創作的非音樂音軌,提供了一套完整的解決方案。
(6)DirectShow:提供對多媒體資料流的高品質捕獲和回放。
(7)DirectSetup:一個簡單的應用程式介面,提供DirectX組件的自動安裝。
(8)DirectX Media Objects:提供對資料流對象的讀寫支援,包括視頻和音訊轉碼器及其效果。
2 COM簡介
DirectX的功能都是以COM組件的形式提供的。COM是元件物件模型(Component Object Model)的簡寫,它是一種協議,是對象串連和嵌入(Object Linking and Embedding)的基礎。COM通常以動態連結程式庫(DLL)的形式存在,它是建立在二進位規範上的對象。COM定義並實現了軟組件(如應用程式、資料對象、控制項及服務)機制,並把他們統稱為“對象”。每個軟組件對象由資料以及訪問資料的函數組成,訪問軟組件對象資料的函數集合稱為“介面”。在應用程式看來COM是一個黑箱,可調用COM提供的方法但不知道它的具體實現。在使用DirectShow編程時,使用者建立的自訂群組件必須以COM形式實現,所以必須知道如何?COM,而一般的應用程式只需要瞭解COM的介面和用法就可以了
DirectShow的系統組成
DirectShow技術是建立在DirectDraw和DirectSound組件基礎之上的,它通過DirectDraw對顯卡進行控制以顯示視頻,通過DirectSound對音效卡進行控制以播放聲音。 DirectShow可提供高品質的多媒體流的捕獲和回放功能;支援多種媒體格式,包括ASF(Advanced Systems Format),MPEG(Motion Picture Experts Group),AVI(Audio-Video Interleaved),MP3(MPEG Audio Layer-3)和WAV音效檔;可以從硬體上捕獲媒體資料流;可以自動檢測並使用視頻和音頻加速硬體。因此,DirectShow可以充分發揮媒體的效能,提高運行速度,可以簡化媒體播放、媒體間的格式轉換和媒體捕獲等工作。同時,它還具有極大的可擴充性和靈活性,可以由使用者自己建立組件,並將這個組件加入DirectShow結構中以支援新的格式或特殊的效果。
應用程式與DirectShow組件以及DirectShow所支援的軟硬體之間的關係1所示。
1、過濾器(filter)
由圖1可以看到,過濾器是DirectShow最基本的組成元件。過濾器是一個COM組件,是完成DirectShow處理過程的基本單元。DirectShow提供了一組標準的過濾器供應用程式使用,程式開發人員也可以建立自訂的過濾器來擴充DirectShow的功能,但必須是以COM形式建立的。DirectX為使用者提供了DirectShow基底類別庫(DirectShow Base Class Library),使用者自訂的過濾器都可以從基底類別庫提供的基類和介面派生出來。
過濾器主要分為以下幾種類型:
(1)源過濾器(source filter):源過濾器引入資料到過濾器圖表中,資料來源可以是檔案、網路、照相機等。不同的源過濾器處理不同類型的資料來源。
(2)變換過濾器(transform filter):變換過濾器的工作是擷取輸入資料流,處理資料,並產生輸出資料流。變換過濾器對資料的處理包括編解碼、格式轉換、壓縮解壓縮等。
(3)提交過濾器(renderer filter):提交過濾器在過濾器圖表裡處於最後一級,它們接收資料並把資料提交給外設。
(4)分割過濾器(splitter filter):分割過濾器把輸入資料流分割成多個輸出。例如,AVI分割過濾器把一個AVI格式的位元組流分割成視頻流和音頻流。
(5)混合過濾器(mux filter):混合過濾器把多個輸入組合成一個單獨的資料流。例如,AVI混合過濾器把視頻流和音頻流合成一個AVI格式的位元組流。
過濾器的這些分類並不是絕對的,例如一個ASF讀過濾器(ASF Reader filter)既是一個源過濾器又是一個分割過濾器。
在DirectShow裡,一組過濾器稱為一個過濾器圖表(filter graph)。過濾器圖表用來串連過濾器以控制媒體流,它也可以將資料返回給應用程式,並搜尋所支援的過濾器。過濾器有三種可能的狀態:運行、停止和暫停。暫停是一種中間狀態,停止狀態到運行狀態必定經過暫停狀態。暫停可以理解為資料就緒狀態,是為了快速切換到運行狀態而設計的。在暫停狀態下,資料線程是啟動的,但被提交過濾器阻塞了。通常情況下,過濾器圖表中所有過濾器的狀態是一致的。
2、引腳(pin)
過濾器可以和一個或多個過濾器相連,串連的介面也是COM形式的,稱為引腳。過濾器利用引腳在各個過濾器間傳輸資料。每個引腳都是從Ipin這個COM對象派生出來的。每個引腳都是過濾器的私人對象,過濾器可以動態建立引腳,銷毀引腳,自由控制引腳的存留時間。引腳可以分為輸入引腳(Input pin)和輸出引腳(Output pin)兩種類型,兩個相連的引腳必須是不同種類的,即輸入引腳只能和輸出引腳相連,且串連的方向總是從輸出引腳指向輸入引腳。
過濾器之間的串連(也就是引腳之間的串連),實際上是串連雙方媒體類型(Media Type)協商的過程。串連的大致過程為:如果調用串連函數時已經指定了完整的媒體類型,則用這個媒體類型進行串連,成功與否都結束串連過程;如果沒有指定或不完全指定了媒體類型,則進入下面的枚舉過程——枚舉欲串連的輸入引腳上所有的媒體類型,逐一用這些媒體類型與輸出引腳進行串連(如果串連函數提供了不完全媒體類型,則要先將每個枚舉出來的媒體類型與它進行匹配檢查),如果輸出引腳也接受這種媒體類型,則引腳之間的串連宣告成功;如果所有輸入引腳上枚舉的媒體類型,輸出引腳都不支援,則枚舉輸出引腳上的所有媒體類型,並逐一用這些媒體類型與輸入引腳進行串連,如果輸入引腳接受其中的一種媒體類型,則引腳之間的串連宣告成功;如果輸出引腳上的所有媒體類型,輸入引腳都不支援,則這兩個引腳之間的串連過程宣告失敗。過濾器與引腳串連2所示。
3、媒體類型(Media Type)
媒體類型是描述數位媒體格式的一種通用的可擴充方式。兩個過濾器相連時,必須使用一致的媒體類型,否則這兩個過濾器就不能相連。媒體類型能識別上一級過濾器傳送給下一級過濾器的資料類型,並對資料進行分類。
實際在很多應用程式中,使用者根本不需要擔心媒體類型的問題,DirectShow會處理好所有的細節。但有些應用程式需要對媒體類型進行操作。媒體類型一般可以有兩種表示:AM_MEDIA_TYPE和CMediaType。前者是一個結構,後者是從這個結構繼承過來的類。
每個AM_MEDIA_TYPE由三部分組成:Major type、Subtype和Format type。這三個部分都使用GUID(通用唯一識別碼)來唯一標示。Major type主要定性描述一種媒體類型,這種媒體類型可以是視頻、音頻、位元資料流或MIDI資料等等;Subtype進一步細化媒體類型,如果是視頻的話可以進一步指定是RGB-24,還是RGB-32,或是UYVY等等;Format type則用一個結構更進一步細化媒體類型。
如果媒體類型的三個部分都指定了某個具體的GUID值,則稱這個媒體類型是完全指定的;如果媒體類型的三個部分中有任何一個值是GUID_NULL,則稱這個媒體類型是不完全指定的。GUID_NULL具有萬用字元的作用。
4、過濾器圖表管理器(Filter Graph Manager)
DirectShow通過過濾器圖表管理器來控制過濾器圖表中的過濾器。過濾器圖表管理器是COM 形式的,它的功能有:協調過濾器間的狀態轉變;建立參考時鐘;把事件(event)傳送給應用程式;為應用程式提供建立過濾器圖表的方法。
一些常用的過濾器圖表管理器介面如下:
IGraphBuilder:為應用程式提供建立過濾器圖表的方法。
IMediaControl:提供控制過濾器圖表中多媒體資料流的方法,包括運行、暫停和停止。
IMediaEventEx:繼承自IMediaEvent介面,處理過濾器圖表的事件。
IVideoWindow:用於設定多媒體播放器視窗的屬性,應用程式可以用它來設定視窗的所有者、位置和尺寸等屬性。
IBasicAudio:用於控制音頻流的音量和平衡。
IBasicVideo:用於設定視頻特性,如視頻顯示的目的地區和來源區域。
IMediaSeeking:提供搜尋資料流位置和設定播放速率的方法。
IMediaPosition:用於尋找資料流的位置。
IVideoFrameStep:用於步進播放視頻流,可使DirectShow應用程式,包括DVD播放器一次只播放一幀視頻。
5、過濾器圖表中的資料流動
當使用者要建立自訂的過濾器時,就需要瞭解媒體資料是如何在過濾器圖表中傳輸的。為了在過濾器圖表中傳送媒體資料,DirectShow過濾器需要支援一些協議,稱之為傳輸協議(transport)。相連的過濾器必須支援同樣的傳輸協議,否則不能交換媒體資料。
大多數的DirectShow過濾器把媒體資料儲存在主儲存空間中,並通過引腳把資料提交給其它的過濾器,這種傳輸稱為局部儲存空間傳輸(local memory transport)。雖然局部儲存空間傳輸在DirectShow中最常用,但並不是所有的過濾器都使用它。例如,有些過濾器通過硬體傳送媒體資料,引腳只是用來提交控制資訊,如IOverlay介面。
DirectShow為局部儲存空間傳輸定義了兩種機制:推模式(push model)和拉模式(pull model)。在推模式中,源過濾器產生資料並提交給下一級過濾器。下一級過濾器被動的接收資料,完成處理後再傳送給再下一級過濾器。在拉模式中,源過濾器與一個分析過濾器相連。分析過濾器向源過濾器請求資料後,源過濾器才傳送資料以響應請求。推模式使用的是IMemInputPin介面,拉模式使用IAsyncReader介面,推模式比拉模式要更常用。
利用DirectShow開發簡單媒體播放器
本節介紹基於DirectShow開發簡單媒體播放器的關鍵步驟。
1、初始化DirectShow
由於DirectShow的組件都是以COM形式存在的,因此首先要調用CoInitializeEx函數來初始化COM庫,嵌入所有的動態連結程式庫和資源。否則,所有對QueryInterface的調用都會失敗。
2、建立過濾器圖表管理器介面
首先申明並初始化所需的介面:
// DirectShow interfaces IGraphBuilder *pGB = NULL; IMediaControl *pMC = NULL; IMediaEventEx *pME = NULL; IVideoWindow *pVW = NULL; IBasicAudio *pBA = NULL; IBasicVideo *pBV = NULL; IMediaSeeking *pMS = NULL; IMediaPosition *pMP = NULL; IVideoFrameStep *pFS = NULL; 然後執行個體化一個過濾器圖表管理器,並查詢各介面: // Get the interface for DirectShow's GraphBuilder CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, IID_IGraphBuilder, (void **)&pGB); // QueryInterface for DirectShow interfaces pGB->QueryInterface(IID_IMediaControl, (void **)&pMC); pGB->QueryInterface(IID_IMediaEventEx, (void **)&pME); pGB->QueryInterface(IID_IMediaSeeking, (void **)&pMS); pGB->QueryInterface(IID_IMediaPosition, (void **)&pMP); // Query for video interfaces, which may not be relevant for audio files pGB->QueryInterface(IID_IVideoWindow, (void **)&pVW); pGB->QueryInterface(IID_IBasicVideo, (void **)&pBV); // Query for audio interfaces, which may not be relevant for video-only files pGB->QueryInterface(IID_IBasicAudio, (void **)&pBA); |
3、建立過濾器圖表
應用DirectShow建立過濾器圖表時,使用者完全不需要操心系統使用了哪一類過濾器以及過濾器是怎樣串連的。只要調用IGraphBuilder::RenderFile函數,就可以建成一個完整的過濾器圖表。
// Have the graph builder construct its the appropriate graph automatically pGB->RenderFile(wFile, NULL); |
建立成功後,過濾器圖表就可以用來播放多媒體檔案了。DirectShow調用IMediaControl::Run函數來播放媒體檔案。
// Run the graph to play the media file pMC->Run(); |
4、使用DirectShow的事件響應機制
DirectShow的事件響應機制是過濾器圖表管理器與使用者進行互動的介面,DirectShow處理的可以是一些事先可以預期的事件,比如資料流的結束;也可以是一些無法預期的錯誤。有的事件可以由過濾器圖表管理器自己處理,但如果過濾器圖表管理器自己無法處理這些事件,它就把事件的通知放在事件隊列裡。使用者程式就可以通過IMediaEventEx介面得到事件,並對它做出相應的處理。
5、清除DirectShow
在程式結束時必須調用Release函數釋放DirectShow的介面指標,並調用CoUninitialize函數來卸載COM庫,釋放所有的動態連結程式庫和資源。
結束語
應用DirectX的組件DirectShow進行多媒體應用程式的開發需瞭解多方面的知識,但在很多應用中利用DirectShow的特性可以減少工作量並能獲得非常高的運行效率。在Visual C++ 6.0的開發環境中利用DirectShow開發的簡單媒體播放器,具有全部隨機播放、暫停和調整播放速率等功能,且可以播放多種媒體檔案,播放效果非常流暢。因此,基於DirectShow開發多媒體應用程式的方法簡單高效,是一種值得推薦的方法。