《電子技術(shù)應(yīng)用》
您所在的位置:首頁 > 其他 > 業(yè)界動態(tài) > DirectShow過濾器組件開發(fā)技術(shù)及其應(yīng)用實例

DirectShow過濾器組件開發(fā)技術(shù)及其應(yīng)用實例

2009-08-14
作者:盛 健 季曉勇

  摘? 要: 討論了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

本站內(nèi)容除特別聲明的原創(chuàng)文章之外,轉(zhuǎn)載內(nèi)容只為傳遞更多信息,并不代表本網(wǎng)站贊同其觀點。轉(zhuǎn)載的所有的文章、圖片、音/視頻文件等資料的版權(quán)歸版權(quán)所有權(quán)人所有。本站采用的非本站原創(chuàng)文章及圖片等內(nèi)容無法一一聯(lián)系確認(rèn)版權(quán)者。如涉及作品內(nèi)容、版權(quán)和其它問題,請及時通過電子郵件或電話通知我們,以便迅速采取適當(dāng)措施,避免給雙方造成不必要的經(jīng)濟(jì)損失。聯(lián)系電話:010-82306118;郵箱:aet@chinaaet.com。