摘? 要: 討論了DirectShow過濾器組件的開發(fā)技術(shù),給出了網(wǎng)絡(luò)視頻應(yīng)用中的一個過濾器組件開發(fā)實例。
關(guān)鍵詞: DirectShow? 過濾器? COM? 視頻應(yīng)用
?
1? DirectShow概述
DirectShow是Windows平臺下流行的流媒體開發(fā)體系,可以實現(xiàn)高質(zhì)量的音視頻采集、編輯、編碼、解碼、格式轉(zhuǎn)換、播放。它解決了網(wǎng)絡(luò)音頻及視頻信息傳輸中數(shù)據(jù)量大、數(shù)據(jù)源種類多、客戶端軟硬件環(huán)境不確定、視頻音頻需要同步等問題,因此有著廣泛的應(yīng)用。
DirectShow使用模塊化的體系結(jié)構(gòu),最主要的組件是過濾器(Filter)。DirectShow把一系列過濾器組合起來形成DirectShow應(yīng)用程序。每個過濾器提供一種功能,如獲取數(shù)據(jù)源、編碼、解碼、播放等。DirectShow提供了很多標(biāo)準(zhǔn)過濾器,用戶可以直接使用。但由于媒體格式、壓縮方式、硬件屬性等方面的特殊要求,用戶經(jīng)常需要自行開發(fā)過濾器來滿足具體需求。
DirectShow應(yīng)用程序中主要包含以下3種過濾器:源過濾器(Source Filter)、轉(zhuǎn)換過濾器(Transform Filter)、呈現(xiàn)過濾器(Render Filter),分別負(fù)責(zé)獲取數(shù)據(jù)流、處理數(shù)據(jù)流和播放數(shù)據(jù)流。有時還需要分解過濾器(Splitter Filter)和合并過濾器(Mux Filter)來分解和合并數(shù)據(jù)流。
DirectShow是基于COM(組件對象模型)規(guī)范的。過濾器是一種COM組件。應(yīng)用程序把多個過濾器組件組合起來,形成對媒體流的處理流程。這一整套過濾器集合被稱為過濾器圖(Filter Graph)。DirectShow提供FGM(Filter Graph Manager)組件來控制整個過濾器圖。過濾器前后相連,連接點也是COM對象,被稱為針腳(Pin)。
DirectShow應(yīng)用程序的原理圖如圖1所示。來自文件系統(tǒng)或外設(shè)的數(shù)據(jù)先由過濾器處理,再存儲到文件系統(tǒng)或由外設(shè)播放。過濾器負(fù)責(zé)與文件系統(tǒng)和外設(shè)的交互。應(yīng)用程序只需控制過濾器,不用關(guān)心其他軟件和硬件的具體情況。
?
2?過濾器組件開發(fā)技術(shù)
DirectShow為過濾器組件開發(fā)提供了一套基類庫(Base Class Library),包括過濾器基類、針腳基類和一些輔助類?;悗鞛檫^濾器組件的開發(fā)提供了一個框架,省去了復(fù)雜的底層編碼工作。用戶可將開發(fā)工作集中到如下二個方面:(1)傳輸和處理媒體流。(2)將過濾器封裝為COM組件。
2.1 媒體流的傳輸和處理
為了傳輸數(shù)據(jù),用戶過濾器先要與過濾器圖中其他過濾器連接起來。連接時要進(jìn)行媒體格式和內(nèi)存分配器的協(xié)調(diào)。過濾器之間通過針腳相連。過濾器之間媒體格式和內(nèi)存分配器的協(xié)調(diào)實際上是通過針腳之間的通信來完成的?!?/P>
主動連接方的過濾器的針腳首先獲取自身支持的所有媒體格式,然后把其中一種格式送交給被動連接的一方。被動方的針腳進(jìn)行判斷:如果支持該格式,媒體格式協(xié)調(diào)成功;如果被動方不支持該格式,就通知主動方,主動方再提供1種不同的格式送交被動方,直到被動方支持被提供的格式,協(xié)調(diào)成功,否則,當(dāng)主動方用完所有支持的格式,協(xié)調(diào)失敗。
DirectShow過濾器使用一種稱作內(nèi)存分配器(Allocator)的COM對象管理媒體流數(shù)據(jù)。當(dāng)2個過濾器連接前,其中1個過濾器上的針腳提供1個內(nèi)存分配器。另外1個過濾器上的針腳對這個內(nèi)存分配器進(jìn)行檢測。當(dāng)2個針腳都支持該內(nèi)存分配器時,協(xié)調(diào)成功。
媒體流傳輸開始之前,內(nèi)存分配器負(fù)責(zé)創(chuàng)建一系列內(nèi)存緩沖區(qū)。媒體流傳輸時,上游(Upstream)過濾器填充這些緩沖區(qū),并把它們傳送給下游(Downstream)過濾器。DirectShow使用一種稱作媒體采樣包(Media Sample)的COM對象管理單個緩沖區(qū)。通過控制媒體采樣包對象,可以修改當(dāng)前緩沖區(qū)中的媒體類型、時間戳等信息,也可以利用算法處理媒體數(shù)據(jù),從而實現(xiàn)對媒體流的處理。
2.2 COM組件的實現(xiàn)
COM組件的實現(xiàn)包括如下內(nèi)容:用接口規(guī)定過濾器組件對外提供的功能;提供類廠,用以創(chuàng)建COM對象的實例;提供COM對象所在dll文件的各個輔助函數(shù),以完成COM組件在應(yīng)用程序中的載入和釋放,在注冊表中的注冊和注銷。
DirectShow中的過濾器、針腳等COM對象通過接口對外提供各種功能。除了提供標(biāo)準(zhǔn)的接口之外,DirectShow還提供了DECLARE_INTERFACE宏讓用戶自定義接口,從而滿足用戶對過濾器組件的指定要求。
COM實現(xiàn)機(jī)制中用類廠創(chuàng)建COM對象實例。DirectShow提供了類廠類CClassFactory和類廠模板類CFactoryTemplate。通過將不同的類廠模板的內(nèi)容填入類廠,實現(xiàn)不同的類廠對象,從而創(chuàng)建不同COM對象實例。
過濾器是dll文件格式的COM組件,需要以下函數(shù):DllMain(載入時的入口)、DllGetClassObject(創(chuàng)建類廠對象)、DllCanUnloadNow(判斷是否釋放dll)、DllRegisterServer(在注冊表中注冊dll)、DllUnregisterServer(在注冊表中反注冊dll)。DirectShow已經(jīng)實現(xiàn)了前3個函數(shù)。后面的2個函數(shù)通常調(diào)用DirectShow中的函數(shù)AMovieDllRegisterServer2()來實現(xiàn),即:
STDAPI DllRegisterServer()
???? ?? ?????? {?? return AMovieDllRegisterServer2(TRUE );}
????STDAPI DllUnregisterServer()
????????????? ? {?? return AMovieDllRegisterServer2(FALSE);}
3? 過濾器組件開發(fā)技術(shù)應(yīng)用實例
???下面介紹過濾器組件開發(fā)技術(shù)在網(wǎng)絡(luò)視頻服務(wù)中的一個應(yīng)用實例?!熬W(wǎng)絡(luò)數(shù)字?jǐn)z像機(jī)”系統(tǒng)使用攝像機(jī)采集視頻,經(jīng)過編碼壓縮后發(fā)送到網(wǎng)絡(luò)上??蛻舳顺绦蚪邮諗?shù)據(jù)并解碼。用戶過濾器利用這些數(shù)據(jù)生成視頻流,進(jìn)行播放或者錄像。“網(wǎng)絡(luò)數(shù)字?jǐn)z像機(jī)”客戶端程序的基本流程如圖2所示。
?
客戶端程序采用多線程的方式,網(wǎng)絡(luò)數(shù)據(jù)接收線程、解碼線程與視頻流生成線程同時運行。在線程之間使用隊列存放數(shù)據(jù)。前一個線程將數(shù)據(jù)寫入隊列,后一個線程從隊列中取出數(shù)據(jù)。要實現(xiàn)的過濾器組件例程位于視頻流生成線程內(nèi),與解碼線程共享一個數(shù)據(jù)隊列。此隊列放在一個自定義的類CDataAdmin中。解碼線程把數(shù)據(jù)放到隊列中。用戶過濾器從隊列中取出數(shù)據(jù),生成視頻流。
3.1 用戶過濾器的實現(xiàn)
(1)選擇合適的基類
用戶過濾器使用整個過濾器圖外部的數(shù)據(jù)生成視頻流,屬于源過濾器?;悗熘械腃Source類是源過濾器的基類,CSource使用CSourceStream基類作為它的針腳。本例中從這2個類派生出CCustomFilter和CCustomPin,作為實際使用的過濾器類和針腳類。
(2)通過自定義接口獲得隊列數(shù)據(jù)
為了獲得過濾器外部的隊列數(shù)據(jù),需要為CCustomFilter提供一個自定義的接口。下面的代碼定義了一個IDataSource接口:DECLARE_INTERFACE_(IDataSource,IUnknown){STDMETHOD(SetData)(THIS_CDataAdmin*pData)PURE;}。CCustomFilter繼承該接口,對外提供了一個SetData()操作。SetData()將外部傳入的CDataAdmin*類型的指針賦值給CCustomFilter的成員變量,過濾器即獲取到外部隊列數(shù)據(jù)。
(3)協(xié)調(diào)媒體類型
CSourceStream基類完成了媒體類型協(xié)調(diào)中大部分的工作,用戶只需要指定過濾器針腳支持的媒體格式。CSourceStream的成員函數(shù)GetMediaType()負(fù)責(zé)完成這個任務(wù),用戶必須在該函數(shù)中為過濾器指定媒體格式。媒體流的信息存放在一個VIDEOINFOHEADER的結(jié)構(gòu)中,指針pvi指向該結(jié)構(gòu)。函數(shù)GetMediaType()中指定媒體格式的代碼如下:
pMediaType->SetType(&MEDIATYPE_Video);
//設(shè)置媒體主類型
pMediaType->SetSubtype(&GetBitmapSubtype(&pvi->bmiHeader)); //設(shè)置媒體次類型
pMediaType->SetFormatType(&FORMAT_VideoInfo);
//設(shè)置媒體格式
pMediaType->SetTemporalCompression(FALSE);
//不壓縮媒體流
pMediaType->SetSampleSize(pvi->bmiHeader.biSizeImage); //設(shè)置媒體采樣包大小
(4)協(xié)調(diào)內(nèi)存分配器
CSourceStream基類完成了大多數(shù)內(nèi)存分配器的協(xié)調(diào)工作。用戶還需要指定每個媒體采樣包的大小。CSourceStream基類的成員函數(shù)DecideBufferSize()負(fù)責(zé)完成此任務(wù)。下面是該函數(shù)中的主要代碼。
pRequest->cbBuffer=pvi->bmiHeader.biSizeImage;
//獲取采樣包大小需求信息
ALLOCATOR_PROPERTIES Actual;
hr=pAlloc->SetProperties(pRequest,&Actual);
//指定采樣包大小,并返回實際的設(shè)置結(jié)果
(5)生成視頻流
CSourceStream基類的FillBuffer()成員函數(shù)負(fù)責(zé)把外部隊列數(shù)據(jù)加入到視頻流中。用戶可以在此函數(shù)內(nèi)部先處理數(shù)據(jù),再把處理過的數(shù)據(jù)加入視頻流中。本例中經(jīng)用戶過濾器解碼后的數(shù)據(jù),不需要進(jìn)行處理。函數(shù)FillBuffer()中的主要代碼如下。
//獲取當(dāng)前媒體采樣包對應(yīng)的緩沖區(qū)的地址和大小
BYTE*pData;
DWORD cbData;
pSample->GetPointer(&pData);
cbData=pSample->GetSize();
//獲取媒體信息
VIDEOINFOHEADER*pVih=(VIDEOINFOHEADER*)
m_mt.pbFormat;
//從數(shù)據(jù)隊列中取出數(shù)據(jù)填充到當(dāng)前緩沖區(qū)中
m_pFilePack=m_pPinData->GetDataBuffer();
memcpy(pData,m_pFilePack,min(pVih->
bmiHeader.biSizeImage,cbData));
//給媒體采樣包加上時間戳
REFERENCE_TIME rtStart=m_iFrameNumber
*m_rtFrameLength;
REFERENCE_TIME rtStop=rtStart+m_rtFrameLength;
pSample->SetTime(&rtStart,&rtStop);
?//幀計數(shù)器加1
m_iFrameNumber++;
(6)生成COM組件
過濾器開發(fā)工作的最后一步是將過濾器封裝成COM組件。此外,需要提供類廠模板。代碼如下:
CFactoryTemplate g_Templates[]={g_wszCustomFilter,
&CLSID_CustomFilter,CCustomFilter∷CreateInstance,
NULL,NULL }; //將過濾器信息填入類廠模板
int g_cTemplates=sizeof(g_Templates)/sizeof(g_Templates[0]); //類廠模板個數(shù)
3.2 實際應(yīng)用效果
??? 在“網(wǎng)絡(luò)數(shù)字?jǐn)z像機(jī)”系統(tǒng)的客戶端應(yīng)用程序中使用上例的過濾器組件,若連接到視頻播放過濾器(Video Renderer)則可播放視頻,播放效果如圖3所示;若連接到寫文件過濾器(File Writer),可將視頻直接寫成硬盤文件,實現(xiàn)視頻錄像。過濾器采用COM組件的形式,可方便地移植到其他機(jī)器和應(yīng)用程序中。
4? 結(jié)束語
過濾器組件在目前多種多樣的音頻視頻流媒體應(yīng)用中發(fā)揮著重要作用。過濾器組件的開發(fā)具有較大的實用價值,但有一定的難度和復(fù)雜性。本文討論了用戶過濾器開發(fā)中的原理和技術(shù)。文中過濾器組件例子的開發(fā)過程具有較大的通用性,可供其他開發(fā)者參考。
?
參考文獻(xiàn)
1? Kruglinski D J.VC++技術(shù)內(nèi)幕(第4版).北京:清華大學(xué)出版社,1999
2? 潘愛民.COM原理和應(yīng)用.北京:清華大學(xué)出版社,1999