• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            牽著老婆滿街逛

            嚴以律己,寬以待人. 三思而后行.
            GMail/GTalk: yanglinbo#google.com;
            MSN/Email: tx7do#yahoo.com.cn;
            QQ: 3 0 3 3 9 6 9 2 0 .

            DirectShow開發快速入門之慨述

              摘要:本篇文檔概括性的介紹了DirectShow的主要組成部分,以及一些Directshow的基本概念。熟悉這些基本的知識對于Directshow的應用開發或者過濾器的開發者都會有所幫助。

              DirectShow是微軟公司提供的一套在Windows平臺上進行流媒體處理的開發包,與DirectX開發包一起發布。那么,DirectShow能夠做些什么呢?且看,DirectShow為多媒體流的捕捉和回放提供了強有力的支持。運用DirectShow,我們可以很方便地從支持WDM驅動模型的采集卡上捕獲數據,并且進行相應的后期處理乃至存儲到文件中。它廣泛地支持各種媒體格式,包括Asf、Mpeg、Avi、Dv、Mp3、Wave等等,使得多媒體數據的回放變得輕而易舉。另外,DirectShow還集成了DirectX其它部分(比如DirectDraw、DirectSound)的技術,直接支持DVD的播放,視頻的非線性編輯,以及與數字攝像機的數據交換。更值得一提的是,DirectShow提供的是一種開放式的開發環境,我們可以根據自己的需要定制自己的組件。

              應用程序與DirectShow組件以及DirectShow所支持的軟硬件之間的關系如圖1所示。


            圖1 DirectShow系統框圖

              1、DirectShow的 Filter

              Directshow是基于模塊化,每個功能模塊都采取COM組件方式,稱為Filter。Directshow提供了一系列的標準的模塊可用于應用開發,開發者也可以開發自己的功能Filter來擴展Directshow的應用。下面我們用一個例子來說明如何采取Filter來播放一個AVI的視頻文件。

              1) 首先從一個文件中讀取AVI數據,形成字節流。(這個工作由源Filter完成)

              2) 檢查AVI數據流的頭格式,然后通過AVI分割Filter將視頻流和音頻流分開。

              3) 解碼視頻流,根據壓縮格式的不同,選取不同的decoder filters 。

              4) 通過Renderer Filter重畫視頻圖像。

              5) 音頻流送到聲卡進行播放,一般采用缺省的 DirectSound Device Filter。流程見下圖。


            圖2 音頻流播放Graph圖

              從上面的圖表看,每一個filter都一個其他的一個或者兩個filter相連接。兩個Filter相連接的連接點也是com對象,我們稱為Pin。Filter通過pin將數據從一個filter傳遞到另一個filter中,從而可以使數據在由filter組成的鏈表中流動。圖中的箭頭表示filter鏈表中的數據流的方向。在Directshow中,像上面的這樣一個filter 鏈表我們稱為filter Graph。

              Filter具有三個狀態,運行,停止,暫停。當一個filter運行時,它就處理媒體數據流,當停止時,filter就不在處理數據,暫停狀態常用來給運行狀態之前cure data。Data Flow in the Filter Graph一章詳細描述了這些概念,可以參考。

              除了一些特別的例外, Filter graph中所有的filter的狀態的改變都是統一的,也就說,filte graph中的所有的filter 的狀態改變是一致協調的。也就是說,我們也可以用filter graph也可以有運行,停止,暫停三種狀態。

              Filter 一般分為下面幾種類型。

              (1)源過濾器(source filter):源過濾器引入數據到過濾器圖表中,數據來源可以是文件、網絡、照相機等。不同的源過濾器處理不同類型的數據源。

              (2)變換過濾器(transform filter):變換過濾器的工作是獲取輸入流,處理數據,并生成輸出流。變換過濾器對數據的處理包括編解碼、格式轉換、壓縮解壓縮等。

              (3)提交過濾器(renderer filter):提交過濾器在過濾器圖表里處于最后一級,它們接收數據并把數據提交給外設。

              (4)分割過濾器(splitter filter):分割過濾器把輸入流分割成多個輸出。例如,AVI分割過濾器把一個AVI格式的字節流分割成視頻流和音頻流。

              (5)混合過濾器(mux filter):混合過濾器把多個輸入組合成一個單獨的數據流。例如,AVI混合過濾器把視頻流和音頻流合成一個AVI格式的字節流。

              過濾器的這些分類并不是絕對的,例如一個ASF讀過濾器(ASF Reader filter)既是一個源過濾器又是一個分割過濾器。

              2、關于Filter Graph Manager

              Filter Graph Manager也是一個com對象,用來控制Filter graph中的所有的filter,主要有以下的功能:

              1) 用來協調filter之間的狀態改變,從而使graph 中的所有的filter的狀態的改變應該一致。

              2) 建立一個參考時鐘。

              3) 將filter 的消息通知返回給應用程序

              4) 提供用來建立 filter graph的方法。

              這里只是簡單的描述一下,詳細地可以參考文檔。

              狀態改變,Graph中的filter的狀態改變應該一致,因此,應用程序并將狀態改變的命令直接發給filter,而是將相應的狀態改變的命令發送給Filter graph Manager,由manager將命令分發給graph中每一個filter。Seeking也是同樣的方式工作,首先由應用程序將seek命令發送到filter graph 管理器,然后由其分發給每個filter。

              參考時鐘,graph中的filter都采用的同一個時鐘,稱為參考時鐘(reference clock),參考時鐘可以確保所有的數據流同步,視頻楨或者音頻楨應該被提交的時間稱為presentation time.presentation time 是相對于參考時鐘來確定的。Filter graph Manager應該選擇一個參考時鐘,可以選擇聲卡上的時鐘,也可以選擇系統時鐘。

              Graph事件, Graph 管理器采用事件機制將graph中發生的事件通知給應用程序,這個機制類似于windows的消息循環機制。

              Graph構建的方法,graph管理器給應用程序提供了將filter添加進graph的方法,連接filter的方法,斷開filter連接的方法。

              但是,graph 管理器沒有提供如何將數據從一個filter發送到另一個filter的方法,這個工作是由filter在內部通過pin來獨立完成的,
              3、媒體類型

              因為Directshow是基于com組件的,就需要有一種方式來描述filter graph每一個點的數據格式,例如,我們還以播放AVI文件為例,數據以RIFF塊的形式進入graph中,然后被分割成視頻和音頻流,視頻流有一系列的壓縮的視頻楨組成,解壓后,視頻流由一系列的無壓縮的位圖組成,音頻流也要走同樣的步驟。

            Media Types: How DirectShow Represents Formats

              媒體類型是一種很普遍的,可以擴展的用來描述數字媒體格式的方法,當兩個filter連接的時候,他們會就采用某一種媒體類型達成一致的協議。媒體類型定義了處于源頭的filter將要給下游的filter發送什么樣的數據,以及數據的physical layout。如果兩個filter不能夠支持同一種的媒體類型,那么他們就沒法連接起來。

              對于大多數的應用來說,也許你不用考慮媒體類型,但是,有些應用程序中,你會直接應用到媒體類型的。

              媒體類型是通過AM_MEDIA_TYPE結構定義的,看看原始定義吧

            typedef struct _MediaType {
             GUID majortype;
             GUID subtype;
             BOOL bFixedSizeSamples;
             BOOL bTemporalCompression;
             ULONG lSampleSize;
             GUID formattype;
             IUnknown *pUnk;
             ULONG cbFormat;
             [size_is(cbFormat)] BYTE *pbFormat;
            } AM_MEDIA_TYPE;

              Major type:是一個GUID,用來定義數據的主類型,包括,音頻,視頻,unparsed字節流,MIDI數據,等等,具體可以參考msdn。

              Subtype:子類型,也是一個GUID,用來進一步的細化數據格式,例如,在視頻主類型中,還包括RGB-24, RGB-32, UYVY等等一些子類型,在音頻主類型中還包括PCM audio, MPEG-1 payload等類型,子類型提供了比主類型更詳細的信息,但是并沒有定義所有的格式,例如,視頻的子類型并沒有定義圖像大小,楨率。這些由下面的字段定義。

              bFixedSizeSamples當這個值為TRUE時,表示sample大小固定。

              bTemporalCompression當這個值為TRUE時,表示sample采用了臨時壓縮格式,表明不是所有的楨都是關鍵楨,如果為FALSE,表明所有的都是關鍵楨。

              lSampleSize 表示sample的大小。對于壓縮的數據,這個值可能為零。
              
              Formattype一個GUID值,用來表明內存塊的格式。包括如下:FORMAT_None,FORMAT_DvInfo,FORMAT_MPEGVideo,FORMAT_MPEG2Video,FORMAT_VideoInfo,FORMAT_VideoInfo2,FORMAT_WaveFormatEx,GUID_NULL。

              pUnk該參數沒有用到。

              cbFormat內存塊的大小。

              pbFormat指向內存塊的指針。

              下面我們看一段代碼,看看filter如何檢測媒體類型的。

            HRESULT CheckMediaType(AM_MEDIA_TYPE *pmt)
            {
             if (pmt == NULL) return E_POINTER;
             // Check the major type. We’re looking for video.
             if (pmt->majortype != MEDIATYPE_Video)
             {
              return VFW_E_INVALIDMEDIATYPE;
             }
             // Check the subtype. We’re looking for 24-bit RGB.
             if (pmt->subtype != MEDIASUBTYPE_RGB24)
             {
              return VFW_E_INVALIDMEDIATYPE;
             }
             // Check the format type and the size of the format block.
             if ((pmt->formattype == FORMAT_VideoInfo) && (pmt->cbFormat >= sizeof(VIDEOINFOHEADER) &&
            (pmt->pbFormat != NULL))
             {
              // Now it’s safe to coerce the format block pointer to the
              // correct structure, as defined by the formattype GUID.
              VIDEOINFOHEADER *pVIH = (VIDEOINFOHEADER*)pmt->pbFormat;
              // Examine pVIH (not shown). If it looks OK, return S_OK.
              return S_OK;
             }
             return VFW_E_INVALIDMEDIATYPE;
            }

              下面簡單介紹幾個和 Media Type相關的函數:

              AM_MEDIA_TYPE結構包含一個指向數據塊的指針,因此,當你使用這個結構的時候,一定要小心內存分配,以防內存泄漏。

              分配函數

              1) AM_MEDIA_TYPE * WINAPI CreateMediaType(AM_MEDIA_TYPE const *pSrc );

              這個函數分配一個新的AM_MEDIA_TYPE結構,包含特定格式的數據塊。釋放由這個函數分配的內存,可以調用DeleteMediaType函數

              2) STDAPI CreateAudioMediaType(const WAVEFORMATEX *pwfx,AM_MEDIA_TYPE *pmt,BOOL bSetFormat);

              該函數利用一個給定的WAVEFORMATIEX結構來初始化媒體類型,如果bsetFormat參數為TRUE,該函數就分配一塊新的內存,如果原來的pmt已經包含內存,就有可能發生內存泄漏。為了避免內存泄漏,在調用這個函數前要調用FreeMediaType(),在這個函數返回之后,再次調用FreeMediaType(),釋放format block。

              3) HRESULT WINAPI CopyMediaType(AM_MEDIA_TYPE *pmtTarget,const AM_MEDIA_TYPE *pmtSource);

              這個函數復制了一個結構到另一個結構中去。這個函數也要重新分配內存給目的結構,如果pmtTarget,已經包含一個內存塊,就要內存泄漏,因此,在調用該函數前后都要調用FreeMediaType函數。

              釋放函數

              4) void WINAPI DeleteMediaType( AM_MEDIA_TYPE *pmt);

              無論是采用CoTaskMemAlloc函數還是用CreateMediaType函數分配的內存都可以用這個函數來釋放,如果你沒有連接基類的動態庫,你可以用下面的代碼

            void MyDeleteMediaType(AM_MEDIA_TYPE *pmt)
            {
             if (pmt != NULL)
             {
              MyFreeMediaType(*pmt); // 見下面的 FreeMediaType 函數
              CoTaskMemFree(pmt);
             }
            }

              5) void WINAPI FreeMediaType( AM_MEDIA_TYPE& mt);

              這個函數用來釋放數據塊的內存,如果要刪除AM_MEDIA_TYPE結構,可以使用DeleteMediaType函數。

            void MyFreeMediaType(AM_MEDIA_TYPE& mt)
            {
             if (mt.cbFormat != 0)
             {
              CoTaskMemFree((PVOID)mt.pbFormat);
              mt.cbFormat = 0;
              mt.pbFormat = NULL;
             }
             if (mt.pUnk != NULL)
             {
              // Unecessary because pUnk should not be used, but safest.
              mt.pUnk->Release();
              mt.pUnk = NULL;
             }
            }

            4、媒體Samples和Allocators

              Filters通過pin的連接來傳遞數據,數據流是從一個filter的輸出pin流向相連的filter的輸入pin。輸出pin常用的傳遞數據的方式是調用輸入pin上的IMemInputPin::Receive方法。

              對于filter來說,可以有好幾種方式來分配媒體數據使用的內存塊,可以在堆上分配,可以在DirectDraw的表面,也可以采用GDI共享內存,還有其他的一些方法,在Directshow中用來進行內存分配任務的是內存分配器(allocator),也是一個COM對象,暴露了一個IMemAllocator接口。

              當兩個pin連接的時候,必須有一個pin提供一個allocator,Directshow定義了一系列函數調用用來確定由哪個pin提供allocator,以及buffer的數量和大小。

              在數據流開始之前,allocator會創建一個內存池(pool of buffer),在開始發送數據流以后,源filter就會將數據填充到內存池中一個空閑的buffer中,然后傳遞給下面的filter。但是,源filter并不是直接將內存buffer的指針直接傳遞給下游的filter,而是通過一個media samples的COM對象,這個sample是allocator創建的用來管理內存buffer。Media sample暴露了IMediaSample接口,一個sample包含了下面的內容:

              一個指向沒有發送的內存的指針。

              一個時間戳

              一些標志

              媒體類型。

              時間戳表明了presentation time,Renderer filter就是根據這個時間來安排render順序的。標志是用來標示數據是否中斷等等,媒體類型提供了中途改變數據格式的一種方法,不過,一般sample沒有媒體類型,表明它們的媒體類型一直沒有改變。

              當一個filter正在使用buffer,它就會保持一個sample的引用計數,allocator通過sample的引用計數用來確定是否可以重新使用一個buffer。這樣就防止了buffer的使用沖突,當所有的filter都釋放了對sample的引用,sample才返回到allocator的內存池,供重新使用。

              5、硬件設備在graph中的作用

              下面的這段話借用的是陸其明的一段文檔,特此標記2005-1-26我覺得他對硬件的表述比較清楚。

              大家知道,為了提高系統的穩定性,Windows操作系統對硬件操作進行了隔離;應用程序一般不能直接訪問硬件。DirectShow Filter工作在用戶模式(User mode,操作系統特權級別為Ring 3),而硬件工作在內核模式(Kernel mode,操作系統特權級別為Ring 0),那么它們之間怎么協同工作呢?

              DirectShow解決的方法是,為這些硬件設計包裝Filter;這種Filter能夠工作在用戶模式下,外觀、控制方法跟普通Filter一樣,而包裝Filter內部完成與硬件驅動程序的交互。這樣的設計,使得編寫DirectShow應用程序的開發人員,從為支持硬件而需做出的特殊處理中解脫出來。DirectShow已經集成的包裝Filter,包括Audio Capture Filter(qcap.dll)、VfW Capture Filter(qcap.dll,Filter的Class Id為CLSID_VfwCapture)、TV Tuner Filter(KSTVTune.ax,Filter的Class Id為CLSID_CTVTunerFilter)、Analog Video Crossbar Filter(ksxbar.ax)、TV Audio Filter(Filter的Class Id為CLSID_TVAudioFilter)等;另外,DirectShow為采用WDM驅動程序的硬件設計了KsProxy Filter(Ksproxy.ax,)。我們可以看一下結構圖:見圖1

              我們可以看出,Ksproxy.ax、Kstune.ax、Ksxbar.ax這些包裝Filter跟其它普通的DirectShow Filter處于同一個級別,可以協同工作;用戶模式下的Filter通過Stream Class控制硬件的驅動程序minidriver(由硬件廠商提供的實現對硬件控制功能的DLL);Stream Class和minidriver一起向上層提供系統底層級別的服務。值得注意的是,這里的Stream Class是一種驅動模型,它負責調用硬件的minidriver;另外,Stream Class的功能還在于協調minidriver之間的工作,使得一些數據可以直接在Kernel mode下從一個硬件傳輸到另一個硬件(或同一個硬件上的不同功能模塊),提高了系統的工作效率。(更多的關于底層驅動程序的細節,請讀者參閱Windows DDK。)

              下面,我們分別來看一下幾種常見的硬件。

              VfW視頻采集卡。這類硬件在市場上已經處于一種淘汰的趨勢;新生產的視頻采集卡一般采用WDM驅動模型。但是,DirectShow為了保持向后兼容,還是專門提供了一個包裝Filter支持這種硬件。和其他硬件的包裝Filter一樣,這種包裝Filter的創建不是像普通Filter一樣使用CoCreateInstance,而要通過系統枚舉,然后BindToObject。

              音頻采集卡(聲卡)。聲卡的采集功能也是通過包裝Filter來實現的;而且現在的聲卡大部分都有混音的功能。這個Filter一般有幾個Input pin,每個pin都代表一個輸入,如Line In、Microphone、CD、MIDI等。值得注意的是,這些pin代表的是聲卡上的物理輸入端子,在Filter Graph中是永遠不會連接到其他Filter上的。聲卡的輸出功能,可以有兩個Filter供選擇:DirectSound Renderer Filter和Audio Renderer (WaveOut) Filter。注意,這兩個Filter不是上述意義上的包裝Filter,它們能夠同硬件交互,是因為它們使用了API函數:前者使用了DirectSound API,后者使用了waveOut API。這兩個Filter的區別,還在于后者輸出音頻的同時不支持混音。(順便說明一下,Video Renderer Filter能夠訪問顯卡,也是因為使用了GDI、DirectDraw或Direct3D API。)如果你的機器上有聲卡的話,你可以通過GraphEdit,在Audio Capture Sources目錄下看到這個聲卡的包裝Filter。

              WDM驅動的硬件(包括視頻捕捉卡、硬件解壓卡等)。這類硬件都使用Ksproxy.ax這個包裝Filter。Ksproxy.ax實現了很多功能,所以有“瑞士軍刀”的美譽;它還被稱作為“變色龍Filter”,因為該Filter上定義了統一的接口,而接口的實現因具體的硬件驅動程序而異。在Filter Graph中,Ksproxy Filter顯示的名字為硬件的Friendly name(一般在驅動程序的.inf文件中定義)。我們可以通過GraphEdit,在WDM Streaming開頭的目錄中找到本機系統中安裝的WDM硬件。因為KsProxy.ax能夠代表各種WDM的音視頻設備,所以這個包裝Filter的工作流程有點復雜。這個Filter不會預先知道要代表哪種類型的設備,它必須首先訪問驅動程序的屬性集,然后動態配置Filter上應該實現的接口。

              當Ksproxy Filter上的接口方法被應用程序或其他Filter調用時,它會將調用方法以及參數傳遞給驅動程序,由驅動程序最終完成指定功能。除此以外,WDM硬件還支持內核流(Kernel Streaming),即內核模式下的數據傳輸,而無需經過到用戶模式的轉換。因為內核模式與用戶模式之間的相互轉換,需要花費很大的計算量。如果使用內核流,不僅可以避免大量的計算,還避免了內核數據與主機內存之間的拷貝過程。在這種情況下,用戶模式的Filter Graph中,即使pin之間是連接的,也不會有實際的數據流動。典型的情況,如帶有Video Port Pin的視頻捕捉卡,Preview時顯示的圖像就是在內核模式下直接傳送到顯卡的顯存的。所以,你也休想在VP Pin后面截獲數據流。

              講到這里,我想大家應該對DirectShow對硬件的支持問題有了一個總體的認識。對于應用程序開發人員來說,這方面的內容不用研究得太透,而只需作為背景知識了解一下就好了。其實,大量繁瑣的工作DirectShow已經幫我們做好了。

            posted on 2006-05-08 18:02 楊粼波 閱讀(495) 評論(0)  編輯 收藏 引用 所屬分類: 文章收藏

            九九精品99久久久香蕉| 久久国产精品-久久精品| 九九热久久免费视频| 婷婷久久综合九色综合98| 91精品久久久久久无码| 久久久久久国产精品免费免费| 无码人妻少妇久久中文字幕| 久久福利资源国产精品999| 久久精品国产亚洲av日韩| 亚洲国产精品热久久| 久久无码中文字幕东京热| 久久综合综合久久97色| 狠狠色丁香久久婷婷综合_中| …久久精品99久久香蕉国产| 亚洲欧美日韩久久精品| 国产精品9999久久久久| 亚洲欧美精品一区久久中文字幕 | 久久国产免费直播| 久久久久久曰本AV免费免费| 久久国产精品久久| 97久久精品人妻人人搡人人玩| 久久无码中文字幕东京热| 日韩AV毛片精品久久久| 国产伊人久久| 麻豆精品久久精品色综合| 久久青青草原亚洲av无码app| 久久中文字幕无码专区| 国产精品99久久久久久董美香| 久久精品九九亚洲精品| 久久亚洲AV成人无码国产 | 日韩精品国产自在久久现线拍| 久久中文字幕人妻丝袜| 伊人久久无码精品中文字幕| 久久久久国产一区二区| 久久99中文字幕久久| 久久99国产精品久久久| 久久―日本道色综合久久| 国产99久久九九精品无码| 国产免费久久精品99久久| 日本久久久久久久久久| 久久这里只精品99re66|