視頻壓縮管理器(VCM)

http://dev.poptool.net/other/2005-11-20/0511200791.html

視頻壓縮管理器提供了一個訪問接口,通過該接口可以使用系統已經安裝了的壓縮器去壓縮處理實時視頻數據。應用程序可以使用安裝的壓縮器去執行下面的任務:

l     壓縮和解壓視頻數據

l     發送一個renderer壓縮視頻數據並畫它顯示它。

l     使用應用程序默認的renderers去壓縮,解壓或者畫視頻數據。

l     使用renderers 去處理文本和自定義數據。

關於視頻壓縮管理器

 

    一般,處理視頻圖形的壓縮器被存放在AVI文件中。通過VCM來訪問安裝了的壓縮器,這些內容涉及到下面的主題:

l     VCM 和VFW的結構

l     從你的程序壓縮和解壓圖形數據

l     從你的程序中使用VCM renderers 去畫視頻數據

l     VCM 函數和數據結構體

在你閱讀這些內容前,最好對Microsoft Win32 圖形處理了解一下。因爲很多內容,比如位圖結構 BITMAPINFO和BITMAPINFOHEADER會大量地被VCM使用。

注意:音頻壓縮管理器(ACM)提供系統級的音頻壓縮、解壓支持,關於音頻壓縮服務,可以去看音頻壓縮管理器。

 

 

 

結構

 

  VCM界於應用程序和壓縮解壓縮驅動程序之間。壓縮解壓縮驅動程序對數據幀進行壓縮和解壓縮處理。

當應用程序去調用VCM的時候,VCM把這個調用動作封裝到一個消息中。然後通過使用ICSendMessage函數,把這個消息發送到適合的壓縮器或解壓器。VCM接收到壓縮器或解壓器的返回值後,就把控制返回給應用程序。

    如果爲一個消息定義一個宏,這個宏可以對ICSendMessage函數調用進行擴衝,因爲它可以爲這個消息提供更適合的參數。假如爲一個消息定義一個宏,那麼你的程序將使用宏而不願意使用消息。在後面的介紹中,這些宏將放在消息後的園括號中。

 

 

 

壓縮,解壓縮以及Renderers的註冊表項目

 

系統使用註冊表中的“相”來定位VCM的驅動程序。    這些“相”用兩個四字符碼(two four-character codes)的形式來表示,中間使用一個點來分割(比如VIDC.DIVX)。第一個4字符碼(four-character code)由系統定義,它的內容是下列中之一:

 

 

 

四字符碼

 

 

說         

 

 

"VIDC"

 

 

壓縮器和解壓縮器的ID

 

 

"VIDS"

 

 

視頻流Renderers的ID

 

 

"TXTS"

 

 

文本流(text-stream)Renderers的ID

 

 

"AUDS"

 

 

音頻流控制器

 

 

 

 

 

自定義的renderers可以定義它自己的四字符碼。

 

 

 

第二個四字符碼由驅動程序定義。比較典型的是,第二個四字符碼用於描述這個驅動程序可以處理的數據類型。

    當打開一個VCM驅動程序的時候,應用程序就指定了這個驅動程序和驅動程序可以處理的數據類型。一般來說,這個信息來源於數據流的頭部。系統將嘗試去打開指定的數據,如果打開失敗了,系統就在註冊表中查找可以處理該數據的其他驅動程序。

    當在查找驅動程序的時候,系統會嘗試用與這四字符碼匹配的其他驅動程序“項”來處理這個數據類型。例如,一個應用程序指定了一個MSSQ的壓縮器,系統在註冊表尋找VIDC.MSSQ這個項。如果沒有找到,它就打開每個驅動程序去看是否可以處理該數據。在這個例子中,系統在註冊表中如果不能找到VIDC.MSSQ“項”,它將打開所有帶“VIDC”“相”指定的驅動程序去處理這些數據。

 

 

 

的服務

 

通常,應用程序使用VCM去處理下面的任務:

l     定位,打開,或安裝一個壓縮器或者解壓器。

l     設置或者獲得壓縮器、解壓縮器的配置信息。

