DirectShow SDK筆記【關於DirectShow(2)】

       2.5    How Hardware Devices Participate in the Filter Graph
       本節描述DirectShow是如何與音頻、視頻設備進行交互。
 
       2.5.1 Wrapper Filters
       所有的DirectShow Filters都是用戶組件模式的軟件組件。爲了把內核模式的硬件設備,比如視頻捕捉卡等加入倒Filter Graph,設備必須被表示爲用戶模式Filter。這項功能是通過DirectShow提供的包裝Filters來實現的。這些Filters包括Audio Capture Filters, VFW Capture Filters, TV Tuner Filter, TV Audio FilterAnalog Video Crossbar Filter. DirectShow也提供了稱爲KsProxyFilter用來描述任何類型的WDM流設備。硬件供應商可以提供聚集了KsProxyCOM對象KsProxy Plug-in, 以此擴展KsProxy來支持自定義功能,
       這些包裝Filters暴露了描述設備功能的接口。應用程序使用這些接口與Filter交互數據。FilterCOM函數調用翻譯爲設備驅動調用,把信息傳遞到內核的驅動,然後再把結果轉化給應用程序。TV Tuner, TV Audio, Analog Video CrossbarKsProxy通過IKsPropertySet接口支持自定義驅動屬性。VFW Capture FilterAudio Capture Filter並沒有這種擴展。
       對應用程序開發人員,包裝Filter允許應用程序控制設備像控制其他DirectShow Filter一樣。我們不需要特殊的編程,這些與內核模式的通信細節都在Filter中封裝起來。
 
       2.5.2 Video for Windows Devices
       VFW Capture Filter支持早期的VFW捕捉卡。當目標系統上存在VFW卡時,它可以被發現,然後通過DirectShowSystem Device Enumerator添加到Filter Graph。細節可參考Enumerating Devices and Filters.
 
 
       2.5.3 Audio Capture and Mixing Devices (Sound Cards)
       新一代的聲卡都有麥克風和其他類型設備的輸入娛樂工具。通常這些聲卡還有控制每個輸入音量、立體音和重音的實時混合功能。在DirectShow中,聲卡的輸入和混合器都被Audio Capture Filter包裝。每個聲卡都能被System Device Enumerator發現。查看系統的聲卡,運行GraphEdit並選擇Audio Capture Source種類。屬於此類的每個Filter都是Audio Capture Filter的一個單獨實例。(參考Using GraphEdit).
 
       2.5.4 WDM Streaming Devices
     新一代硬件解碼器和捕捉卡都與WDM規範一致。這些設備的功能比VFW設備強,可以在Windows NT/2KWindows 98/ME移植。WDM視頻捕捉卡可以支持VFW不能支持的特徵,包括枚舉捕捉格式,編程控制視頻參數,比如色調和亮度,編程選擇輸入端和TV Tuner支持。
     爲了支持WDM流設備,DirectShow提供了KsProxy Filter(ksproxy.ax)KsProxy也稱爲“Swiss Army Knife Filter”因爲它的實現的功能很多。此FilterPINS數量、暴露的COM接口數量,都取決於內在驅動的性能。KsProxyFilter Graph中不以名稱KsProxy出現。而總是採用設備的Friendly Name, 可以通過註冊表讀取。查看系統的WDM設備,運行GraphEdit選擇WDM Streaming種類。即使系統只有一個WDM卡,它也可能包括多個設備。每個設備是以不同的Filter表示。每個Filter實際上都是KsProxy.
       應用程序使用System Device Enumerator查找系統的WDM設備MonikersKsProxy被函數BindToObject實例化。因爲KsProxy可以表示所有的WDM設備。它必須查詢驅動來決定驅動支持那種屬性集。屬性集是WDM驅動使用的數據結構集合,也被一些用戶模式的Filter使用。KsProxyCOM調用轉譯爲屬性集併發送給驅動。硬件供應商可以提供Plug-in擴展KsProxyPlug-in是硬件供應商指定暴露、實現特殊設備功能的接口。所有這些都從應用程序隱藏。應用程序通過KsProxy控制設備像控制其他DirectShow Filter一樣。
 
       2.5.5 Kernel Streaming
       WDM設備支持內核流,這當中所有的數據都是在內核模式流動。永遠也不需要轉換到用戶模式。在內核模式和用戶模式切換的計算代價很高。內核模式允許高位率的流而不加重CPU負擔。基於WDMFilter可使用內核流把多媒體數據從一個設備傳遞到另一個設備,可以是相同卡也可是不同的設備卡,不需要把數據拷貝到系統內存。
       從應用程序角度看,數據好像是從一個用戶模式Filter移動到下一個。而實際上,數據可能根本沒有進入用戶模式,而是從一個內核設備直接流向另一個設備,直到被提交到圖形卡。對於某些情況,比如捕捉到文件,某些時候要求數據從內核模式傳遞到用戶模式。但是這樣的交換並不必要求數據被拷貝到內存的新位置。應用程序開發人員一般不需要關心內核流的細節,除了作爲背景信息。參考Microsoft DDK的關於WDM, 內核流和KsProxy的相關主題。
 
