




版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡介
1、深入淺出DirectShow FilterFilter概述Filter是一個(gè)COM組件,由一個(gè)或多個(gè)Pin組成。Pin也是一個(gè)COM組件。Filter文件的擴(kuò)展名為.ax,但也可以是.dll。Filter根據(jù)其包含Input pin或Output pin的情況(或在Filter Graph的位置),大致可分為三類:Source Filter(僅有Output pin)、Transform Filter(同時(shí)具有Input pin和Output pin)和Renderer Filter(僅有Input pin)。一般情況下,創(chuàng)建Filter使用一個(gè)普通的Win32 DLL項(xiàng)目。而且,一般Filt
2、er項(xiàng)目不使用MFC。這時(shí),應(yīng)用程序通過CoCreateInstance函數(shù)Filter實(shí)例;Filter與應(yīng)用程序在二進(jìn)制級(jí)別的協(xié)作。另外一種方法,也可以在MFC的應(yīng)用程序項(xiàng)目中創(chuàng)建Filter。這種情況下,F(xiàn)ilter不需注冊(cè)為COM組件,F(xiàn)ilter與應(yīng)用程序之間的協(xié)作是源代碼級(jí)別的;創(chuàng)建Filter實(shí)例,不再使用CoCreateInstance函數(shù),而是直接new出一個(gè)Filter對(duì)象,如下:m_pFilterObject = new CFilterClass();/ make the initial refcount 1 to match COM creationm_pFilterO
3、bject ->AddRef();因?yàn)镕ilter的基類實(shí)現(xiàn)了對(duì)象的引用計(jì)數(shù),所以即使在第二種情況下,對(duì)創(chuàng)建后的Filter對(duì)象的操作也完全可以遵循COM標(biāo)準(zhǔn)。Filter是一個(gè)獨(dú)立功能模塊,最好不要將Filter依賴于其他第三方的DLL。因?yàn)镕ilter具有COM的位置透明性特點(diǎn),F(xiàn)ilter文件可以放在硬盤的任何位置,只要位置移動(dòng)后重新注冊(cè)。但此時(shí),如果Filter依賴其他DLL,則Filter對(duì)該DLL的定位就會(huì)出現(xiàn)問題。Filter不能脫離Filter Graph單獨(dú)使用。所以,如果你想繞過Filter Graph直接使用Filter實(shí)現(xiàn)的模塊功能,請(qǐng)將你的Filter移植成DM
4、O(DirectX Media Object)。2. Filter的注冊(cè)Filter是COM組件,所以在使用前一定要注冊(cè)。Filter的注冊(cè)程序?yàn)閞egsvr32.exe。如果帶上命令行參數(shù)/u,表示注銷;如果帶上是/s,表示不彈出任何注冊(cè)/注銷成功與否的提示對(duì)話框。如果你想在Build Filter項(xiàng)目的時(shí)候進(jìn)行自動(dòng)注冊(cè),請(qǐng)?jiān)赩C的Project settings的Custom Build頁如下設(shè)置:Description: Register filterCommands: regsvr32 /s /c $(TargetPath) echo regsvr32 exe.time > $(
5、TargetDir)$(TargetName).trgOutputs: $(TargetDir)$(TargetName).trgFilter的注冊(cè)信息包括兩部分:基本的COM信息和Filter信息。注冊(cè)信息都存放在注冊(cè)表中。前者的位置為:HKEY_CLASSES_ROOTCLSIDFilter Clsid,后者的位置為:HKEY_CLASSES_ROOTCLSIDCategoryInstance Filter Clsid。COM信息標(biāo)示了Filter是一個(gè)標(biāo)準(zhǔn)的可以通過CoCreateInstance函數(shù)創(chuàng)建的COM組件,F(xiàn)ilter信息標(biāo)示了我們通過Graphedit看到的描述這個(gè)Filt
6、er的信息。如果你不想讓Graphedit看到(或者讓Filter枚舉器找到)你寫的Filter,你完全可以不注冊(cè)Filter信息。而且不用擔(dān)心,你這么做也完全不會(huì)影響Filter的功能。屏蔽注冊(cè)Filter信息的方法也很簡單。因?yàn)镃BaseFilter實(shí)現(xiàn)了IAMovieSetup接口的兩個(gè)函數(shù):Register和Unregister。我們只需重載這兩個(gè)函數(shù),直接return S_OK就行了。(注意:IAMovieSetup是用以注冊(cè)Filter信息部分的接口,但已經(jīng)廢棄,僅在AMovieDllRegisterServer和AMovieDllUnregisterServer調(diào)用才會(huì)用到。新寫
7、的Filter注冊(cè)函數(shù)一般使用AMovieDllRegisterServer2,這個(gè)函數(shù)不使用IAMovieSetup接口。如果想要不注冊(cè)Filter信息,最好自己實(shí)現(xiàn)Filter的兩個(gè)導(dǎo)出函數(shù):DllRegisterServer和DllUnregisterServer,其中只使用RegisterAllServers函數(shù)注冊(cè)O(shè)le Server。)Filter的Merit值。這個(gè)值是微軟的“智能連接”函數(shù)使用的。在Graphedit中,當(dāng)我們加入一個(gè)Source Filter后,在它的pin上執(zhí)行“Render”,會(huì)自動(dòng)連上一些Filter。Merit的值參考如下:MERIT_PREFERRE
8、D = 0x800000,MERIT_NORMAL = 0x600000,MERIT_UNLIKELY = 0x400000,MERIT_DO_NOT_USE = 0x200000,MERIT_SW_COMPRESSOR = 0x100000,MERIT_HW_COMPRESSOR = 0x100050Merit值只有大于MERIT_DO_NOT_USE的時(shí)候才有可能被“智能連接”使用;Merit的值越大,這個(gè)Filter的機(jī)會(huì)就越大。3. Filter之間Pin的連接過程Filter只有加入到Filter Graph中并且和其它Filter連接成完整的鏈路后,才會(huì)發(fā)揮作用。Filter之間的
9、連接(也就是Pin之間的連接),實(shí)際上是連接雙方的一個(gè)Media type的協(xié)商過程。連接的方向總是從Output pin指向Input pin。連接的大致過程為:如果調(diào)用連接函數(shù)時(shí)已經(jīng)指定了完整的Media type,則用這個(gè)Media type進(jìn)行連接,成功與否都結(jié)束連接過程;如果沒有指定或不完全指定了Media type,則進(jìn)入下面的枚舉過程。枚舉欲連接的Input pin上所有的Media type,逐一用這些Media type與Output pin進(jìn)行連接(如果連接函數(shù)提供了不完全Media type,則要先將每個(gè)枚舉出來的Media type與它進(jìn)行匹配檢查),如果Output
10、pin也接受這種Media type,則Pin之間的連接宣告成功;如果所有Input pin上枚舉的Media type,Output pin都不支持,則枚舉Output pin上的所有Media type,并逐一用這些Media type與Input pin進(jìn)行連接。如果Input pin接受其中的一種Media type,則Pin之間的連接到此也宣告成功;如果Output pin上的所有Media type,Input pin都不支持,則這兩個(gè)Pin之間的連接過程宣告失敗。每個(gè)Pin都可以實(shí)現(xiàn)GetMediaType函數(shù)來提供該P(yáng)in上支持的所有Preferred Media type(但
11、一般只在Output pin上實(shí)現(xiàn),Input pin主要實(shí)現(xiàn)CheckMediaType看是否支持當(dāng)前提供的Media type就行了)。連接過程中,Pin上枚舉得到的所有Media type就是這里提供的。在CBasePin類中有一個(gè)protected的成員變量m_bTryMyTypesFirst,默認(rèn)值為false。在我們定制Filter的Output pin中改變這個(gè)變量的值為true,可以定制我們自己的連接過程(先枚舉Output pin上的Media type)。當(dāng)Pin之間的連接成功后,各自的pin上都會(huì)調(diào)用CompleteConnect函數(shù)。我們可以在這里取得一些連接上的Med
12、ia type的信息,以及進(jìn)行一些計(jì)算等。在Output pin的CompleteConnect實(shí)現(xiàn)中,還有一個(gè)重要的任務(wù),就是協(xié)商Filter Graph運(yùn)行起來后Sample傳輸使用的內(nèi)存配置情況。這同樣是一個(gè)交互過程:首先要詢問一下Input pin上的配置要求,如果Input pin提供內(nèi)存管理器(Allocator),則優(yōu)先使用Input pin上的內(nèi)存管理器;否則,使用Output pin自己生成的內(nèi)存管理器。我們一般都要實(shí)現(xiàn)DecideBufferSize來決定存放Sample的內(nèi)存大小。注意:這個(gè)過程協(xié)商完成之后,實(shí)際的內(nèi)存并沒有分配,而要等到Output pin上的Activ
13、e函數(shù)調(diào)用。4. Filter Media type概述Media type一般可以有兩種表示:AM_MEDIA_TYPE和CMediaType。前者是一個(gè)Struct,后者是從這個(gè)Struct繼承過來的類。每個(gè)Media type有三部分組成:Major type、Subtype和Format type。這三個(gè)部分都使用GUID來唯一標(biāo)示。Major type主要定性描述一種Media type,比如指定這是一個(gè)Video,或Audio或Stream等;Subtype進(jìn)一步細(xì)化Media type,如果Video的話可以進(jìn)一步指定是UYVY或YUY2或RGB24或RGB32等;Format
14、type用一個(gè)Struct更進(jìn)一步細(xì)化Media type。如果Media type的三個(gè)部分都是指定了某個(gè)具體的GUID值,則稱這個(gè)Media type是完全指定的;如果Media type的三個(gè)部分中有任何一個(gè)值是GUID_NULL,則稱這個(gè)Media type 是不完全指定的。GUID_NULL具有通配符的作用。常用的Major type:MEDIATYPE_Video;MEDIATYPE_Audio;MEDIATYPE_AnalogVideo; / Analog captureMEDIATYPE_AnalogAudio;MEDIATYPE_Text;MEDIATYPE_Midi;MED
15、IATYPE_Stream;MEDIATYPE_Interleaved; / DV camcorderMEDIATYPE_MPEG1SystemStream;MEDIATYPE_MPEG2_PACK;MEDIATYPE_MPEG2_PES;MEDIATYPE_DVD_ENCRYPTED_PACK;MEDIATYPE_DVD_NAVIGATION;常用的Subtype:MEDIASUBTYPE_YUY2;MEDIASUBTYPE_YVYU;MEDIASUBTYPE_YUYV;MEDIASUBTYPE_UYVY;MEDIASUBTYPE_YVU9;MEDIASUBTYPE_Y411;MEDIASU
16、BTYPE_RGB4;MEDIASUBTYPE_RGB8;MEDIASUBTYPE_RGB565;MEDIASUBTYPE_RGB555;MEDIASUBTYPE_RGB24;MEDIASUBTYPE_RGB32;MEDIASUBTYPE_ARGB32; / Contains alpha valueMEDIASUBTYPE_Overlay;MEDIASUBTYPE_MPEG1Packet;MEDIASUBTYPE_MPEG1Payload; / Video payload MEDIASUBTYPE_MPEG1AudioPayload; / Audio payloadMEDIASUBTYPE_M
17、PEG1System; / A/V payloadMEDIASUBTYPE_MPEG1VideoCD;MEDIASUBTYPE_MPEG1Video;MEDIASUBTYPE_MPEG1Audio;MEDIASUBTYPE_Avi;MEDIASUBTYPE_Asf;MEDIASUBTYPE_QTMovie;MEDIASUBTYPE_PCM;MEDIASUBTYPE_WAVE;MEDIASUBTYPE_dvsd; / DVMEDIASUBTYPE_dvhd;MEDIASUBTYPE_dvsl;MEDIASUBTYPE_MPEG2_VIDEO;MEDIASUBTYPE_MPEG2_PROGRAM;
18、 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_DVD_NAVIGATION_PROVIDER;常用的Format type:FORMAT_NoneFORMAT_DvInfo DVINFOFORMAT_MPEGVideo MP
19、EG1VIDEOINFOFORMAT_MPEG2Video MPEG2VIDEOINFOFORMAT_VideoInfo VIDEOINFOHEADERFORMAT_VideoInfo2 VIDEOINFOHEADER2FORMAT_WaveFormatEx WAVEFORMATEX5. Filter之間的數(shù)據(jù)傳送Filter之間的數(shù)據(jù)是通過Sample來傳送的。Sample是一個(gè)COM組件,擁有自己的一段數(shù)據(jù)緩沖。Sample由Allocator統(tǒng)一管理。如下圖所示:1 完整類型如果媒體類型每一個(gè)部分都定義的很完成,那么pin 就嚴(yán)格按照定義的類型進(jìn)行連接。如果不匹配,連接失敗。2 部分媒體
20、類型如果媒體類型的機(jī)構(gòu)中,major type, subtype, or format type 的值為GUID_NULL,這個(gè)值是一個(gè)通配符號(hào)。任何類型都可以匹配。3 沒有媒體類型如果filter 圖表管理器傳遞過來一個(gè)NULL 的指針,這個(gè)pin 就可以和任意的類型的媒體類型匹配。Filter之間數(shù)據(jù)傳送的方式有兩種:Push模式和Pull模式。所謂Push模式,即Source filter自己能夠產(chǎn)生數(shù)據(jù),并且一般在它的Output pin上有獨(dú)立的子線程負(fù)責(zé)將數(shù)據(jù)發(fā)送出去,常見的情況如WDM模型的采集卡的Live Source Filter;而所謂Pull模式,即Source filt
21、er不具有把自己的數(shù)據(jù)送出去的能力,這種情況下,一般Source filter后緊跟著接一個(gè)Parser Filter或Splitter Filter,這種Filter一般在Input pin上有個(gè)獨(dú)立的子線程,負(fù)責(zé)不斷地從Source filter索取數(shù)據(jù),然后經(jīng)過處理后將數(shù)據(jù)傳送下去,常見的情況如File source。Push模式下,Source filter是主動(dòng)的;Pull模式下,Source filter是被動(dòng)的。而事實(shí)上,如果將上圖Pull模式中的Source filter和Splitter Filter看成另一個(gè)虛擬的Source filter,則后面的Filter之間的數(shù)據(jù)傳
22、送也與Push模式完全相同。那么,數(shù)據(jù)到底是怎么通過連接著的Pin傳送的呢?首先來看Push模式。在Source filter后面Filter的 Input pin上,一定實(shí)現(xiàn)了一個(gè)IMemInputPin接口,數(shù)據(jù)正是通過上一級(jí)Filter調(diào)用這個(gè)接口的Receive方法進(jìn)行傳送的。值得注意的是,數(shù)據(jù)從Output pin通過Receive方法調(diào)用傳送到Input pin上,并沒有進(jìn)行內(nèi)存拷貝,它只是一個(gè)相當(dāng)于數(shù)據(jù)到達(dá)的“通知”。再看一下Pull模式。Pull模式下的Source filter的 Output pin上,一定實(shí)現(xiàn)了一個(gè)IAsyncReader接口;其后面的Splitter F
23、ilter,就是通過調(diào)用這個(gè)接口的Request方法或者SyncRead方法來獲得數(shù)據(jù)。Splitter Filter然后像Push模式一樣,調(diào)用下一級(jí)Filter的Input pin上的IMemInputPin接口Receive方法實(shí)現(xiàn)數(shù)據(jù)的往下傳送。一個(gè)DirectShow的應(yīng)用程序,至少會(huì)有兩條線程:主線程和Filter用于數(shù)據(jù)傳送的子線程。既然是多線程,就不可避免會(huì)出現(xiàn)線程同步問題。Filter的狀態(tài)改變都在主線程中完成,F(xiàn)ilter的數(shù)據(jù)相關(guān)操作都在數(shù)據(jù)線程中調(diào)用。各線程一些主要函數(shù)調(diào)用參考如下:Streaming thread(s): IMemInputPin:Receive, I
24、MemInputPin:ReceiveMultiple, IPin:EndOfStream, IMemAllocator:GetBuffer. Application thread: IMediaFilter:Pause, IMediaFilter:Run, IMediaFilter:Stop, IMediaSeeking:SetPositions, IPin:BeginFlush, IPin:EndFlush. Either: IPin:NewSegment. 這些函數(shù)切忌混合調(diào)用,否則會(huì)引起線程的死鎖。另外值得注意的是,BeginFlush和EndFlush屬于主線程調(diào)用,而不是數(shù)據(jù)線程調(diào)
25、用。6. Transform filter和Trans-in-place filter的區(qū)別首先,這兩種Filter是有共同點(diǎn)的,因?yàn)門rans-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是ReadOnl
26、y的,而Trans-in-place filter又要修改Sample的數(shù)據(jù),則Trans-in-place filter的Input pin和Output pin將不得不使用不同的Allocator。)Trans-in-place filter有一個(gè)protected的成員變量m_bModifiesData,默認(rèn)值為true。如果你確信定制Trans-in-place filter不需要修改Sample數(shù)據(jù),則將m_bModifiesData賦值為false,這樣可以保證Input pin和Output pin使用相同的Allocator。Trans-in-place filter的實(shí)現(xiàn)主要
27、體現(xiàn)在以下三個(gè)函數(shù):CTransInPlaceFilter:CompleteConnect、CTransInPlaceInputPin:GetAllocator和CTransInPlaceInputPin:NotifyAllocator。CompleteConnect中進(jìn)行必要的重連(Reconnect),保證Trans-in-place filter的Input pin和Output pin使用相同的Media type。GetAllocator能夠取得Trans-in-place filter下一級(jí)Filter的Input pin上的Allocator。NotifyAllocator“盡力
28、”使Trans-in-place filter的Input pin和Output pin使用同一個(gè)Allocator。7. IMediaSeeking的實(shí)現(xiàn)IMediaSeeking的實(shí)現(xiàn)在Filter上,但應(yīng)用程序應(yīng)該從Filter Graph Manager上得到這個(gè)接口。在Filter級(jí)別,F(xiàn)ilter Graph Manager首先從Renderer filter開始詢問上一級(jí)Filter的Output pin是否支持IMediaSeeking接口。如果支持,則返回這個(gè)接口;如果不支持,則繼續(xù)往上一級(jí)Filter詢問,直到Source filter。一般在Source filter的O
29、utput pin上實(shí)現(xiàn)IMediaSeeking接口。(如果是File source,一般在Parser Filter或Splitter Filter實(shí)現(xiàn)這個(gè)接口。)對(duì)于Filter開發(fā)者來說,如果我們寫的是Source filter,就要在Filter的Output pin上實(shí)現(xiàn)IMediaSeeking接口;如果寫的是Transform filter,只需要在Output pin上將用戶的接口請(qǐng)求往上傳遞給上一級(jí)Filter的Output pin;如果寫的是Renderer Filter,需要在Filter上將用戶的接口請(qǐng)求往上傳遞給上一級(jí)Filter的Output pin。注意:為了保
30、證Seek操作后Stream的同步性,如果實(shí)際實(shí)現(xiàn)IMediaSeeking接口的Filter有多個(gè)Output pin,一般僅有一個(gè)pin支持Seek操作。對(duì)于你定制的Transform filter,如果有多個(gè)Input pin,你需要自己決定當(dāng)Output pin接收到IMediaSeeking接口請(qǐng)求時(shí)選擇哪一條路徑往上繼續(xù)請(qǐng)求。應(yīng)用程序能夠在任何時(shí)候(running, paused or stopped)對(duì)Filter graph執(zhí)行Seek操作。但當(dāng)Filter graph正在running的時(shí)候,F(xiàn)ilter graph manager會(huì)先pause住,執(zhí)行完Seek操作后,再重
31、新run起來。IMediaSeeking可以有如下幾種Seek的時(shí)間格式:TIME_FORMAT_FRAME Video frames.TIME_FORMAT_SAMPLE Samples in the stream.TIME_FORMAT_FIELD Interlaced video fields.TIME_FORMAT_BYTE Byte offset within the stream.TIME_FORMAT_MEDIA_TIME Reference time (100-nanosecond units).但實(shí)現(xiàn)這個(gè)接口的Filter未必支持所有的這些格式。一般Filter都會(huì)支持TIM
32、E_FORMAT_MEDIA_ TIME,當(dāng)使用其它的格式時(shí),最好調(diào)用IMediaSeeking:IsFormatSupported進(jìn)行一下確認(rèn)。對(duì)于Filter,不贊成使用IMediaPosition接口。IMediaPosition是用以支持Automation的(比如VB里面使用DirectShow),IMediaSeeking不支持Automation。8. 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),是為了
33、快速切換到running狀態(tài)而設(shè)計(jì)的。在paused狀態(tài)下,數(shù)據(jù)線程是啟動(dòng)的,但被Renderer filter阻塞了。paused與running兩者間的狀態(tài)轉(zhuǎn)換,對(duì)于Source filter和Transform filter可以忽略不計(jì),而對(duì)于Renderer filter(特別是Video renderer / Audio renderer)情形稍有不同。Renderer首先處理那個(gè)paused狀態(tài)下Hold的Sample,當(dāng)接收到新的Sample時(shí),判斷Sample上的時(shí)間戳。如果時(shí)間未到,Renderer會(huì)Hold住這個(gè)Sample進(jìn)行等待。Filter graph manager
34、以從下到上的順序?qū)ilter進(jìn)行狀態(tài)轉(zhuǎn)換,即從Renderer filter一直回溯到Source filter。這個(gè)順序能夠有效地避免Sample的丟失以及Filter graph的死鎖。Stopped to paused:首先從Renderer開始進(jìn)行paused狀態(tài)的轉(zhuǎn)換。這時(shí),F(xiàn)ilter調(diào)用自己所有Pin的Active函數(shù)進(jìn)行初始化(一般Pin在Active中進(jìn)行Sample內(nèi)存的分配,如果是Source filter還將啟動(dòng)數(shù)據(jù)線程),使Filter處于一種就緒狀態(tài)。Source filter是最后一個(gè)完成到就緒狀態(tài)轉(zhuǎn)換的Filter。然后,Source filter啟動(dòng)數(shù)據(jù)線程
35、,往下發(fā)送Sample。當(dāng)Renderer接收到第一個(gè)Sample后就阻塞住。當(dāng)所有的Renderer實(shí)現(xiàn)了狀態(tài)轉(zhuǎn)換,F(xiàn)ilter graph manager才認(rèn)為狀態(tài)轉(zhuǎn)換完成Paused to stopped:當(dāng)Filter進(jìn)入stopped狀態(tài)時(shí),調(diào)用自己所有Pin的Inactive函數(shù)(一般Pin在Inactive中進(jìn)行Sample內(nèi)存的釋放,如果是Source filter還將終止數(shù)據(jù)線程)。釋放所有Hold的Sample,以使上一級(jí)Filter的GetBuffer脫離阻塞;終止所有在Receive中的等待,以使上一級(jí)Filter的Receive函數(shù)調(diào)用返回。Filter在stoppe
36、d狀態(tài)下拒絕接受任何Sample。這樣從Renderer filter往上一級(jí)一級(jí)脫離阻塞,當(dāng)?shù)竭_(dá)Source filter的時(shí)候,可以確保數(shù)據(jù)線程終止。9. EndOfStream問題當(dāng)Source filter的所有數(shù)據(jù)都已經(jīng)發(fā)送出去,則會(huì)調(diào)用下一級(jí)Filter的Input pin上的IPin:EndOfStream,直到Renderer filter。當(dāng)這個(gè)Renderer filter的所有Input pin都被調(diào)用了EndOfStream,則向Filter graph manager發(fā)送一個(gè)EC_COMPLETE事件。僅當(dāng)Filter graph中的所有Stream都發(fā)送了EC_CO
37、MPLETE事件,F(xiàn)ilter graph manager才會(huì)將這個(gè)事件發(fā)送給應(yīng)用程序。在我們定制的Filter中,如果接收到了上一級(jí)Filter傳過來的EndOfStream,則說明上面的數(shù)據(jù)已經(jīng)全部傳送完畢,Receive方法不須再接收數(shù)據(jù)。如果我們對(duì)數(shù)據(jù)進(jìn)行了緩沖,則應(yīng)確認(rèn)緩沖中的所有數(shù)據(jù)都被處理完并往下發(fā)送了,然后再往下調(diào)用EndOfStream。Pull模式下,一般是Splitter filter或Parser filter發(fā)送EndOfStream,而且方向是往下的,Source filter上不會(huì)收到這樣的通知。10. BeginFlush、EndFlush、NewSegment
38、問題典型的情況,當(dāng)進(jìn)行MediaSeeking之后,會(huì)調(diào)用BeginFlush、EndFlush。一般在Input pin上實(shí)現(xiàn)這兩個(gè)函數(shù)。對(duì)于Filter開發(fā)者來說,F(xiàn)ilter在被調(diào)用BeginFlush時(shí)需要做以下工作:· 調(diào)用下一級(jí)Filter的BeginFlush,使其不再接收新的Sample;· 拒絕接收上一級(jí)Filter的數(shù)據(jù),包括Receive調(diào)用和EndOfStream調(diào)用;· 如果上一級(jí)Filter正在阻塞等待空的Sample,此時(shí)需要讓它脫離阻塞(通過析構(gòu)Allocator);· 確保數(shù)據(jù)流線程脫離阻塞狀態(tài)。Filter在被調(diào)用En
39、dFlush時(shí)需要做以下工作:· 確保所有等待緩存的Sample被丟棄;· 確保Filter上已經(jīng)緩存的數(shù)據(jù)被丟棄;· 清除沒有發(fā)出去的EC_COMPLETE事件(如果這是一個(gè)Rendered input pin);· 調(diào)用下一級(jí)Filter的EndFlush。還有一點(diǎn):如果你必須在定制的Filter中為每個(gè)Sample打Time stamp,那么記住在MediaSeeking之后出去的Sample的Time stamp應(yīng)該從0開始重打。Segment是一段時(shí)間內(nèi)具有相同的Playback rate的一組Sample,以NewSegment函數(shù)調(diào)用來表示
40、這個(gè)Segment的開始。NewSegment一般在開始新的Stream的時(shí)候,或者用戶進(jìn)行了MediaSeeking之后,由Source filter(Push模式下)或Parser/Splitter filter(Pull模式下)發(fā)起,并往下層層調(diào)用,一直到Renderer filter。在我們定制的Filter中可以利用NewSegment傳遞下來的信息,特別是對(duì)于Decoder。對(duì)于Audio renderer也是一個(gè)典型例子,它根據(jù)Playback rate和Audio實(shí)際的采樣頻率來對(duì)聲卡產(chǎn)生輸出。11. Quality Control問題Filter之間的數(shù)據(jù)傳送,有時(shí)候過快,有
41、時(shí)候過慢。DirectShow使用Quality Control來解決這個(gè)問題,即IQualityControl接口的兩個(gè)函數(shù)(SetSink和Notify)。一般,Renderer filter在Filter上實(shí)現(xiàn)這個(gè)接口,而其他Filter在Output pin上實(shí)現(xiàn)這個(gè)接口。上圖為一般的Quality Control的處理過程。而能夠調(diào)整發(fā)送速度的IQualityControl接口一般在Source filter(pull模式下為parser/splitter filter)上實(shí)現(xiàn),Transform filter只是將Quality Message往上一級(jí)Filter傳遞。應(yīng)用程序可以
42、實(shí)現(xiàn)自己的Quality Control Manager,然后通過調(diào)用SetSink方法設(shè)置給Filter。上述的處理過程就改變了,Quality Message直接發(fā)送給自定義的Manager。12. 對(duì)運(yùn)行過程中Media type改變的支持我們可以從CBaseInputPin:Receive中可以看到,Input pin每次在接收Sample的之前,一般都會(huì)進(jìn)行CheckStreaming(如果當(dāng)前Filter已經(jīng)Stop或正在Flush或發(fā)生了RuntimeError,則拒絕接收Sample),然后將當(dāng)前的Sample屬性保存到protected的m_SampleProps成員變量中。
43、描述Sample屬性的是一個(gè)AM_SAMPLE2_PROPERTIES的結(jié)構(gòu),它有一個(gè)標(biāo)記來表明當(dāng)前Sample的Media type是否已經(jīng)改變。如果Media type改變了,則進(jìn)行CheckMedaiType看我們的Filter是否仍然支持它。如果不支持,則發(fā)出一個(gè)RuntimeError,并發(fā)送EndOfStream。一個(gè)健全的Filter應(yīng)該能夠?qū)\(yùn)行時(shí)Media type的改變做出處理。在我們的Receive方法實(shí)現(xiàn)中,我們可以通過if (pProps->dwSampleFlags & AM_SAMPLE_TYPECHANGED)來判斷Meida type是否已經(jīng)改變
44、;如果改變,我們需要根據(jù)新的Media type進(jìn)行必要的初始化。一個(gè)典型的案例:當(dāng)Camcorder輸入時(shí),Audio的Media type可能改變。比如,F(xiàn)ilter連接時(shí)Media type使用了MEDIATYPE_PCM,而在運(yùn)行時(shí)又換成了MEDIATYPE_WAVE;或者連接時(shí)Audio的采樣頻率時(shí)44.1K,而在運(yùn)行時(shí)卻變成了48K;或者Camcorder的帶子上本身保存了混合的44.1K和48K的Audio。創(chuàng)建一個(gè)filter實(shí)例 1、選擇所要?jiǎng)?chuàng)建的filter的用途,據(jù)此來選擇基類?;惪梢詮腃TransformFilter、CTransInPlaceFilter、
45、CVideoTransformFilter和CBaseFilter中來選取。(1) CTransInPlaceFilter提供了本地處理Sample的機(jī)制(Sample可以認(rèn)為是存儲(chǔ)一個(gè)視頻幀的結(jié)構(gòu)),當(dāng)一個(gè)trans-in-place filter收到一個(gè)sample時(shí),你可以通過重載它的Transform()函數(shù)來修改其中的數(shù)據(jù),trans-in-place filter會(huì)在Transform()函數(shù)執(zhí)行完后直接把這個(gè)sample傳遞給下一個(gè)filter。(2) CTransformFilter完成的功能與CTransInPlaceFilter一樣,它們的區(qū)別就是CTransformFil
46、ter總是把上游filter傳遞過來的sample復(fù)制一份,并把復(fù)制后的sample傳遞給下一個(gè)filter。當(dāng)然,你可以通過重載Transform()函數(shù)來控制這個(gè)過程,包括修改其中的數(shù)據(jù)(這也是自己寫filter的原因)。(3)CVideoTransformFilter與CTransformFilter一樣,只是多加了質(zhì)量控制功能。(4)以上三個(gè)filter都繼承于CBaseFilter,所以如果想對(duì)filter進(jìn)行更多的控制,就要直接從CBaseFilter來繼承,但是所要做的工作也最多。在這個(gè)例子中,我選擇CTransformFilter,因?yàn)镃TransInPlaceFilter太簡
47、單了,dx9sdk中的例子NullNull就是一個(gè)完整的CTransInPlaceFilter的框架,并且只是一個(gè)框架,什么工作也沒有做,如果要用的話直接修改就可以用了。2、在vc中選擇win32 dll,創(chuàng)建一個(gè)dll,名字隨便取,這里我取SplitFilter,選擇空dll。3、然后,創(chuàng)建一個(gè)類CSplitFilter,繼承自CTransformFilter,當(dāng)然要選擇public方式。4、為filter生成一個(gè)CLSID,可以使用Guidgen,它的用法是在命令行中打Guidgen,然后回車,Guidgen就執(zhí)行了,單擊New GUID就會(huì)生成一個(gè)新的GUID;單擊Copy就可以把新生成
48、的GUID復(fù)制到剪貼板上。然后在SplitFilter.h文件上粘貼進(jìn)來,最后是這個(gè)樣子:/ GUID/ 3DCD790F-B7A0-429a-B9E1-3CE3255D8D1CDEFINE_GUID(<<name>>, 0x3dcd790f, 0xb7a0, 0x429a, 0xb9, 0xe1, 0x3c, 0xe3, 0x25, 0x5d, 0x8d, 0x1c);然后把其中的<<name>>換成自己設(shè)置的名字,如下:/ 3DCD790F-B7A0-429a-B9E1-3CE3255D8D1CDEFINE_GUID(CLSID_S
49、plitFilter, 0x3dcd790f, 0xb7a0, 0x429a, 0xb9, 0xe1, 0x3c, 0xe3, 0x25, 0x5d, 0x8d, 0x1c);然后在SplitFilter.cpp中加入#include <initguid.h>再來修改構(gòu)造函數(shù),形式如下:CSplitFilter:CSplitFilter() : CTransformFilter(NAME("SplitFilter"), 0, CLSID_SplitFilter)5、處理媒體類型首先要明白一點(diǎn),兩個(gè)filter連接,也就是兩個(gè)filter的輸出pin和輸入pin在
50、進(jìn)行連接,這個(gè)工作是由輸出pin發(fā)起的,由輸入pin來檢查媒體類型是否匹配,并決定是否接受這個(gè)連接。在CTransformFilter中協(xié)商媒體類型的工作是由CTransformFilter來完成的,這個(gè)工作本來是應(yīng)該由pin來完成的,但是CTransformFilter中的pin只是簡單的調(diào)用CTransformFilter中相應(yīng)的函數(shù)而已,所以我們所有的工作只是重載CTransformFilter中的三個(gè)虛函數(shù)而已:(1)實(shí)現(xiàn)CheckInputType(const CMediaType *mtIn)(不要問我如何添加這個(gè)函數(shù),如果這個(gè)都不會(huì)的話趁早別看dshow了,趕緊去看vc的書去)。
51、這個(gè)函數(shù)是由輸入pin來調(diào)用,當(dāng)上游輸出pin要來進(jìn)行連接的時(shí)候,我們的filter的輸入pin就會(huì)調(diào)用這個(gè)函數(shù)來檢查是否支持上游輸出pin的媒體類型。其中的CMediaType類是對(duì)AM_MEDIA_TYPE結(jié)構(gòu)的封裝,AM_MEDIA_TYPE包含了有關(guān)的媒體類型,具體可查閱dxsdk文檔。因?yàn)槲疫@里只是寫一個(gè)框架來為大家演示,功能只是傳遞sample,并不對(duì)數(shù)據(jù)有任何的修改,所以任何的格式都可以,所以不管什么格式我們都應(yīng)該返回ok,實(shí)際函數(shù)如下:HRESULT CSplitFilter:CheckInputType(const CMediaType *mtIn)/ Everything
52、is good. return S_OK;(2)實(shí)現(xiàn)GetMediaType(int iPosition, CMediaType *pMediaType)前面我們已經(jīng)講了輸入pin接受連接的時(shí)候用的函數(shù),那么這個(gè)函數(shù)呢就是輸入pin進(jìn)行連接的時(shí)候用的,輸出pin進(jìn)行連接的時(shí)候首先要有一個(gè)支持的媒體類型列表,這個(gè)函數(shù)就是來生成這個(gè)列表的。實(shí)際上我們也可以直接返回一個(gè)ok了事,但是這樣做有點(diǎn)太不負(fù)責(zé)任,我們雖然什么工作都不做,但是還是要把例行的檢查做完了,這樣老板(filter graph)看到了也會(huì)很滿意,你們是不是也很贊同?首先,我們要確定輸入pin是否已經(jīng)
53、連接了,如果輸入pin沒有連接,那我們和下面的filter連接了也沒有意義,因?yàn)樯嫌螞]有數(shù)據(jù)傳過來,我們拿什么給后面的filter?畫餅是不能充饑的:)ASSERT(m_pInput->IsConnected();然后,看看pin的位置是否正確if (iPosition < 0) return E_INVALIDARG; 這個(gè)函數(shù)最后的結(jié)果是這樣的:HRESULT CSplitFilter:GetMediaType(int iPosition, CMediaTyp
54、e *pMediaType) / Is the input pin connected if(m_pInput->IsConnected() = FALSE) return E_UNEXPECTED; / This should never happen if(iPosition
55、 < 0) return E_INVALIDARG; / Do we have more items to offer if(iPosition > 0) return VFW_S_NO_MORE_ITEMS;
56、 CheckPointer(pMediaType,E_POINTER); *pMediaType = m_pInput->CurrentMediaType(); return NOERROR;(3)實(shí)現(xiàn)CheckTransform(const CMediaType *mtIn, const CMediaType *mtOut)雖然輸入pin和輸出pin的媒體類型都已經(jīng)協(xié)商過了,但是我們的filter是否能夠完成兩中媒體類型的轉(zhuǎn)換還是個(gè)未知數(shù),那
57、么就要調(diào)用CheckTransform來判斷我們的filter是否能夠支持這兩個(gè)媒體類型的轉(zhuǎn)換。我在這里的選擇是直接返回ok。呵呵:)6、設(shè)置分配器雖然我們已經(jīng)完成了媒體類型的匹配,但filter的連接還沒有完成,我們的pin還必須為連接選擇allocator,設(shè)置allocator的屬性,比如緩沖區(qū)的大小和數(shù)量等。輸入pin我們可以不用管它,因?yàn)槟J(rèn)的情況下它只需要同意輸出pin提供的allocator就可以了,但是輸出pin的allocator就要我們自己來搞定了。如果后面的filter提供了一個(gè)allocator,輸出pin可以使用這個(gè)allocator,否則就要建立一個(gè)新的alloca
58、tor。這就要用到CTransformFilter的DecideBufferSize(IMemAllocator *pAlloc, ALLOCATOR_PROPERTIES *pProp),pAlloc是一個(gè)allocator的指針,pProp是一個(gè)ALLOCATOR_PROPERTIES結(jié)構(gòu)體,代表后面的filter所需要的allocator的屬性。我們可以在這個(gè)函數(shù)中根據(jù)我們自己的filter的需要和后面的filter的需要來設(shè)置allocator的各項(xiàng)屬性,設(shè)置allocator的屬性可以用IMemAllocator:SetProperties函數(shù)。這里面最重要的就是設(shè)置buffer的大
59、小,一般是從輸入pin的媒體類型的大小來決定,如果從這里沒有得到固定的大小的話,我們就要猜buffer的大小了。最后的函數(shù)是這樣的:HRESULT CSplitFilter:DecideBufferSize(IMemAllocator *pAlloc, ALLOCATOR_PROPERTIES *pProperties) CheckPointer(pAlloc,E_POINTER); CheckPointer(pProperties,E_POINTER); / Is the
60、input pin connected if(m_pInput->IsConnected() = FALSE) return E_UNEXPECTED; HRESULT hr = NOERROR; pProperties->cBuffers = 1; pPro
61、perties->cbBuffer = m_pInput->CurrentMediaType().GetSampleSize(); ASSERT(pProperties->cbBuffer); / If we don't have fixed sized samples we must guess some size if(!m_pInput->CurrentMediaType().bFixedSizeSamples) &
62、#160; if(pProperties->cbBuffer < 100000) / nothing more than a guess! pProperties->cbBuffer = 100000; / Ask the allocator to reserve us some sample memory, NOTE the function / can succeed (t
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 山東省日照市莒縣一中2025屆高考?xì)v史試題終極仿真預(yù)測(cè)試卷含解析
- 石家莊理工職業(yè)學(xué)院《統(tǒng)計(jì)軟件R語言》2023-2024學(xué)年第二學(xué)期期末試卷
- 2025年網(wǎng)絡(luò)安全與信息法考試試卷及答案
- 江西省上饒市信州區(qū)2024-2025學(xué)年五年級(jí)數(shù)學(xué)第二學(xué)期期末達(dá)標(biāo)檢測(cè)模擬試題含答案
- 天津美術(shù)學(xué)院《腫瘤免疫治療學(xué)》2023-2024學(xué)年第二學(xué)期期末試卷
- 江蘇省興化市安豐初級(jí)中學(xué)2024-2025學(xué)年學(xué)業(yè)水平模擬考試化學(xué)試題仿真模擬試題B卷含解析
- 2025年職稱外語考試專項(xiàng)測(cè)試卷及答案
- 2025年注冊(cè)會(huì)計(jì)師考試試卷及答案
- 無錫工藝職業(yè)技術(shù)學(xué)院《基礎(chǔ)醫(yī)學(xué)實(shí)驗(yàn)(二)》2023-2024學(xué)年第二學(xué)期期末試卷
- 西安建筑科技大學(xué)華清學(xué)院《房地產(chǎn)項(xiàng)目投資與融資》2023-2024學(xué)年第二學(xué)期期末試卷
- 中醫(yī)內(nèi)科學(xué)(十版)
- 成品檢驗(yàn)記錄表
- DB33-T 2196-2019水利工程標(biāo)識(shí)牌設(shè)置規(guī)范
- 基于前藥原理的藥物設(shè)計(jì)解析課件
- 2022年上海海洋大學(xué)食品科學(xué)復(fù)試資料
- 病例報(bào)告表(CRF)模板
- 我把沒有送給你(課堂版)(1)
- 杭汽HNKS50-63-28型汽輪機(jī)大修施工方案
- Q∕GDW 12113-2021 邊緣物聯(lián)代理技術(shù)要求
- 劉半農(nóng)雨散文的特點(diǎn)
- 濰柴發(fā)動(dòng)機(jī)WD615系列分解圖冊(cè)
評(píng)論
0/150
提交評(píng)論