l     使用函數去壓縮,解壓縮或者顯示這些數據。

DrawDib庫的函數和宏可以完成完成這些任務,同時它可以提供更方便的方法去使用VCM。關於DrawDib庫更多的信息可以,去查看MSDN中DrawDib的內容。

下面將描述使用VCM完成如下的任務:

 

 

壓縮器和解壓縮器基礎

 

    你可以使用ICLocateICOpen函數來打開和定位一個壓縮器。你可以是使用ICLocate來定位一個指定類型的壓縮器,並通過使用其他VCM函數來獲得這個壓縮器句柄。要打開一個壓縮器,你可以使用ICOpen函數。你的應用程序使用該函數返回的句柄來標示一個打開的壓縮器。該壓縮器的句柄,在其他VCM函數中會用到。

應用程序使用ICDecompressOpenICDrwaOpen宏來打開和定位一個解壓器,這些宏都使用ICLocate操作。

當你的應用程序使用完壓縮器和解壓器後,必須關閉你打開的解壓器和壓縮器,同時還要釋放到所有用於壓縮和解壓的資源。你的程序可以使用ICClose函數去關閉壓縮器和解壓器。

    同樣,你的程序通過使用ICInfo函數來列舉系統中所有的壓縮器和解碼器。

 

 

 

注意:在AVI文件的流的頭部包含了關於流類型和流指定的處理器的信息。在流的頭部內,fccTypefccHandler數據成員指定流的類型和流指定的處理器信息。

 

 

 

用戶選擇壓縮器

 

 

當要壓縮數據的時候,你的應用程序可以使用ICCompressorChoose函數去創建一個對話框,讓用戶在對話框中去選擇壓縮器。你可以給這個函數指定一個標誌,來允許用戶指定關鍵幀頻率(Key-frame frequency)和運動數據速度(movie-data rate),或者去顯示一個預覽的窗體。

    在ICCompressorChoose函數中,用戶選擇的壓縮器將被自動開,並且壓縮器的句柄將保存在COMPVARS數據結構體的hic數據成員中。

    如果你使用了ICCompressorChoose函數,那麼使用ICCompressorFree函數去關閉壓縮器和釋放COMPVARS對象關聯的所有資源。

 

 

 

安裝和移除壓縮器和解壓器

 

 

    應用程序可以使用系統中已經安裝的壓縮器後解壓器來運行在Microsoft的操作系統上。應用程序同樣可以爲一般用戶或者特殊用戶安裝壓縮器和解壓器。大多數的應用程序不需要去安裝或者移除壓縮起或者解壓器,因爲它們通常是通過一個安裝程序來安裝。有的時候,有的函數可以被當作壓縮器或者解壓器。

  

應用程序通過使用ICInstall函數來安裝解壓器或者壓縮器(或者被當作壓縮器或者解壓器的函數)。這個函數將在註冊表中建立一個“項”去確定壓縮器或者解壓器。你的應用程序或者其他應用程序可以在查找註冊表來確定是否有適合自己使用的解碼器和壓縮器。使用ICInstall安裝壓縮器和解壓器的所有驅動程序

   

應用程序可以使用ICLocateICOpen函數來定位和打開系統中安裝的解壓器和壓縮器。當應用程序使用完成後,使用ICClose函數來關閉它們。

 

 

 

應用程序使用ICRemove函數,可以在註冊表中移除一個已經安裝了的壓縮器或者解壓器的“項”。 該函數不能移除當前已經加載到內存中的壓縮器或者解壓器。應用程序通過安裝,打開,關閉,移除操作可以限定壓縮器或解壓器的使用。

 

 

 

作爲其他選擇,應用程序可以使用ICOpenFunction函數,來把一個函數當做壓縮器或者解碼器來使用,而不需要去註冊表進行安裝。這個函數要求應用程序獲得去進行壓縮和解壓函數的地址。當應用程序使用完這個函數後,必須使用ICClose來關閉該函數。因爲該函數沒有在註冊表中進行註冊,所以不用對註冊表進行操作。

 

 

 

   被當做壓縮器或者解壓器使用的函數的內部結構,與用於安裝驅動程序DriverProc指針函數的結構一樣。關於DriverPro指針函數的更多信息,你查看MSDN的 Installable Drivers

 

 