3.     Building the Filter Graph
       The Filter Graph and Its Component一節描述了建立DirectShow Filter Graph的基本組件。本節檢查這些組件是如何創建、連接在一起開始處理數據的。
 
       3.1    Graph-Building Components
       DirectShow提供了組件用來建立Filter Graph。包括:
       ·Filter Graph Manager. 它控制Filter Graph. 支持IGraphBuiler, IMediaControl, IMediaEventEx及其他接口。所有的DirectShow應用程序在某些時候都使用此對象,儘管在某些情況下Filter Graph Manager是由其他對象創建的。
       ·Capture Graph Builder. 提供建立Filter Graphs的額外方法。它最開始是設計來創建執行視頻捕捉的Graph(從名稱看也是),但是對一些其他類型的自定義Filter Graph也有用。它支持接口ICaptureGraphBuilder2.
       ·Filter MapperSystem Device Enumerator. 它們定位註冊在用戶系統上的Filters. 或者表示硬件設備。
       ·DVD Graph Builder. 建立DVD回放和導航的Filter Graph. 支持IDvdGraphBuilder接口。基於腳本的應用程序可使用MSWebDVD ActiveX控制進行DVD回放。
       ·視頻控制。此ActiveX控制在Windows XP上可用。它在DirectShow中處理數字和模擬電視。更多信息可參考Using the Video Control.
 
       3.1.1 Intelligent Connect
       術語“Intelligent Connect”覆蓋了Filter Graph Manager用來建立全部、部分Filter Graph的一個算法集。在Filter Graph Manager需要其他Filters來完成Graph的任何時候,粗略按照如下步驟進行:
       (1)、如果FilterGraph中,並且至少有一個未連接的PINFilter Graph Manager就嘗試使用這個Filter
       (2)、否則Filter Graph Manager在註冊表查詢能接收媒體類型連接的Filter。每個Filter在註冊表有一個Merit值。它能粗略說明在完成GraphFilter可能有多大的用處。Filter Graph Manager根據Merit值的順序進行嘗試。每種流類型(比如音頻、視頻或者MIDI),默認的Renderer都有一個較高的Merit值。解碼器的Merit值也比較高。特殊目的Filter的值比較低。
     如果Filter Graph Manager遇到困難,就返回嘗試不同Filters的組合。我們可從Intelligent Gonnect主題找到具體細節。
 
       3.2    Overview of Graph Building
       創建Filter Graph,先創建Filter Graph Manager實例:
