日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

DirectShow开发快速入门之慨述

發布時間:2024/4/13 编程问答 37 豆豆
生活随笔 收集整理的這篇文章主要介紹了 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已經幫我們做好了。 超強干貨來襲 云風專訪:近40年碼齡,通宵達旦的技術人生

總結

以上是生活随笔為你收集整理的DirectShow开发快速入门之慨述的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。