注意: 應用程序安裝了一個可以當作壓縮器或解碼器的函數,在應用程序關閉前,必須要移出這個函數,好讓其他程序不要去嘗試使用該函數。當移除這個函數的時候,應用程序把它當作採用四字符碼安裝的驅動一樣。

 

 

 

壓縮器和解壓器的配置

 

 

    你的程序可以自動配置壓縮器或者解壓器,或者允許用戶去配置這些信息。用戶可以使用配置對話框來設置壓縮器或者解壓器的配置信息。你可以發送ICM_CONFIGURE消息給VCM(或者使用ICQueryConfigure宏)來判斷壓縮器或者解碼器是否支持顯示配置對話框。如果支持,發送ICM_CONFIGURE消息(ICConfigure宏)來顯示它。

 

 

 

    你的程序發送ICM_GETSTATEICM_SETSTATE消息(ICGetStateSizeICGetStateICSetState宏)去獲得和設置壓縮器、解碼器的狀態。如果你的程序創建或者改變了狀態,必須在恢復它們狀態前去獲得壓縮器或者解碼器數據的狀態。如果你的應用程序從壓縮器或者編碼器獲得了它們的狀態,並將在後面使用它們來恢復以前的狀態。要保證你獲得的信息爲當前的信息。

 

 

 

獲得壓縮器和編碼器的信息

 

要得到壓縮器或者解碼器的信息,你可以使用ICGetInfo函數來實現。這個函數把解碼器和壓縮器的信息寫入一個ICINFO的數據結構中。你的程序必須爲ICINFO數據結構分配內存,並且在ICGetInfo中通過一個指針指向它。ICINFO數據結構體中將提供很多關於壓縮器或者解碼器性能的信息。

要獲得壓縮器或者解碼器默認的關鍵幀頻率(key-frame rate)和默認的質量值,可以發送ICM_GETDEFAULTKEYFRAMERATEICM_GETDEFAULTQUALITY消息,或者使用ICGetDefaultKeyFrameRateICGetDafaultQuality宏。

    可以使用ICGetDisplayFormat函數來測量壓縮器或解碼器最好的顯示格式。發送ICM_ABOUT消息可以檢測壓縮器或解碼器是否能顯示“關於”對話框。(或使用ICQueryAbout宏)。你同樣可以顯示壓縮器、解碼器的這個“關於”對話框,發送ICM_ABOUT消息並改變wParam的參數值(或者使用ICAbout宏)。

 

 

單張圖像壓縮

 

你可以使用ICImageCompress函數去壓縮單張圖像。該函數返回一個壓縮的DIB位圖的句柄。這個壓縮的DIB位圖使用CF_DIB格式。

 

 

序列壓縮 (Sequence Compression)

 

    你的應用程序可以使用ICSeqCompressFrame,ICSeqCompressFrameStartICSeqCompressFrameEnd函數來壓縮一個幀的序列。這些函數使用保存在COMPVARS數據結構中的數據。應用程序使用ICCompressorChoose函數允許用戶選擇一個壓縮器,打開它,並在COMPVARS中設置壓縮參數;同時,應用程序也可以手動去設置這個數據結構中的參數。

    在應用程序使用開始壓縮一個幀序列前,必須使用ICSeqCompressFrameStart去分配必要的資源。在資源分配好後,應用程序就可以是使用ICSeqCompressFrame去壓縮每個幀了。幀率(frame rate)和關鍵幀頻率(key-frame frequency)使用COMPVARS數據結構中指定的數去進行壓縮。ICSeqCompressFrame的返回值,指向被壓縮的數據。

    當程序完成壓縮時,可以使用ICSeqCompressFrameEnd函數去釋放系統資源(ICSeqCompressFrameStart分配的)。應用程序可以使用ICCompressorFree函數去釋放爲COMPVARS數據結構分配的系統資源。

 

 

 

圖像數據壓縮(Image-Data Compression)

    你的程序可以使用一個連串的ICCompress函數和宏去壓縮數據。這個函數和宏可以幫你完成下面的任務:

 

 