IGraphBuilder* pIGB;
HRESULT hr = CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER,             IID_IGraphBuilder, (void **)&pIGB);
       Filter Graph Manager支持如下的Graph建立方法:
       ·IFilterGraph::ConnectDirect直接在兩個PINS上嘗試連接。若PIN不能連接,函數失敗。
       ·IFilterGraph::Connect連接兩個PINS,如果可能先進行直接連接。否則使用中介Filter完成連接。
       ·IGraphBuilder::Render從某輸出PIN開,建立Graph剩餘部分。此函數在下一級添加必要的Filter直到到達Renderer Filter.
       ·IFilterGraph::AddFilterFilter添加到Graph. 並不連接Filter.必須在調用函數前創建Filter
       這些方法提供三種基本的方法建立Graph:
       (1)Filter Graph Manager建立整個Graph
       (2)Filter Graph Manager建立部分Graph
       (3)、應用程序建立整個Graph
 
       3.2.1 The Filter Graph Manager builds the entire graph
       如果我們想簡單播放的已經格式創建的文件,比如AVI, MPEG, WAVMP3, 使用RenderFile函數。How To Play a File一節說明了具體如何操作。
       RendererFile開始在註冊表搜索能解析文件的Source Filter。它使用協議(比如文件名稱中的http://), 文件擴展名,或者文件中的預定義字節模式來決定使用哪個Source Filter。詳細信息可參考Registering a Custom File Type.
       在建立Graph剩餘部分時,Filter Graph Manager使用一種交互過程。交互時用Filter輸出PIN支持的媒體類型,然後在註冊表查找可以用此類型做輸入的Filter。它使用幾個標準來加快搜索和決定Filters的使用次序:
       ·Filter Category標示了Filter的基本功能
       ·媒體類型描述了Filter可接受的輸入或者發送的輸出格式
       ·Merit值決定了Filter被嘗試使用的順序。如果兩個Filter屬於相同的Category,支持相同的輸入類型,Filter Graph Manager就選擇Merit值高的一個。某些FilterMerit設置得比較低,因爲它們通常用於特殊用途,應該由應用程序把它們加入到Graph
       Filter Graph Manager使用Filter Mapper來查找註冊表。
       只要添加了每個Filter後,Filter Graph Manager開始把它與前一個Filter的輸出PIN連接。Filter進行協商來決定它們是否連接。如果是,再決定連接採用的媒體類型。如果新Filter不能連接。Filter Graph Manager丟棄它並嘗試其他Filter。這個過程會一直持續下去直到每個流都被提交。
 
       3.2.2 The Filter Graph Manager builds part of the graph
       當我們不止想簡單播放文件時,應用程序必須至少執行一些Graph的建立工作。比如視頻捕捉應用程序必須選擇捕捉Source Filter並加入Graph。如果把數據寫入到AVI文件,必須添加AVI MuxFile Writer FilterGraph中。但是,讓Filter Graph Manager來完成Graph也是可能的。比如我們可以調用Render函數提交PIN進行預覽。
 
       3.2.3 The Application builds the entire graph
       在某些時候,我們的應用程序需要通過添加或者連接每個Filter來建立Graph。這時,我們應該知道哪個Filter應該被加入到Graph。使用這種方法,應用程序調用AddFilter來添加Filter。枚舉FilterPIN,調用ConnectConnectDirect來連接它們。
 
       3.3    Intelligent Connect
       智能連接是Filter Graph Manager建立Graph的機制。它包含幾個選擇Filter、把Filter添加到Filter Graph的算法。對於應用程序編程,我們很少需要知道智能連接的細節。如果你建立某些Filter有困難,並且想解決問題時,或者是編寫自己的Filter並且允許支持自動Graph建立,可閱讀此節。
       智能連接主要涉及到如下幾個IGraphBuilder的函數:
       ·IGraphBuilder::Render
       ·IGraphBuilder::AddSourceFiler
       ·IGraphBuilder::RenderFile
       ·IGraphBuilder::Connect
       Render函數建立部分Graph.它從一個未連接的輸出PIN開始,並向下工作,必要時添加Filter.開始的Filter必須已經在Graph中。每一步,Render函數都查找可以連接上一級FilterFilter。數據流可以分支,如果連接Filter有多個輸出PIN。直到每個流都有提交查找才停止。如果Render遇到困難,它可能返回用不同的Filter進行再次的嘗試。
       爲了連接每個輸出PINRender函數執行如下工作:
       (1)、如果PIN支持ISteamBuilder接口,Filter Graph Manager就委派給IStreamBuilderRender函數。通過暴露此接口,PIN就被假定承擔建立Graph剩餘部分,一直到Renderer. 但是很少有PIN支持這個接口。
       (2)Filter Graph Manager嘗試使用緩存在內存的Filter。如果有,貫穿整個智能連接過程,Filter Graph Manager可能緩存這個Filter開始處理的每一步的Filter.(參考Dynamic Graph Building)
       (3)、如果Filter Graph中某個Filter有未連接輸出PINFilter Graph Manager下一步就嘗試它們。我們可以在調用Render之前,把某個特殊Filter添加到Graph,以此來強制Render函數嘗試使用它。
       (4)、最後,Filter Graph Manager調用IFilterMapper2::EnumMatchingFitlers查找註冊表。它嘗試用輸出PIN的首先媒體類型來匹配註冊表中列舉的媒體類型。
       每個Filter都註冊了一個Merit數值來區別與其他Filter的優先級。EnumMatchingFitlers函數返回以Merit排序的Filters, 最小的MeritMERIT_DO_NOT_USE+1. 它會忽略比這個最小值還小的Filter. Filter還會以GUID值的種類排序。種類本身也有MeritEnumMatchingFitlers也忽略Merit比最小值小的種類。即使種類中存在Merit比較高的Filter
       總結起來,Render函數按下面的順序嘗試Filter:
       ·使用IStramBuilder
       ·嘗試緩存Filter
       ·嘗試Graph中的Filter
       ·查詢註冊表的Filter
     AddSourceFilter函數添加一個可以提交指定文件的Source Filter。首先查詢註冊表匹配協議(比如http://),文件擴展名,預定義的檢測字節集(通常是匹配一定模式的在文件中特殊偏移位置的字節)。細節可參考Registering a Custom File Type. 如果函數找到合適的Source Filter, 就創建Filter的實例並添加到Graph. 然後用文件名調用FilterIFileSourceFilter::Load函數。
       RenderFile函數從文件名建立一個默認的回放Graph。內部它使用AddSourceFilter來查找正確的Source Filter,然後用Render建立Graph的餘下部分。
       Connect函數連接輸入、輸出PIN。此函數會根據Render中描述的各種算法添加中介Filter,如果需要:
       (1)、嘗試不用中介Filter直接連接兩個Filter
       (2)、嘗試緩存Filter
       (3)、嘗試Graph中的Filter
       (4)。查詢註冊表的Filter.
 
4.     Data Flow in the Filter Graph
       本節描述媒體數據如何在Filter Graph中移動。通常,編寫DirectShow應用程序不需要知道這些細節,儘管有時候會感覺到有幫助。如果是編寫DirectShow Filter,就需要理解這些知識。
     4.1    Transports
       爲了在Filter Graph中傳送媒體數據,DirectShow Filter需要支持一些協議,稱之爲傳輸協議(transport)。相連的Filter必須支持同樣的傳輸協議,否則不能交換媒體數據。通常,一個傳輸協議要求PIN支持一個特殊的接口。當Filter連接時,一個PIN就向另一個查詢此接口。
大多數的DirectShow Filter把媒體數據保存在主存儲器中,並通過Pin連接把數據遞送給其它的Filter,這種傳輸稱爲本地內存傳輸。雖然本地內存傳輸在DirectShow中最常用,但並不是所有的Filter都使用它。例如,有些Filter通過硬件傳送媒體數據,Pin只是用來提交控制信息,如IOverlay接口。
   DirectShow爲本地內存器傳輸定義了兩種機制:推模式和拉模式。在推模式中,Source Filter生成數據並遞送給下一級Filter。下一級Filter被動接收數據,完成處理後再傳送給再下一級Filter。在拉模式中,Source Filter與一個Parse Filter相連。Parse FilterSource Filter請求數據後,Source Filter才遞送數據以響應請求。推模式用IMemInputPin接口,拉模式用IAsyncReader接口。
   推模式比拉模式要更常用。因爲後續的文章都假定使用推模式。本節的最後一部分,Pull Mode說明了IAsyncReader接口與IMemInputPin接口的區別。
 
4.2 Samples and Allocators
       當一個Pin向另一個Pin傳遞數據的時候,它並不是直接將內存塊的指針傳遞下一個Pin,實際上傳遞的是一個管理內存的COM對象指針。這個COM對象稱Media Sample暴露了IMediaSample接口。接收Pin通過調用IMediaSample的方法來對內存進行操作,比如GetSizeGetActualDataLength以及GetPointer方法。
   Sample總是從上到下遞送的,從輸出Pin到輸入Pin,輸出Pin通過調用輸入PinIMemInputPinReceive方法傳遞Sample,輸入Pin可以在Receive函數同步處理數據,或者另外採用一個工作線程進行異步處理。輸入PIN也允許阻塞在Receive方法中,如果需要等待資源。
       另外一個COM對象,叫做Allocator,用來創建和管理Sample的。暴露了IMemAllocator接口。當一個Filter需要一個空的Buffer的時候,就可以調用IMemAllocator::GetBuffer,該方法返回一個指向Sample的指針。每一個Pin連接都共享一個Allocator,當兩個Pin連接的時候,他們會決定由哪個Filter來提供Allocator,通過Pin還可以設置Allocator的屬性,例如,Buffer的數量和大小。具體細節可參考How Filters Connect, Negotiating Allocators.
       下面的圖表顯示了AllocatorSampleFilter之間的關係。

       4.2.1 Media Sample Reference Counts
       Allocator創建了一個有限的Sample池。在任何時候,某些Sample可能在使用,而其他的可以響應GetBuffer調用。Allocator用引用計數保持跟蹤SamplesGetBuffer返回的Sample的引用計數是1。當Sample的引用計數爲0時,Sample就返回Allocator池,成爲空閒Sample,可以再次響應GetBuffer的調用。如果所有的Sample都處於繁忙狀態,GetBuffer就會阻塞,直到有一個Sample空閒。
       例如,假設一個輸入Pin接到一個Sample,如果它在Receive方法裏同步的處理這個Sample,沒有增加該Sample的引用計數,在Receive返回後,輸出Pin釋放這個Sample,引用計數爲0Sample就返回到Allocator池。另一方面,如果輸入Pin在工作線程中處理該Sample,引用計數增加1,成爲2,輸出Pin返回,釋放Sample,計數成1。在工作線程結束處理後,調用Release釋放Sample. 現在Sample返回到池中。
       當一個Pin接收到一個Sample時,它可以將數據複製到另一個Sample中,也可以將這個Sample傳遞到下一個Filter。一個Sample可以流遍整個Filter graph。每個Filter依次調用AddRefRelease(譯註:相當於使引用計數要保持大於0)。因此,輸出Pin調用Receive後就決不能再使用這個Sample。因爲也許下游還有Filter正在使用該Sample。輸出Pin必須調用GetBuffer獲取新的Sample
       這種機制減少了內存分配的,因爲Buffer可以重用。也防止了Filter在沒有處理的Sample重新寫入,因爲Allocator維護了一個可用Sample列表。
       Filter可以給輸入、輸出使用分別的Allocator. Filter可以在擴展輸入數據時這麼做(比如解壓數據)。如果輸出並不比輸入大,Filter可以就地處理數據,而不需拷貝到新Sample。那時,兩個或者更多的PIN連接就可以共享Allocator.
 
       4.2.2 Committing and Decommitting Allocators
       Filter創建一個Allocator的時候,Allocator還沒有保留任何的內存,如果這個時候有人GetBuffer,就會失敗。只有當數據流開始的時候,輸出Pin調用IMemAllocator::Commit,提交Allocator,現在才能分配內存。然後纔可以調用GetBuffer.

       當數據流停止時,Pin就調用IMemAllocator::Decommit,來銷燬Allocator。在Allocator再次Commit之前,所有的GetBuffer調用都會失敗。同樣,即使是GetBuffer正在阻塞等待Sample,也會立即返回一個錯誤碼。Decommit能不能釋放內存取決於它的實現。比如CMemAllocator類會等到它的析構函數來釋放內存。 

【下一篇文章】

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