




版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報或認(rèn)領(lǐng)
文檔簡介
1、深入淺出 DirectShow FilterFilter 概述Filter 是一個 COM 組件,由一個或多個 Pin 組成。 Pin 也是一個 COM 組件。 Filter 文件的擴(kuò)展名為.ax,但也可以是.dll。Filter根據(jù)其包含In put pi n或Output pin 的情況(或在 Filter Graph 的位置),大致可分為三類: Source Filter( 僅有 Output pin) 、Transform Filter( 同時具有 Input pin 和 Output pin) 和 Renderer Filter( 僅有 Input pin) 。一般情況下,創(chuàng)建 Fi
2、lter 使用一個普通的 Win32 DLL 項目。而且,一般 Filter 項目不 使用 MFC 。這時,應(yīng)用程序通過 CoCreateInstance 函數(shù) Filter 實例; Filter 與應(yīng)用程序在 二進(jìn)制級別的協(xié)作。另外一種方法,也可以在 MFC 的應(yīng)用程序項目中創(chuàng)建 Filter 。這種情 況下, Filter 不需注冊為 COM 組件, Filter 與應(yīng)用程序之間的協(xié)作是源代碼級別的;創(chuàng)建 Filter 實例,不再使用 CoCreateInstance 函數(shù),而是直接 new 出一個 Filter 對象,如下: m_pFilterObject = new CFilterCl
3、ass();/ make the initial refcount 1 to match COM creationm_pFilterObject ->AddRef();因為 Filter 的基類實現(xiàn)了對象的引用計數(shù), 所以即使在第二種情況下, 對創(chuàng)建后的 Filter 對象的操作也完全可以遵循 COM 標(biāo)準(zhǔn)。Filter 是一個獨立功能模塊,最好不要將 Filter 依賴于其他第三方的 DLL 。因為 Filter 具有 COM 的位置透明性特點, Filter 文件可以放在硬盤的任何位置,只要位置移動后重新 注冊。但此時,如果 Filter依賴其他DLL,則Filter對該DLL的定位
4、就會出現(xiàn)問題。Filter 不能脫離 Filter Graph 單獨使用。所以,如果你想繞過 Filter Graph 直接使用Filter 實現(xiàn)的模塊功能,請將你的 Filter 移植成 DMO ( DirectX Media Object )。2. Filter 的注冊Filter 是 COM 組件,所以在使用前一定要注冊。 Filter 的注冊程序為 regsvr32.exe如果帶上命令行參數(shù) /u ,表示注銷;如果帶上是 /s ,表示不彈出任何注冊 / 注銷成功與否的 提示對話框。 如果你想在 Build Filter 項目的時候進(jìn)行自動注冊, 請在 VC 的 Project sett
5、ings 的 Custom Build 頁如下設(shè)置:Description: Register filterCommands: regsvr32 /s /c $(TargetPath)echo regsvr32 exe.time > $(TargetDir)$(T argetName).trgOutputs: $(TargetDir)$(TargetName).trgFilter 的注冊信息包括兩部分:基本的 COM 信息和 Filter 信息。注冊信息都存放在注 冊表中。前者的位置為: HKEY_CLASSES_ROOTCLSIDFilter ,后者的位置為:。COM信息標(biāo)示了 Fil
6、ter是一個標(biāo)準(zhǔn)的可以通過 CoCreateInstance 函數(shù)創(chuàng)建的 COM 組件, Filter 信息標(biāo)示了我們 通過 Graphedit 看到的描述這個 Filter 的信息。如果你不想讓 Graphedit 看到(或者讓 Filter 枚舉器找到)你寫的 Filter ,你完全可以不注冊 Filter 信息。而且不用擔(dān)心,你這么做也完 全不會影響 Filter 的功能。屏蔽注冊 Filter 信息的方法也很簡單。 因為 CBaseFilter 實現(xiàn)了 IAMovieSetup 接口的 兩個函數(shù): Register 和 Unregister 。我們只需重載這兩個函數(shù), 直接 retur
7、n S_OK 就行了。(注意:lAMovieSetup 是用以注冊Filter信息部分的接口,但已經(jīng)廢棄,僅在AMovieDllRegisterServer 和 AMovieDllUnregisterServer 調(diào)用才會用到。新寫的 Filter 注冊函數(shù)一般使用 AMovieDllRegisterServer2 ,這個函數(shù)不使用 lAMovieSetup 接口。 如 果想要不注冊 Filter 信息,最好自己實現(xiàn) Filter 的兩個導(dǎo)出函數(shù): DllRegisterServer 和DllUnregisterServer ,其中只使用 RegisterAllServers 函數(shù)注冊 Ole
8、 Server 。)Filter 的 Merit 值。這個值是微軟的“智能連接”函數(shù)使用的。在 Graphedit 中,當(dāng)我 們加入一個 Source Filter 后,在它的pin上執(zhí)行“ Render ”,會自動連上一些 Filter。Merit 的值參考如下:MERIT_PREFERRED = 0x800000, MERIT_NORMAL = 0x600000,MERIT_UNLIKELY = 0x400000, MERIT_DO_NOT_USE = 0x200000, MERIT_SW_COMPRESSOR = 0x100000, MERIT_HW_COMPRESSOR = 0x100
9、050Merit 值只有大于 MERIT_DO_NOT_USE 的時候才有可能被“智能連接”使用; Merit 的 值越大,這個 Filter 的機(jī)會就越大。3. Filter 之間 Pin 的連接過程Filter 只有加入到 Filter Graph 中并且和其它 Filter 連接成完整的鏈路后,才會發(fā)揮作 用。 Filter 之間的連接(也就是 Pin 之間的連接),實際上是連接雙方的一個 Media type 的協(xié)商過程。連接的方向總是從 Output pin 指向 Input pin 。連接的大致過程為:如果調(diào) 用連接函數(shù)時已經(jīng)指定了完整的 Media type ,則用這個 Medi
10、a type 進(jìn)行連接,成功與否 都結(jié)束連接過程;如果沒有指定或不完全指定了 Media type ,則進(jìn)入下面的枚舉過程。枚 舉欲連接的 Input pin 上所有的 Media type ,逐一用這些 Media type 與 Output pin 進(jìn) 行連接(如果連接函數(shù)提供了不完全 Media type ,則要先將每個枚舉出來的 Media type 與它進(jìn)行匹配檢查) ,如果 Output pin 也接受這種 Media type ,則 Pin 之間的連接宣告成 功;如果所有 Input pin 上枚舉的 Media type , Output pin 都不支持, 則枚舉 Outpu
11、t pin上的所有 Media type ,并逐一用這些 Media type 與 Input pin 進(jìn)行連接。 如果 Input pin 接受其中的一種 Media type ,則 Pin 之間的連接到此也宣告成功;如果 Output pin 上的 所有 Media type , Input pin 都不支持,則這兩個 Pin 之間的連接過程宣告失敗。每個 Pin 都可以實現(xiàn) GetMediaType 函數(shù)來提供該 Pin 上支持的所有 Preferred Media type (但一般只在 Output pin 上實現(xiàn), Input pin 主要實現(xiàn) CheckMediaType 看是否
12、支 持當(dāng)前提供的 Media type 就行了)。連接過程中, Pin 上枚舉得到的所有 Media type 就 是這里提供的。在 CBasePin 類中有一個 protected 的成員變量 m_bTryMyTypesFirst ,默認(rèn)值為 false 。在我們定制 Filter 的 Output pin 中改變這個變量的值為 true ,可以定制我們自己的 連接過程(先枚舉 Output pin 上的 Media type )。當(dāng) Pin 之間的連接成功后,各自的 pin 上都會調(diào)用 CompleteConnect 函數(shù)。我們可 以在這里取得一些連接上的 Media type 的信息,以
13、及進(jìn)行一些計算等。在 Output pin 的 CompleteConnect 實現(xiàn)中,還有一個重要的任務(wù),就是協(xié)商 Filter Graph 運(yùn)行起來后 Sample 傳輸使用的內(nèi)存配置情況。這同樣是一個交互過程:首先要詢問一下 Input pin 上 的配置要求,如果 Input pin 提供內(nèi)存管理器( Allocator ),則優(yōu)先使用 Input pin 上的內(nèi) 存管理器; 否則, 使用 Output pin 自己生成的 內(nèi)存管理器。我們一般 都要實 現(xiàn) DecideBufferSize 來決定存放 Sample 的內(nèi)存大小。注意:這個過程協(xié)商完成之后,實際 的內(nèi)存并沒有分配,而要等
14、到 Output pin 上的 Active 函數(shù)調(diào)用。4. Filter Media type概述Media type 一般可以有兩種表示: AM_MEDIA_TYPE 和 CMediaType 。前者是一個 Struct ,后者是從這個 Struct 繼承過來的類。每個 Media type 有三部分組成: Major type 、 Subtype 和 Format type 。這三個部 分都使用 GUID 來唯一標(biāo)示。 Major type 主要定性描述一種 Media type ,比如指定這是 一個 Video ,或 Audio 或 Stream 等; Subtype 進(jìn)一步細(xì)化 Me
15、dia type ,如果 Video 的 話可以進(jìn)一步指定是 UYVY 或 YUY2 或 RGB24 或 RGB32 等; Format type 用一個 Struct 更進(jìn)一步細(xì)化 Media type 。如果 Media type 的三個部分都是指定了某個具體的 GUID 值,則稱這個 Media type 是完全指定的;如果 Media type 的三個部分中有任何一個值是 GUID_NULL ,則稱這個 Media type 是不完全指定的。 GUID_NULL 具有通配符的作用。常用的 Major type:MEDIATYPE_Video;MEDIATYPE_Audio;MEDIAT
16、YPE_AnalogVideo; / Analog captureMEDIATYPE_AnalogAudio;MEDIATYPE_Text;MEDIATYPE_Midi;MEDIATYPE_Stream;MEDIATYPE_Interleaved; / DV camcorderMEDIATYPE_MPEG1SystemStream;MEDIATYPE_MPEG2_PACK;MEDIATYPE_MPEG2_PES;MEDIATYPE_DVD_ENCRYPTED_PACK;MEDIATYPE_DVD_NAVIGATION;常用的 Subtype:MEDIASUBTYPE_YUY2;MEDIASUB
17、TYPE_YVYU;MEDIASUBTYPE_YUYV;MEDIASUBTYPE_UYVY;MEDIASUBTYPE_YVU9;MEDIASUBTYPE_Y411;MEDIASUBTYPE_RGB4;MEDIASUBTYPE_RGB8;MEDIASUBTYPE_RGB565;MEDIASUBTYPE_RGB555;MEDIASUBTYPE_RGB24;MEDIASUBTYPE_RGB32;MEDIASUBTYPE_ARGB32; / Contains alpha valueMEDIASUBTYPE_Overlay;MEDIASUBTYPE_MPEG1Packet;MEDIASUBTYPE_MP
18、EG1Payload; / Video payloadMEDIASUBTYPE_MPEG1AudioPayload; / Audio payloadMEDIASUBTYPE_MPEG1System; / A/V payloadMEDIASUBTYPE_MPEG1VideoCD;MEDIASUBTYPE_MPEG1Video;MEDIASUBTYPE_MPEG1Audio;MEDIASUBTYPE_Avi;MEDIASUBTYPE_Asf;MEDIASUBTYPE_QTMovie;MEDIASUBTYPE_PCM;MEDIASUBTYPE_WAVE;MEDIASUBTYPE_dvsd; / DV
19、MEDIASUBTYPE_dvhd;MEDIASUBTYPE_dvsl;MEDIASUBTYPE_MPEG2_VIDEO;MEDIASUBTYPE_MPEG2_PROGRAM;MEDIASUBTYPE_MPEG2_TRANSPORT;MEDIASUBTYPE_MPEG2_AUDIO;MEDIASUBTYPE_DOLBY_AC3;MEDIASUBTYPE_DVD_SUBPICTURE;MEDIASUBTYPE_DVD_LPCM_AUDIO;MEDIASUBTYPE_DVD_NAVIGATION_PCI;MEDIASUBTYPE_DVD_NAVIGATION_DSI;MEDIASUBTYPE_DV
20、D_NAVIGATION_PROVIDER;常用的 Format type :FORMAT_NoneFORMAT_DvInfo DVINFOFORMAT_MPEGVideo MPEG1VIDEOINFOFORMAT_MPEG2Video MPEG2VIDEOINFOFORMAT_VideoInfo VIDEOINFOHEADERFORMAT_VideoInfo2 VIDEOINFOHEADER2FORMAT_WaveFormatEx WAVEFORMATEX5. Filter 之間的數(shù)據(jù)傳送Filter 之間的數(shù)據(jù)是通過 Sample 來傳送的。 Sample 是一個 COM 組件,擁有自己的
21、 一段數(shù)據(jù)緩沖。 Sample 由 Allocator 統(tǒng)一管理。如下圖所示:1 完整類型如果媒體類型每一個部分都定義的很完成, 那么 pin 就嚴(yán)格按照定義的類型進(jìn)行連接。 如果不匹配,連接失敗。2 部分媒體類型如果媒體類型的機(jī)構(gòu)中, major type, subtype, or format type 的值為 GUID_NULL , 這個值是一個通配符號。任何類型都可以匹配。3 沒有媒體類型如果 filter 圖表管理器傳遞過來一個 NULL 的指針,這個 pin 就可以和任意的類型的 媒體類型匹配。Filter 之間數(shù)據(jù)傳送的方式有兩種: Push 模式和 Pull 模式。所謂 Pus
22、h 模式,即 Source filter 自己能夠產(chǎn)生數(shù)據(jù),并且一般在它的 Output pin 上 有獨立的子線程負(fù)責(zé)將數(shù)據(jù)發(fā)送出去,常見的情況如 WDM 模型的采集卡的 Live Source Filter ;而所謂 Pull 模式,即 Source filter 不具有把自己的數(shù)據(jù)送出去的能力, 這種情況下, 一般 Source filter 后緊跟著接一個 Parser Filter 或 Splitter Filter ,這種 Filter 一般在 Input pin 上有個獨立的子線程, 負(fù)責(zé)不斷地從 Source filter 索取數(shù)據(jù), 然后經(jīng)過處理后將數(shù)據(jù)傳 送下去,常見的情況
23、如 File source 。 Push 模式下, Source filter 是主動的; Pull 模式下, Source filter 是被動的。而事實上,如果將上圖 Pull 模式中的 Source filter 和 Splitter Filter看成另一個虛擬的 Source filter ,則后面的 Filter 之間的數(shù)據(jù)傳送也與 Push 模式完全相同。那么,數(shù)據(jù)到底是怎么通過連接著的 Pin 傳送的呢?首先來看 Push 模式。在 Source filter 后面 Filter 的 Input pin 上,一定實現(xiàn)了一個 IMemInputPin 接口,數(shù)據(jù)正是通過上 一級 F
24、ilter 調(diào)用這個接口的 Receive 方法進(jìn)行傳送的。值得注意的是,數(shù)據(jù)從 Output pin 通過 Receive 方法調(diào)用傳送到 Input pin 上,并沒有進(jìn)行內(nèi)存拷貝, 它只是一個相當(dāng)于數(shù)據(jù) 到達(dá)的“通知” 。再看一下 Pull 模式。 Pull 模式下的 Source filter 的 Output pin 上,一定 實現(xiàn)了一個 IAsyncReader 接口;其后面的 Splitter Filter ,就是通過調(diào)用這個接口的 Request 方法或者 SyncRead 方法來獲得數(shù)據(jù)。 Splitter Filter 然后像 Push 模式一樣,調(diào) 用下一級 Filter
25、 的 Input pin 上的 IMemInputPin 接口 Receive 方法實現(xiàn)數(shù)據(jù)的往下傳送。一個 DirectShow 的應(yīng)用程序, 至少會有兩條線程: 主線程和 Filter 用于數(shù)據(jù)傳送的子 線程。既然是多線程,就不可避免會出現(xiàn)線程同步問題。 Filter 的狀態(tài)改變都在主線程中完 成, Filter 的數(shù)據(jù)相關(guān)操作都在數(shù)據(jù)線程中調(diào)用。各線程一些主要函數(shù)調(diào)用參考如下:Streaming thread(s): IMemInputPin:Receive, IMemInputPin:ReceiveMultiple, IPin:EndOfStream, IMemAllocator:Ge
26、tBuffer.Application thread: IMediaFilter:Pause, IMediaFilter:Run, IMediaFilter:Stop, IMediaSeeking:SetPositions, IPin:BeginFlush, IPin:EndFlush.Either: IPin:NewSegment.這些函數(shù)切忌混合調(diào)用,否則會引起線程的死鎖。另外值得注意的是, BeginFlush 和 EndFlush 屬于主線程調(diào)用,而不是數(shù)據(jù)線程調(diào)用。6. Transform filter 和 Trans-in-place filter 的區(qū)別首先,這兩種 Filter
27、 是有共同點的,因為 Trans-in-place filter 本身就是從 Transform filter 中繼承過來的。 其次, 我們要明白的是, Trans-in-place filter “盡力” 使自己的 Input pin 和 Output pin 使用相同的 Allocator ,以免去一次 Sample 數(shù)據(jù)的 memcpy 。我們說“盡力”,就是說 Trans-in-place filter 也未必能夠?qū)崿F(xiàn)它的初衷。 (如果 Trans-in-place filter 使用的 Allocator 是 ReadOnly 的,而 Trans-in-place filter 又要
28、修改 Sample 的數(shù) 據(jù),則 Trans-in-place filter 的 Input pin 和 Output pin 將不得不使用不同的 Allocator 。)Trans-in-place filter 有一個 protected 的成員變量 m_bModifiesData ,默認(rèn)值為 true 。 如 果 你 確 信 定 制 Trans-in-place filter 不 需 要 修 改 Sample 數(shù) 據(jù) , 則 將 m_bModifiesData 賦值為 false ,這樣可以保證 Input pin 和 Output pin 使用相同的 Allocator 。Trans-
29、in-place filter 的 實 現(xiàn) 主 要 體 現(xiàn) 在 以 下 三 個 函 數(shù) : CTransInPlaceFilter:CompleteConnect 、 CTransInPlaceInputPin:GetAllocator 和 CTransInPlaceInputPin:NotifyAllocator 。 CompleteConnect 中 進(jìn) 行 必 要 的 重 連Reconnect ),保證 Trans-in-place filter的 Input pin 和 Output pin 使用相同的 Media type 。 GetAllocator 能夠取得 Trans-in-p
30、lace filter 下一級 Filter 的 Input pin 上的Allocator 。 NotifyAllocator “盡力”使 Trans-in-place filter的 Input pin 和 Output pin使用同一個 Allocator 。7. IMediaSeeking 的實現(xiàn)IMediaSeeking 的實現(xiàn)在 Filter 上,但應(yīng)用程序應(yīng)該從 Filter Graph Manager 上得到 這個接口。在 Filter 級別, Filter Graph Manager 首先從 Renderer filter 開始詢問上一級 Filter 的 Output pi
31、n 是否支持 IMediaSeeking 接口。如果支持, 則返回這個接口;如果不 支持,則繼續(xù)往上一級 Filter 詢問,直到 Source filter 。一般在 Source filter 的 Output pin上實現(xiàn) IMediaSeeking 接口。(如果是 File source ,一般在 Parser Filter 或 Splitter Filter 實現(xiàn)這個接口。 )對于 Filter 開發(fā)者來說,如果我們寫的是 Source filter ,就要在 Filter 的Output pin 上實現(xiàn) IMediaSeeking 接口;如果寫的是 Transform filter
32、 ,只需要在 Output pin 上將用戶的接口請求往上傳遞給上一級 Filter 的 Output pin ;如果寫的是 Renderer Filter ,需要在 Filter 上將用戶的接口請求往上傳遞給上一級 Filter 的 Output pin 。注意:為了保證 Seek 操作后 Stream 的同步性,如果實際實現(xiàn) IMediaSeeking 接口 的 Filter 有多個 Output pin ,一般僅有一個 pin 支持 Seek 操作。對于你定制的 Transform filter ,如果有多個 Input pin ,你需要自己決定當(dāng) Output pin 接收到 IMed
33、iaSeeking 接 口請求時選擇哪一條路徑往上繼續(xù)請求。應(yīng)用程序能夠在任何時候( running, paused or stopped )對 Filter graph 執(zhí)行 Seek 操 作。但當(dāng) Filter graph 正在 running 的時候, Filter graph manager會先 pause 住,執(zhí)行完 Seek 操作后,再重新 run 起來。IMediaSeeking 可以有如下幾種 Seek 的時間格式:TIME_FORMAT_FRAME Video frames.TIME_FORMAT_SAMPLE Samples in the stream.TIME_FORMA
34、T_FIELD Interlaced video fields.TIME_FORMAT_BYTE Byte offset within the stream.TIME_FORMAT_MEDIA_TIME Reference time (100-nanosecond units).但實現(xiàn)這個接口的 Filter 未必支 持所有的這 些格式。 一般 Filter 都會支持 TIME_FORMAT_MEDIA_ TIME , 當(dāng) 使 用 其 它 的 格 式 時 , 最 好 調(diào) 用 IMediaSeeking:IsFormatSupported 進(jìn)行一下確認(rèn)。對于 Filter ,不贊成使用 IMed
35、iaPosition 接口。 IMediaPosition 是用以支持 Automation的(比如 VB 里面使用 DirectShow ), IMediaSeeking 不支持 Automation8. Filter 的狀態(tài)轉(zhuǎn)換Filter 有三種狀態(tài): stopped, paused, running 。 paused 是一種中間狀態(tài), stopped 狀態(tài)到 running 狀態(tài)必定經(jīng)過 paused 狀態(tài)。 paused 可以理解為數(shù)據(jù)就緒狀態(tài), 是為了快 速切換到 running 狀態(tài)而設(shè)計的。在 paused 狀態(tài)下,數(shù)據(jù)線程是啟動的,但被 Renderer filter 阻塞了
36、。paused 與 running 兩者間的狀態(tài)轉(zhuǎn)換, 對于 Source filter 和 Transform filter 可以忽 略不計,而對于 Renderer filter (特別是 Video renderer / Audio renderer )情形稍有不 同。 Renderer 首先處理那個 paused 狀態(tài)下 Hold 的 Sample ,當(dāng)接收到新的 Sample 時, 判斷 Sample 上的時間戳。如果時間未到, Renderer 會 Hold 住這個 Sample 進(jìn)行等待。Filter graph manager 以從下到上的順序?qū)?Filter 進(jìn)行狀態(tài)轉(zhuǎn)換,
37、即從 Renderer filter 一直回溯到 Source filter 。這個順序能夠有效地避免 Sample 的丟失以及 Filter graph 的 死鎖。Stopped to paused: 首先從 Renderer 開始進(jìn)行 paused 狀態(tài)的轉(zhuǎn)換。這時, Filter 調(diào) 用自己所有 Pin 的 Active 函數(shù)進(jìn)行初始化 (一般 Pin 在 Active 中進(jìn)行 Sample 內(nèi)存的分配, 如果是 Source filter 還將啟動數(shù)據(jù)線程) ,使 Filter 處于一種就緒狀態(tài)。 Source filter 是最 后一個完成到就緒狀態(tài)轉(zhuǎn)換的Filter。然后,Sour
38、ce filter啟動數(shù)據(jù)線程,往下發(fā)送Sample 。當(dāng) Renderer 接收到第一個 Sample 后就阻塞住。 當(dāng)所有的 Renderer 實現(xiàn)了狀態(tài)轉(zhuǎn)換, Filter graph manager 才認(rèn)為狀態(tài)轉(zhuǎn)換完成Paused to stopped: 當(dāng) Filter 進(jìn)入 stopped 狀態(tài)時,調(diào)用自己所有 Pin 的 Inactive 函 數(shù)(一般 Pin 在 Inactive 中進(jìn)行 Sample 內(nèi)存的釋放,如果是 Source filter 還將終止數(shù)據(jù) 線程)。釋放所有 Hold 的 Sample ,以使上一級 Filter 的 GetBuffer 脫離阻塞; 終止所
39、有在Receive 中的等待, 以使上一級 Filter 的 Receive 函數(shù)調(diào)用返回。 Filter 在 stopped 狀態(tài)下 拒絕接受任何 Sample 。這樣從 Renderer filter 往上一級一級脫離阻塞,當(dāng)?shù)竭_(dá) Source filter 的時候,可以確保數(shù)據(jù)線程終止。9. EndOfStream 問題當(dāng) Source filter 的所有數(shù)據(jù)都已經(jīng)發(fā)送出去, 則會調(diào)用下一級 Filter 的 Input pin 上的 IPin:EndOfStream ,直到 Renderer filter 。當(dāng)這個 Renderer filter 的所有 Input pin 都被 調(diào)
40、用了 EndOfStream ,則向 Filter graph manager 發(fā)送一個 EC_COMPLETE 事件。僅當(dāng) Filter graph 中的所有 Stream 都發(fā)送了 EC_COMPLETE 事件, Filter graph manager 才 會將這個事件發(fā)送給應(yīng)用程序。在我們定制的 Filter 中,如果接收到了上一級 Filter 傳過來的 EndOfStream ,則說明 上面的數(shù)據(jù)已經(jīng)全部傳送完畢, Receive 方法不須再接收數(shù)據(jù)。 如果我們對數(shù)據(jù)進(jìn)行了緩沖, 則應(yīng)確認(rèn)緩沖中的所有數(shù)據(jù)都被處理完并往下發(fā)送了,然后再往下調(diào)用EndOfStream 。Pull模式下
41、,一般是 Splitter filter 或 Parser filter 發(fā)送 EndOfStream ,而且方向是往下的, Source filter 上不會收到這樣的通知。10. BeginFlush 、EndFlush 、 NewSegment 問題典型的情況,當(dāng)進(jìn)行 MediaSeeking 之后,會調(diào)用 BeginFlush 、 EndFlush 。一般在 Input pin 上實現(xiàn)這兩個函數(shù)。對于 Filter 開發(fā)者來說, Filter 在被調(diào)用 BeginFlush 時需要做以下工作:調(diào)用下一級Filter的BeginFlush,使其不再接收新的Sample ;拒絕接收上一級
42、Filter的數(shù)據(jù),包括 Receive調(diào)用和EndOfStream調(diào)用;如果上一級 Filter正在阻塞等待空的Sample,此時需要讓它脫離阻塞(通過析構(gòu)Allocator );確保數(shù)據(jù)流線程脫離阻塞狀態(tài)。Filter 在被調(diào)用 EndFlush 時需要做以下工作:確保所有等待緩存的Sample被丟棄;確保Filter上已經(jīng)緩存的數(shù)據(jù)被丟棄;清除沒有發(fā)出去的 EC_COMPLETE事件(如果這是一個Ren dered in put pin );調(diào)用下一級 Filter 的 EndFlush。還有一點:如果你必須在定制的 Filter 中為每個 Sample 打 Time stamp ,那么
43、記住在MediaSeeking 之后出去的 Sample 的 Time stamp 應(yīng)該從 0 開始重打。Segment 是一段時間內(nèi)具有相同的 Playback rate 的一組 Sample ,以 NewSegment 函數(shù)調(diào)用來表示這個 Segment 的開始。 NewSegment 一般在開始新的 Stream 的時候, 或者用戶進(jìn)行了 MediaSeeking 之后,由 Source filter ( Push 模式下)或 Parser/Splitter filter ( Pull 模式下)發(fā)起,并往下層層調(diào)用,一直到 Renderer filter 。在我們定制的 Filter 中
44、可以利用 NewSegment 傳遞下來的信息, 特別是對于 Decoder 對于 Audio renderer 也是一個典型例子, 它根據(jù) Playback rate 和 Audio 實際的采樣頻率 來對聲卡產(chǎn)生輸出。11. Quality Control 問題Filter 之間的數(shù)據(jù)傳送,有時候過快,有時候過慢。 DirectShow 使用 Quality Control 來解決這個問題,即IQualityControl 接口的兩個函數(shù)(SetSink和Notify )。一般,Renderer filter 在 Filter 上實現(xiàn)這個接口,而其他 Filter 在 Output pin
45、上實現(xiàn)這個接口。上圖為一般的 Quality Control 的處理過程。而能夠調(diào)整發(fā)送速度的 IQualityControl 接口一般在 Source filter ( pull 模式下為 parser/splitter filter )上實現(xiàn), Transform filter 只是將 Quality Message 往上一級 Filter 傳遞。應(yīng)用程序可以實現(xiàn)自己的 Quality Control Manager ,然后通過調(diào)用 SetSink 方法設(shè) 置給 Filter 。上述的處理過程就改變了, Quality Message 直接發(fā)送給自定義的 Manager 。12. 對運(yùn)行過
46、程中 Media type 改變的支持我們可以從 CBaseInputPin:Receive 中可以看到, Input pin 每次在接收 Sample 的 之前,一般都會進(jìn)行 CheckStreaming (如果當(dāng)前 Filter 已經(jīng) Stop 或正在 Flush 或發(fā)生了 RuntimeError ,則拒絕接收 Sample ),然后將當(dāng)前的 Sample 屬性保存到 protected 的 m_SampleProps 成員變量中。描述 Sample 屬性的是一個 AM_SAMPLE2_PROPERTIES 的結(jié)構(gòu),它有一個標(biāo)記來表明當(dāng)前 Sample 的 Media type 是否已經(jīng)
47、改變。 如果 Media type 改變了,則進(jìn)行 CheckMedaiType 看我們的 Filter 是否仍然支持它。如果不支持,則發(fā)出 一個 RuntimeError ,并發(fā)送 EndOfStream 。一個健全的 Filter 應(yīng)該能夠?qū)\(yùn)行時 Media type 的改變做出處理。在我們的 Receive 方 法 實 現(xiàn) 中 , 我 們 可 以 通 過 if (pProps->dwSampleFlags & AM_SAMPLE_TYPECHANGED) 來判斷 Meida type 是否已經(jīng)改變;如果改變,我們需要 根據(jù)新的 Media type 進(jìn)行必要的初始化。一個
48、典型的案例:當(dāng) Camcorder 輸入時, Audio 的 Media type 可能改變。比如, Filter 連接時 Media type 使用了 MEDIATYPE_PCM ,而在運(yùn)行時又換成了 MEDIATYPE_WAVE ; 或者連接時 Audio 的采樣頻率時 44.1K ,而在運(yùn)行時卻變成了 48K ;或者 Camcorder 的 帶子上本身保存了混合的 44.1K 和 48K 的 Audio 。創(chuàng)建一個 filter 實例CTransformFilter1 、選擇所要創(chuàng)建的 filter 的用途,據(jù)此來選擇基類?;惪梢詮腃TransInPlaceFilter 、 CVide
49、oTransformFilter和 CBaseFilter 中來選取。( 1 ) CTransInPlaceFilter 提供了本地處理 Sample 的機(jī)制( Sample 可以認(rèn)為是存儲一 個視頻幀的結(jié)構(gòu)) ,當(dāng)一個 trans-in-place filter 收到一個 sample 時,你可以通過重載它 的 Transform ()函數(shù)來修改其中的數(shù)據(jù), trans-in-place filter 會在 Transform ()函數(shù) 執(zhí)行完后直接把這個 sample 傳遞給下一個 filter 。( 2 ) CTransformFilter 完成的功能與 CTransInPlaceFil
50、ter 一樣,它們的區(qū)別就是 CTransformFilter 總是把上游 filter 傳遞過來的 sample 復(fù)制一份,并把復(fù)制后的 sample 傳遞給下一個 filter 。當(dāng)然,你可以通過重載 Transform ()函數(shù)來控制這個過程,包括修 改其中的數(shù)據(jù)(這也是自己寫 filter 的原因)。( 3 ) CVideoTransformFilter與 CTransformFilter 一樣,只是多加了質(zhì)量控制功能。(4 )以上三個 filter 都繼承于 CBaseFilter ,所以如果想對 filter 進(jìn)行更多的控制,就要直 接從 CBaseFilter 來繼承,但是所要做
51、的工作也最多。在這個例子中,我選擇 CTransformFilter ,因為 CTransInPlaceFilter 太簡單了, dx9sdk 中的例子 NullNull 就是一個完整的 CTransInPlaceFilter 的框架, 并且只是一個框架, 什么 工作也沒有做,如果要用的話直接修改就可以用了。2、在 vc 中選擇 win32 dll ,創(chuàng)建一個 dll ,名字隨便取,這里我取 SplitFilter ,選擇空 dll 。3、然后,創(chuàng)建一個類 CSplitFilter ,繼承自CTransformFilter ,當(dāng)然要選擇 public 方式。4、為 filter 生成一個CLS
52、ID ,可以使用 Guidgen ,它的用法是在命令行中打 Guidgen ,然后回車, Guidgen 就執(zhí)行了,單擊 New GUID 就會生成一個新的 GUID ;單擊 Copy 就可以把新生成的 GUID復(fù)制到剪貼板上。然后在 SplitFilter.h 文件上粘貼進(jìn)來,最后是這個樣子:/ GUID/ 3DCD790F-B7A0-429a-B9E1-3CE3255D8D1CDEFINE_GUID(<<name>>,0x3dcd790f, 0xb7a0, 0x429a, 0xb9, 0xe1, 0x3c, 0xe3, 0x25, 0x5d, 0x8d, 0x1c)
53、;然后把其中的 <<name>> 換成自己設(shè)置的名字,如下:/ 3DCD790F-B7A0-429a-B9E1-3CE3255D8D1CDEFINE_GUID(CLSID_SplitFilter,0x3dcd790f, 0xb7a0, 0x429a, 0xb9, 0xe1, 0x3c, 0xe3, 0x25, 0x5d, 0x8d, 0x1c);然后在 SplitFilter.cpp 中加入#include <initguid.h>再來修改構(gòu)造函數(shù),形式如下:CSplitFilter:CSplitFilter() : CTransformFilter(NAM
54、E("SplitFilter"), 0, CLSID_SplitFilter)5、處理媒體類型首先要明白一點,兩個 filter 連接,也就是兩個 filter 的輸出 pin 和輸入 pin 在進(jìn)行連接,這個工作是由輸出 pin 發(fā)起的, 由輸入 pin 來檢查媒體類型是否匹配, 并決定是否接受 這個連接。在 CTransformFilter 中協(xié)商媒體類型的工作是由 CTransformFilter 來完成的, 這個工作本來是應(yīng)該由 pin 來完成的,但是 CTransformFilter 中的 pin 只是簡單的調(diào)用CTransformFilter 中相應(yīng)的函數(shù)而已,
55、所以我們所有的工作只是重載 CTransformFilter 中 的三個虛函數(shù)而已:(1)實現(xiàn)CheckInputType(const CMediaType *mtIn) (不要問我如何添加這個函數(shù),如果這 個都不會的話趁早別看 dshow 了,趕緊去看 vc 的書去)。這個函數(shù)是由輸入 pin 來調(diào)用, 當(dāng)上游輸出 pin 要來進(jìn)行連接的時候, 我們的 filter 的輸入 pin 就會調(diào)用這個函數(shù)來檢查是 否支持上游輸出 pin 的媒體類型。其中的 CMediaType 類是對 AM_MEDIA_TYPE 結(jié)構(gòu)的 封裝, AM_MEDIA_TYPE 包含了有關(guān)的媒體類型,具體可查閱 dxs
56、dk 文檔。因為我這里只 是寫一個框架來為大家演示,功能只是傳遞 sample ,并不對數(shù)據(jù)有任何的修改,所以任何 的格式都可以,所以不管什么格式我們都應(yīng)該返回ok ,實際函數(shù)如下:HRESULT CSplitFilter:CheckInputType(const CMediaType *mtIn) / Everything is good.return S_OK;( 2 )實現(xiàn) GetMediaType(int iPosition, CMediaType *pMediaType)前面我們已經(jīng)講了輸入 pin 接受連接的時候用的函數(shù),那么這個函數(shù)呢就是輸入 pin 進(jìn)行連接的時候用的, 輸出
57、pin 進(jìn)行連接的時候首先要有一個支持的媒體類型列表, 這個函 數(shù)就是來生成這個列表的。實際上我們也可以直接返回一個 ok 了事,但是這樣做有點太不 負(fù)責(zé)任,我們雖然什么工作都不做,但是還是要把例行的檢查做完了,這樣老板( filter graph )看到了也會很滿意,你們是不是也很贊同?首先, 我們要確定輸入 pin 是否已經(jīng)連接了, 如果輸入 pin 沒有連接, 那我們和下面的?畫餅是不filter 連接了也沒有意義, 因為上游沒有數(shù)據(jù)傳過來, 我們拿什么給后面的 filter 能充饑的:)ASSERT(m_pInput->IsConnected();然后,看看 pin 的位置是否正確if (iPosition < 0)return E_INVALIDARG;這個函數(shù)最后的結(jié)果是這樣的:HRESULT CSplitFilter:GetMediaType(int iPosition, CMediaType *pMediaType)/ Is the input pin connectedif(m_pInput->IsConnected() = FALSE)return E_UNEXPECTED;/ This should never happenif(iPosition &
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 節(jié)水技術(shù)的推廣與應(yīng)用計劃
- 確保企業(yè)形象的管理措施計劃
- 進(jìn)口醫(yī)療器械使用與管理計劃
- 增強(qiáng)社區(qū)兒童保護(hù)意識的個人方案計劃
- 健身教練技能提升計劃
- 班主任對學(xué)生品德培養(yǎng)的貢獻(xiàn)計劃
- 榮格游戲治療
- 《貴州恒睿礦業(yè)有限公司福泉市龍昌鎮(zhèn)順意煤礦(兼并重組)礦產(chǎn)資源綠色開發(fā)利用方案(三合一)》評審意見
- 檔案基本知識培訓(xùn)課件
- 第八章 走進(jìn)國家第一節(jié)日本(第1課時)教學(xué)設(shè)計2023-2024學(xué)年下學(xué)期七年級地理下冊同步課堂系列(湘教版)
- 2024年湖南科技職業(yè)學(xué)院高職單招語文歷年參考題庫含答案解析
- 《性病防治知識講座》課件
- 定額〔2025〕2號文-關(guān)于發(fā)布2020版電網(wǎng)技術(shù)改造及檢修工程概預(yù)算定額2024年下半年價格
- 2024年河南省中職對口升學(xué)高考語文試題真題(原卷版)
- 卵巢囊腫護(hù)理病例討論
- 《無線局域網(wǎng)組建》課件-0無線課程概述
- 拉薩市2025屆高三第一次聯(lián)考(一模)語文試卷(含答案解析)
- 危險品運(yùn)輸行業(yè)可行性分析報告
- 2024解析:第八章牛頓第一定律、二力平衡-講核心(解析版)
- 《勞動法與勞動關(guān)系》課件
- 2025陜西延長石油(集團(tuán))有限責(zé)任公司招聘(1881人)筆試備考題庫及答案解析
評論
0/150
提交評論