l     決定使用壓縮格式

l     準備壓縮器

l     壓縮數據

l     結束壓縮工作

使用ICCompress函數和宏可以增加你程序對壓縮處理的控制。這組函數和宏處理幀時,是採用單獨的幀處理,而不是把所有幀當成一個序列來處理。這樣處理更加靈活。比如,你的程序可以使用ICCompress函數,把一些幀當作關鍵幀來(key-frames)處理。

 

 

 

    壓縮器接收統一格式的數據,然後壓縮這些數據,並且還會返回這個指定格式的數據壓縮版本號。比較典型的輸入格式DIBs使用BITMAPINFO數據類型。比較典型的輸出格式壓縮DIBs,也使用BITMAPINFO數據結構。

 

 

 

Note To minimize image and audio degradation during playback, avoid compressing an AVI file more than once. Combine uncompressed pieces of video in your editing system and then compress the final product.

 

 

 

壓縮器和壓縮格式選擇

    如果你要按一指定輸出格式壓縮數據,你可以發送ICM_COMPRESS_QUERY消息(ICCompressQuery宏)去查詢你的系統是否支持該格式。

如果輸出格式對你的程序可能並不重要。你只想找到一個壓縮器可以處理輸入的格式就行了。要判斷壓縮器是否可以處理這種輸入格式,你可以發送ICM_COMPRESS_QUERY消息,並指定lParam參數爲NULL。這個消息不會返回輸出格式到你的程序。你可以發送ICM_COMPRESS_GET_FORMAT消息(ICCompressGetFormatSize宏),來知道採用該壓縮格式的數據需要分配的緩存大小。

要想獲得壓縮器在壓縮時需要的最大緩存區,可以發送ICM_COMPRESS_GET_SIZE消息(ICCompressGetSize宏)。你可以使用ICSendMessage函數返回的字節(byte)數來爲圖像壓縮分配緩存大小。

 

 

 

壓縮初始化

    你的程序選擇了壓縮器後,你可以使用ICM_COMPRESS_BEGIN消息來初始壓縮器。(ICCompressBegin宏)。這個消息需要壓縮器的句柄和輸入、輸出的格式。

 

 

 

數據壓縮

    你可以使用ICCompress函數去壓縮一個幀。你的程序必須重複使用該函數,直到序列中的所有幀都被壓縮完成。每壓縮一幀圖像,你的程序必須在ICCompress函數中增加壓縮幀的數字。壓縮器靠這個值來檢測壓縮情況。如果要重新壓縮一幀圖像,你就使用上次相同的值。如果你要壓縮一個靜態圖形(still-frame),就讓該值爲0。

    使用ICCOMPRESS_KEYFRAME消息可以讓這些幀被壓縮爲一個關鍵幀(Key-frame)。

    當完成一個幀的壓縮後,VCM就把控制權交給你的應用程序,VCM就把壓縮數據保存在一個數據結構體中,可通過lpbiOutputlpData參數來引用。如果你要移動這些數據,可以在lpbiOutput參數指定的BITMAPINFO的數據結構的biSizeImage成員中,得到數據的大小。

注意 你的程序必須分配數據結構和緩存去存儲沒壓縮的和已壓縮的數據。如果壓縮器支持臨時壓縮,你的程序還必須分配數據結構和緩存去保留以前幀的信息。

 

 

 

結束壓縮

    你的程序完成數據壓縮後,可使用ICCompressEnd宏去通知壓縮器它工作完成了。如果你想要使用這個函數重新壓縮,你的程序必須重新初始化壓縮器。同樣是通過發送消息ICM_COMPRESS_BEGINICCompressBegin)。

 

 

 

單幀圖像解壓

 

你可使用ICImageDecompress函數解壓縮一個單幀圖像。這個函數返回一個解壓縮DIB的句柄。這個解壓DIB保存在一個CF_DIB格式中。

 

 

 

圖像數據解壓

    你的程序使用一連串的ICDecompressEx函數去控制解壓器。這些函數可以幫助你完成下面的任務:

l     選擇一個解壓器.

l     解壓器準備

l     數據解壓

l     結束解壓工作

你的程序處理數據解壓的過程和數據壓縮的過程類似,只不過現在的輸入格式是一個壓縮了的格式,輸出格式是一個可以顯示的格式。對於要解碼的輸入格式信息通常在流的頭部來獲得。在獲得了輸入的格式後,你的程序就可以使用ICLocate或者ICOpen函數去查找可以處理它的解碼器。

    ICDecompressEx函數和宏是對ICDecompress函數的擴從,它提供了更多的處理能力。ICDeCompressEx,ICDecompressExBegin,ICDecompressExEnd,ICDecompressExQuery在功能上替代了ICDecompress,ICDecopmressBegin,ICDecompressEnd,ICDecompressQuery函數。使用ICDecompressEx函數和宏去替換ICDecompress函數。

 

 

 

解壓器和解壓格式選擇

    如果你想按一種指定的輸出格式來解壓數據,你該使用ICDecompressExQuery函數去查詢解碼器看是否可以支持這中輸入和輸出的格式。

    如果對於你的應用程序,輸出格式並不重要,你只需要去查找可以處理這中輸入格式的解碼器就可以了。使用ICDecompressExQuery去要判斷一個解碼器是否可以處理這種輸入格式,但是要把lpbiDst參數設爲NULL。使用ICM_DECOMPRESS_GET_FORMAT消息(ICDecompressGetFormatSize宏)可以知道爲採用該解壓格式需要分配的緩存大小。發送ICM_DECOMPRESS_GET_FORMAT(ICDecompressGetFormat宏)可以獲得格式數據。解碼器在一個BITMAPINFO數據結構返回它的格式。這個格式一般保存解壓期間的大部分信息。在解碼器成功解壓數據前,你的應用程序應該保存這些信息。

    發送ICM_DECOMPRESS_GET_FORMAT消息可以獲得解碼器需要的默認字節數。

    如果你要定義自己的格式(通過使用ICDecompresssExQuery),必須要獲得這個位圖的調色板。ICDecompressExQuery不通過調色板定義(大部分程序使用標準格式,而不需要獲取調色板)。你的程序通過發送ICM_DECOMPRESS_GET_PALETTE消息可以獲得調色板。(ICDecompressGetPalette宏)

 

 

 

解壓器初始化

你程序選擇瞭解碼器後,你可使用ICDecompressExBegin函數去初始化這個解碼器。這個函數需要這個解碼器的句柄和輸入、輸出的格式。

 

數據解壓

    你可以使用ICDecompressEx函數去解壓一個幀。你的應用程序必須重複使用這個函數,自到所有的幀都解壓了。

    在回放過程中,如果你的視頻圖像滯後(比如圖像比聲音延後),你的程序可以使用ICDECOMPRESS_HURRYUP標誌去加速解碼。使用加速解碼,在解碼過程中可能不會對整個幀都解碼,而時取其中關鍵的一部分數據進行解壓。所以當使用這個標誌的時候,你的程序最好不要嘗試把解壓的數據畫出來。

    你的程序解壓完數據後,發送ICM_DECOMPRESSEX_END消息(ICDecompressExEnd宏)去通知解碼器,它工作完成了。如果你還要重新使用這個函數進行壓縮,你必須要使用ICDecompressExBegin去重新初始化解碼器。

 

 

 

監視壓縮器和解碼器的進度

 

   對於解碼器或者壓縮器的一個長時間的操作,可以通過回調函數來讓你的程序監視整個操作的進度。你可以使用ICSetStatusProc函數把回調函數的地址發送給壓縮器或解碼器。當它們接收到這個地址後,它們發送狀態消息給函數。這些消息就標識了這個操作動作開始,停止,yielding,正在處理。

 

 

 

硬件繪畫能力

 

    一些Renderers可以把它們解壓的視頻幀直接畫到硬件上去。這些Renderers在對ICGetInfo函數的響應中返回VIDCF_DRAW標誌。當使用這類Render的時候,你的程序不用去處理數據解壓。它爲Renderer提供用於繪畫的解壓數據。

 

 

 

如果你程序使用一個代繪畫能立的Renderer,就必須處理下面的任務:

l     選擇一個renderer.

l     指定圖像格式

l     初始畫Renderer

l     把數據畫出來

l     控制繪畫參數

 

 

 

選擇Renderer

 

    ICDrawOpen宏打開一個可以按指定格式繪畫的Renderer。如果成功,它返回一個Rendder的句柄,否則返回0。這個宏使用ICLocate函數去打開這個Renderer。

 

 

 

指定圖形格式

    因爲你的程序不需要去畫解壓縮的數據,所以它不需要知道輸出格式。然而,要必須保證Renderer可以使用輸入格式來繪畫。(通過ICM_DRAW_QUERYICDrawQuery宏來實現)。這個消息不能判斷Renderer是否能畫一個位圖。如果你的程序必須要判斷Renderer是否能繪畫位圖,可以在ICDrawBegin函數使用這個消息。

你的程序使用ICDrawSuggestFormat函數可以得到Renderer的輸入格式。這個函數用於去劃分Render的解壓能力和繪畫能力。大多數程序使用這個函數,而不需要去知道它的輸出格式。

 

 

 

Renderer 初始化

 

    ICDrawBegin函數用於初始化一個Renderer,並告訴它繪畫的地點。這個函數可以完成如下任務:

l     判斷Renderer是否支持一個特定的輸入格式

l     指定繪畫操作是全屏還是僅在一個窗口上進行

l     指定使用源尺寸去顯示圖像

l     定義回放的圖像速度

爲Render提供一些緩存器去存放壓縮數據,將讓操作更有效率。你的程序可以發送ICM_GETBUFFERSWANDTED消息(ICGetBuffersWanted宏)去決定Render需要的緩存大小。你的程序在繪畫前就應該預加載緩存並把它們發送給Render。

 

 

 

把數據畫出來

 

你可以爲繪畫使用ICDraw函數去解壓縮這些數據。這個Renderer,如果在沒有發送ICM_DRAW_START消息(ICDrawStart宏)之前是不會開始進行繪畫的。當你的程序調用這個函數時,這個Renderer就開始繪畫了,函數的dwRate參數除以dwScale參數的值用於指定繪畫幀的速度。當你的程序使用ICDrawBegin函數去初始化Renderer時,就回提供這些參數。繪畫將持續到你的程序發送ICM_DRAW_STOP消息(ICDrawStop)的時候,才停止。

注意:如果在繪畫前,Render緩存了一些數據,你的程序就不能使用ICDrawStart宏,除非通過ICGetBuffersWanted宏,把這些幀的數量發送給Renderer。

 

ICDraw函數的lTime參數指定畫一幀圖像所要的時間。Render通過ICDrawBegin指定的時間比例區劃分這個lTime的整數,從而獲得實際的時間。ICDRaw函數的時間和ICDrawStart成比例。ICDrawStart設定時鐘爲0。比如,你的程序指定時間比例爲1000,lTime爲75。那麼Render繪畫的爲75毫秒。

 

 

 

控制繪畫參賽

    發送ICM_DRAW_GETTIME消息(ICDrawGetTime宏)可以監視Renderer的時鐘,同時發送ICM_DRAW_SETTING消息(ICDrawSetTime宏)可以設置Renderer的時鐘。

    當一個Renderer正在繪畫,這時要改變當前的位置,可以發送ICM_DRAW_WINDOW消息(ICDrawWindow宏)去重新配置窗體。一般,只要窗體改變了就使用該消息。

    如果回放窗體得到了一個真實的調色板消息,你必須發送ICM_DRAW_REALIZE消息(ICDrawRealize宏)讓Renderer去再次實現調色板。程序可以改變調色板,通過發送消息ICM_DRAW_CHANGEPALETTEICDrawChangePalette宏)來實現。要獲得當前調色板可以發送ICM_DRAE_GET_PALETTE消息。

    一些Renderer必須通過特定的指令才能去顯示數據幀,發送如下的消息/宏可以讓這些Renderer去畫數據幀:ICM_DRAW_RENDERBUFFER 消息 ICDrawRenderBuff

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章