avi文件格式詳解
AVI是音頻視頻交錯(Audio Video Interleaved)的英文縮寫,它是Microsoft公司開發的一種符合RIFF文件規范的數字音頻與視頻文件格式,原先用于Microsoft Video for Windows (簡稱VFW)環境,現在已被Windows 95/98、OS/2等多數操作系統直接支持。AVI格式允許視頻和音頻交錯在一起同步播放,支持256色和RLE壓縮,但AVI文件并未限定壓縮標準,因此,AVI文件格式只是作為控制界面上的標準,不具有兼容性,用不同壓縮算法生成的AVI文件,必須使用相應的解壓縮算法才能播放出來。常用的AVI播放驅動程序,主要是Microsoft Video for Windows或Windows 95/98中的Video 1,以及Intel公司的Indeo Video。
在介紹AVI文件前,我們要先來看看RIFF文件結構。AVI文件采用的是RIFF文件結構方式,RIFF(Resource Interchange File Format,資源互換文件格式)是微軟公司定義的一種用于管理windows環境中多媒體數據的文件格式,波形音頻wave,MIDI和數字視頻AVI都采用這種格式存儲。構造RIFF文件的基本單元叫做數據塊(Chunk),每個數據塊包含3個部分,
1、4字節的數據塊標記(或者叫做數據塊的ID)
2、數據塊的大小
3、數據
整個RIFF文件可以看成一個數據塊,其數據塊ID為RIFF,稱為RIFF塊。一個RIFF文件中只允許存在一個RIFF塊。RIFF塊中包含一系列的子塊,其中有一種字塊的ID為"LIST",稱為LIST,LIST塊中可以再包含一系列的子塊,但除了LIST塊外的其他所有的子塊都不能再包含子塊。
RIFF和LIST塊分別比普通的數據塊多一個被稱為形式類型(Form Type)和列表類型(List Type)的數據域,其組成如下:
1、4字節的數據塊標記(Chunk ID)
2、數據塊的大小
3、4字節的形式類型或者列表類型
4、數據
下面我們看看AVI文件的結構。AVI文件是目前使用的最復雜的RIFF文件,它能同時存儲同步表現的音頻視頻數據。AVI的RIFF塊的形式類型是AVI,它包含3個子塊,如下所述:
1、信息塊,一個ID為"hdrl"的LIST塊,定義AVI文件的數據格式。
2、數據塊,一個ID為 "movi"的LIST塊,包含AVI的音視頻序列數據。
3、索引塊,ID為 "idxl"的子塊,定義 "movi"LIST塊的索引數據,是可選塊。
AVI文件的結構如下圖所示,下面將具體介紹AVI文件的各子塊構造。
1、信息塊,信息塊包含兩個子塊,即一個ID為 avih 的子塊和一個ID 為 strl 的LIST塊。
![]() |
"avih"子塊的內容可由如下的結構定義:
typedef struct { DWORD dwMicroSecPerFrame ; //顯示每楨所需的時間ns,定義avi的顯示速率 DWORD dwMaxBytesPerSec; // 最大的數據傳輸率 DWORD dwPaddingGranularity; //記錄塊的長度需為此值的倍數,通常是2048 DWORD dwFlages; //AVI文件的特殊屬性,如是否包含索引塊,音視頻數據是否交叉存儲 DWORD dwTotalFrame; //文件中的總楨數 DWORD dwInitialFrames; //說明在開始播放前需要多少楨 DWORD dwStreams; //文件中包含的數據流種類 DWORD dwSuggestedBufferSize; //建議使用的緩沖區的大小, //通常為存儲一楨圖像以及同步聲音所需要的數據之和 DWORD dwWidth; //圖像寬 DWORD dwHeight; //圖像高 DWORD dwReserved[4]; //保留值 }MainAVIHeader; |
"strl" LIST塊用于記錄AVI數據流,每一種數據流都在該LIST塊中占有3個子塊,他們的ID分別是"strh","strf", "strd";
"strh"子塊由如下結構定義。
typedef struct { FOURCC fccType; //4字節,表示數據流的種類 vids 表示視頻數據流 //auds 音頻數據流 FOURCC fccHandler;//4字節 ,表示數據流解壓縮的驅動程序代號 DWORD dwFlags; //數據流屬性 WORD wPriority; //此數據流的播放優先級 WORD wLanguage; //音頻的語言代號 DWORD dwInitalFrames;//說明在開始播放前需要多少楨 DWORD dwScale; //數據量,視頻每楨的大小或者音頻的采樣大小 DWORD dwRate; //dwScale /dwRate = 每秒的采樣數 DWORD dwStart; //數據流開始播放的位置,以dwScale為單位 DWORD dwLength; //數據流的數據量,以dwScale為單位 DWORD dwSuggestedBufferSize; //建議緩沖區的大小 DWORD dwQuality; //解壓縮質量參數,值越大,質量越好 DWORD dwSampleSize; //音頻的采樣大小 RECT rcFrame; //視頻圖像所占的矩形 }AVIStreamHeader; |
"strf"子塊緊跟在"strh"子塊之后,其結構視"strh"子塊的類型而定,如下所述;如果 strh子塊是視頻數據流,則 strf子塊的內容是一個與windows設備無關位圖的BIMAPINFO結構,如下:
typedef struct tagBITMAPINFO { BITMAPINFOHEADER bmiHeader; RGBQUAD bmiColors[1]; //顏色表 }BITMAPINFO; typedef struct tagBITMAPINFOHEADER { DWORD biSize; LONG biWidth; LONG biHeight; WORD biPlanes; WORD biBitCount; DWORD biCompression; DWORD biSizeImage; LONG biXPelsPerMeter; LONG biYPelsPerMeter; DWORD biClrUsed; DWORD biClrImportant; }BITMAPINFOHEADER; |
如果 strh子塊是音頻數據流,則strf子塊的內容是一個WAVEFORMAT結構,如下:
typedef struct { WORD wFormatTag; WORD nChannels; //聲道數 DWORD nSamplesPerSec; //采樣率 DWORD nAvgBytesPerSec; //WAVE聲音中每秒的數據量 WORD nBlockAlign; //數據塊的對齊標志 WORD biSize; //此結構的大小 }WAVEFORMAT |
"strd"子塊緊跟在strf子塊后,存儲供壓縮驅動程序使用的參數,不一定存在,也沒有固定的結構。
"strl" LIST塊定義的AVI數據流依次將 "hdrl " LIST 塊中的數據流頭結構與"movi" LIST塊中的數據聯系在一起,第一個數據流頭結構用于數據流0,第二個用于數據流1,依次類推。
數據塊中存儲視頻和音頻數據流,數據可直接存于 "movi" LIST塊中。數據塊中音視頻數據按不同的字塊存放,其結構如下所述,
音頻字塊
"##wb"
Wave 數據流
視頻子塊中存儲DIB數據,又分為壓縮或者未壓縮DIB,
"##db"
RGB數據流
"##dc"
壓縮的圖像數據流
看到了吧,avi文件的圖像數據可以是壓縮的,和非壓縮格式的。對于壓縮格式來說,也可采用不同的編碼,也許你曾經遇到有些avi沒法識別,就是因為編碼方式不一樣,如果沒有相應的解碼,你就沒法識別視頻數據。AVI的編碼方式有很多種,比較常見的有 mpeg2,mpeg4,divx等。
索引塊,索引快包含數據塊在文件中的位置索引,能提高avi文件的讀寫速度,其中存放著一組AVIINDEXENTRY結構數據。如下,這個塊并不是必需的,也許不存在。
{
DWORD ckid; //記錄數據塊中子塊的標記
DWORD dwFlags; //表示chid所指子塊的屬性
DWORD dwChunkOffset; //子塊的相對位置
DWORD dwChunkLength; //子塊長度
};
視頻捕捉全教程
前 言
????????? 視頻捕獲是指由專用的視頻采集卡捕獲聲頻和視頻信息,然后將其進行數據化處理,再經過軟件的壓縮進行處理,這時就可對這些數據進行保存、回放、傳輸等各種操作。
???????? Windows專門提供了Video for Windows來對視頻處理進行支持,提供的接口可以被大多數的視頻采集卡支持,并有多種視頻壓縮驅動供選擇(當然視頻壓縮可以自己開發),采集卡支持攝像頭,TV等多種輸入。
一. 視頻捕獲快速入門
????????? 視頻捕捉將一個視頻流和音頻流數字化, 然后存儲在硬盤或其他存儲介質上.
????????? 一個AVICap視窗口句柄描述了聲頻與視頻流的細節, 這樣就使你的應用程序從AVI文件格式, 聲頻視頻緩沖管理, 低層聲頻視頻驅動訪問等等解脫出來, AVICap為應用程序提供了一個靈活的介面, 你可以僅僅使用如下幾行代碼就可以將視頻捕捉加入你的程序:
hWndC = capCreateCaptureWindow ( "My Own Capture Window",
WS_CHILD | WS_VISIBLE , 0, 0, 160, 120, hwndParent, nID);
SendMessage (hWndC, WM_CAP_DRIVER_CONNECT, 0 /* wIndex */, 0L);
SendMessage (hWndC, WM_CAP_SEQUENCE, 0, 0L);
?????????? 一個宏其實也是使用SendMessage, 只不過提供給程序一個更易讀的代碼而已, 下面的這些示例就是使用宏的方法將視頻捕捉加入程序:
hWndC = capCreateCaptureWindow ( "My Own Capture Window",
WS_CHILD | WS_VISIBLE , 0, 0, 160, 120, hwndParent, nID);
capDriverConnect (hWndC, 0);
capCaptureSequence (hWndC);
????????? 當你創建了一個AVICap類的捕捉窗口并將它連接到一個視頻驅動時, 此捕捉窗口即可以開始捕捉數據, 你的程序可以簡單的發送WM_CAP_SEQUENCE消息(或者使用capCaptureSequence宏)來開始捕捉.
????????? 如果是缺省的設置, WM_CAP_SEQUENCE會開始捕捉視頻音頻流到CAPTURE.AVI文件中, 直到下面的某一事件發生為止:
用戶按下了ESC鍵或者一個鼠標鍵
你的應用程序終止或異常中斷捕捉操作
磁盤已滿
????????????? 在一個應用程序里, 你可以發送WM_CAP_STOP消息來終止捕捉數據(或者使用capCaptureStop宏), 你也可以發送WM_CAP_ABORT消息(或者使用capCaptureAbort宏)來終止.
二.基本的捕獲設置
????????? 基本的捕獲設置包括:設置捕獲速度(每秒捕獲多少幀),是否同時捕獲聲頻,捕獲緩沖,允許最大丟失多少幀,是否使用DOS內存,以及用鍵盤的哪個鍵或鼠標 的哪個鍵來終止捕獲等等。這些基本的設置都可以使用CAPTUREPARAMS結構來描述,你可以使用capCaptureGetSetup宏來得到當前 的設置,然后改變此結構的成員變量,再使用capCaptureSetSetup宏設置新的設置。
例如:
1.設置捕獲速度:
?????????? 捕捉速度是指捕捉任務每秒鐘捕獲的幀數, 你可以發送WM_CAP_GET_SEQUENCE_SETUP消息(或者使用capCaptureGetSetup宏)來得到當前的捕捉速度, 當前的捕捉速度保存在CAPTUREPARAMS結構的dwRequestMicroSecPerFrame成員變量中, 你可以通過設置此變量來改變當前設置, 單位是每毫秒連續的幀數, 你可以發送WM_CAP_SET_SEQUENCE_SETUP消息(或者使用capCaptureSetSetup宏), dwRequestMicroSecPerFrame的值是66667, 相當于每秒15幀.
2.設置終止捕獲
?????????? 你可以允許用戶按下某鍵或某組合鍵或者鼠標的左右鍵來終止一個捕獲任務, 如果是實時的捕獲, 則捕獲的文件將會被丟棄; 如果是單步捕獲, 在終止之前所捕獲的內容將會被保存.
????????? 你可以通過發送WM_CAP_GETQUENCE_SETUP消息(或者使用capCaptureGetSetup宏)來得到當前的設置, 當前的按鍵設置保存在CAPTUREPARAMS的vKeyAbort成員中, 當前的鼠標設置保存在fAbortLeftMouse和fAbortRightMouse成員中. 你可以設置新的按鍵或按鍵組合, 或者鼠標左右鍵, 當你修改的CAPTUREPARAMS后,應該發送WM_CAP_SET_SEQUENCE_SETUP消息來進行更新(或者使用 capCaptureSetSetup宏). 缺省的按鍵是VK_ESCAPE. 你必須在指定按鍵之前使用RegisterHotKey函數, 鼠標缺省的值是fAbortLeftMouse和fAbortRightMouse都為TRUE.
3.捕獲的時間限制
?????????? CAPTUREPARAMS結構中的fLimitEnabled指示是否有時間限度, wTimeLimit指示最大的持續時間, 單位為秒.
????????? 得到fLimitEnabled和wTimeLimit的值可以發送WM_CAP_GET_SEQUENCE_SETUP消息(或使用 capCatureGetSetup宏), 當設置了這些成員變量后, 應該發送消息WM_CAP_SET_SEQUENCE_SETUP消息(或capCaptureSetSetup宏)來更新CAPTUREPARAMS結 構.
三.關于捕獲窗口
??????? 在捕獲之前必須創建一個捕獲窗口(capture window),在發送消息或使用宏的過程中都需要使用此窗口。
1.創建一個AVICap捕獲窗口
??????? 你可以使用capCreateCaptureWindow函數來創建一個AVICap捕獲窗口, 此函數將會返回一個句柄, 此句柄以后在發送消息時要用.
??????? 你可以在一個程序里創建一個或多個捕獲窗口, 然后給每一個窗口連接不同的捕獲設置.
2.將一個捕獲窗口連接至捕獲設備
?????????? 你可以動態的在一個捕獲窗口與一個捕獲設備之前連接或斷接, 你可以發送WM_CAP_DRIVER_CONNECT消息來使一個捕獲窗口與一個捕獲設備連接或關聯. 當連接上以后, 你就可以通過捕獲窗口向捕獲設備發送各種消息.
??????? 如果你的系統里裝有多個捕獲設備, 你可以在發送WM_CAP_DRIVER_CONNECT消息時用wParam參數指定使用哪一個, 此參數是登記在SYSTEM.INI文件的[drivers]一節里的列表中的某一項, 0為第一個.
????????? 你可以使用capGetDriverDescription函數來得到已安裝的捕獲設備的名稱及版本, 這樣你的程序就可以列舉所有已安裝的捕獲設備和驅動, 這樣用戶就可以選擇其中的一個來與你的捕獲窗口連接.
???????? 你可以發送WM_CAP_DRIVER_GET_NAME消息(或capDriverGetName宏)來得到連接到捕獲窗口的捕獲設備的名稱, 得到版本發送WM_CAP_DRIVER_GET_VERSION消息(或capDriverGetVersion宏)
???????? 你可以發送WM_CAP_DRIVER_DISCONNECT消息(或capDriverDisconnect宏)來斷接.
3. 父窗口與子窗口的交互
?????????? 一些象WM_PALETTECHANGED和WM_QUERYNEWPALETTE的系統級消息只能發送到頂級窗口或OVERLAPPED窗口, 如果一個捕獲窗口是子窗口,就必須通過父窗口轉送.
???????? 同樣的, 如果父窗口的尺寸改變了, 它就需要通知捕獲窗口, 相反地, 如果捕獲窗口的尺寸改變了, 捕獲窗口就需要發送消息給父窗口, 一個簡單的方法就是始終保持捕獲窗口的尺寸與視頻流的尺寸一致, 并隨時將尺寸的改變通知父窗口.
4.捕獲窗口的狀態
????????? 你可以發送WM_CAP_GET_STATUS消息(或capGetStatus宏)來得到當前捕獲窗口的狀態, 得到的是一個CAPSTATUS結構的拷貝, 它包含圖片的尺寸, 卷軸的當前位置, overlay和preview是否已設置.
??????? 因為CAPSTATUS信息是動態的, 你的程序應該只要捕獲的視頻流的尺寸或格式可能發生了改變就應該進行刷新(例如: 顯示了捕獲設備的視頻格式以后).
??????? 改變捕獲窗口的尺寸并不影響實際的捕獲的視頻流的尺寸, 視頻捕獲設備的格式對話框捕獲頻流的尺寸.
四.視頻捕獲驅動和音頻驅動
1.視頻捕獲驅動的性能:
????????? 你可以通過發送WM_CAP_DRIVER_GET_CAPS消息(或者capDriverGetCaps宏)來得到當前連接的視頻驅動的硬件性能. 得到的信息保存在CAPDRIVERCAPS結構中.
2.視頻對話框:
????????? 每一個視頻驅動能夠提供四個對話框來控制視頻捕獲和數字化處理, 定義壓縮品質等, 這些對話框都定義在視頻捕獲驅動中.
????????? Video Source對話框用于控制選擇視頻來源, 此對話框列舉了此視頻捕獲卡連接的所有視頻源(典型的例如:SVHS和合成輸入), 并提供了改變色調, 對比度, 飽和度. 如果視頻驅動支持此對話框, 你就可以顯示并更新它, 使用WM_CAP_DLG_VIDEOSOURCE消息(或capDlgVideoSource宏).
?????????? Video Format對話框定義視頻幀的尺寸以及精度, 視頻捕獲卡的壓縮設置. 如果卡支持的話, 可以發送消息WM_CAP_DLG_VIDEOFORMAT消息或(capDlgVideoFormat宏).
?????????? Video Display對話框控制在視頻捕獲期間在顯示器上的顯示, 此控制不會影響視頻數字數據, 但是他們可能會影響數字信號的表現形式, 例如: 如果捕獲設備支持overlay, 可能允許改變色調和飽和度, 關鍵色彩 或者overlay隊列. 如果卡支持, 你可以發送WM_CAP_DLG_VIDEODISPLAY消息(或者使用capDlgVideoDisplay宏).
?????????? Video Compression對話框控制壓縮品質, 如果卡支持, 發送消息WM_CAP_DLG_VIDEOCOMPRESSION(或capDlgVideoCompression宏).
3.Preview 和 Overlay模式:
???????????????? 一個視頻捕獲驅動對進入的視頻流有兩種工作模式: Preview模式和overlay模式, 如果一個捕獲驅動能夠執行兩種方法, 用戶可以在其中選擇一種.
?????????????? Preview模式把從捕獲硬件傳來的數據送入系統內存并使用圖形設備介面(GDI)將數字化幀顯示在捕獲窗口內. 應用程序可以在父窗口失去焦點時減緩顯示速度, 當重新又得到焦點后加快顯示速度, 此種模式要占用大量CPU時間.
有三種消息控制Preview操作:
WM_CAP_SET_PREIVEW消息(capPreview宏)允許或禁止preview模式
WM_CAP_SET_PREVIEWRATE(capPreviewRate宏)當幀在preview模式顯示時設置速度.
WM_CAP_SET_SCALE(capPreviewScale宏)允許或禁止preview視頻的縮放比例.
????????? 當preview和scaling同時使用, 捕獲的視頻幀將會根據捕獲窗口的尺寸自動縮放, 允許preview模式會自動關閉overlay模式.
????????????????????? overlay模式是一個硬件函數它將數據送入捕獲緩沖區中因而不占用CPU資源. 你可以發送消息WM_CAP_SET_OVERLAY(或capOverlay宏)給捕獲窗口來啟用或終止overlay模式, 允許overlay模式會自動禁止preview模式.
?????????????? 你同時也可以在preview模式或overlay模式里發送WM_CAP_SET_SCROLL消息(或capSetScrollPos宏)來設置視頻幀的客戶區卷軸位置.
4.視頻格式
??????????? 你可以通過發送WM_CAP_GET_VIDEOFORMAT消息(或capGetVideoFormat和capGetVideoFormatSize 宏)來得到視頻格式的結構或結構的尺寸. 你可以通過發送CAP_SET_VIDEOFORMAT消息(或capSetVideoFormat宏)來設置視頻格式.
5.視頻捕獲設置
?????????? CAPTUREPARMS結構包含了對視頻捕獲流的控制參數, 你可以完成以下這些任務:
指定幀數
指定分配多少視頻緩沖
允許或禁止聲頻捕獲
指定捕獲的時間間隔
指定在捕獲的過程中是否使用MCI設置(VCR或者videodisc)
指定終止流的鍵盤或鼠標
specify the type of video averaging applied during capture.
得到:WM_CAP_GET_SEQUENCE_SETUP消息(或capCaptureGetSetup宏)
設置:WM_CAP_SET_SEQUENCE_SETUP消息(或capCaptureSetSetup宏)
6.聲頻格式
?????????????? 你可以通過發送WM_CAP_GET_AUDIOFORMAT消息(或capGetAudioFormat宏和 capGetAudioFormatSize宏)來得到當前捕獲音頻數據的格式或尺寸格式。缺省的聲頻格式是:單聲道、8位、11kHz PCM。 當你使用WM_CAP_GET_AUDIOFORMAT時,總是使用WAVEFORMATEX結構。
???????????? 設置發送消息WM_CAP_SET_AUDIOFORMAT消息(或capSetAudioFormat宏),可以傳送WAVEFORMAT,WAVEFORMATEX,PCMWAVEFORMAT結構指針。
五.使用視頻捕獲
1.創建捕獲窗口(Creating a Capture Window)
hWndC = capCreateCaptureWindow (
(LPSTR) "My Capture Window", // window name if pop-up
WS_CHILD | WS_VISIBLE, // window style
0, 0, 160, 120, // window position and dimensions
(HWND) hwndParent,
(int) nID /* child ID */);
2.連接到捕獲驅動(Connecting to a Capture Driver)
?????????? 下面的例子是將MSVIDEO驅動連接到句柄為hWndC的捕獲窗口, 然后調用capDriverDisconnect宏來斷接.
fOK = SendMessage (hWndC, WM_CAP_DRIVER_CONNECT, 0, 0L);
//
// Or, use the macro to connect to the MSVIDEO driver:
// fOK = capDriverConnect(hWndC, 0);
//
// Place code to set up and capture video here.
//
capDriverDisconnect (hWndC);
3.列舉所有已安裝的捕獲驅動(Enumerating Installed Capture Drivers)
?????????? 下面的例子使用capGetDriverDescription函數得到已安裝的捕獲驅動的名稱及版本:
char szDeviceName[80];
char szDeviceVersion[80];
for (wIndex = 0; wIndex < 10; wIndex++)
{
if (capGetDriverDescription (wIndex, szDeviceName,
sizeof (szDeviceName), szDeviceVersion,
sizeof (szDeviceVersion))
{
// Append name to list of installed capture drivers
// and then let the user select a driver to use.
}
}
4.得到捕獲驅動的性能(Obtaining the Capabilities of a Capture Driver)
????????????? 發送WM_CAP_DRIVER_GET_CAPS消息可以得到捕獲驅動的性能,并保存入一個CAPDRIVERCAPS結構.每當程序連接一個新的捕獲 驅動到一個捕獲窗口時, 就應該更新CAPDRIVERCAPS結構. 下面的程序舉例說明了如何使用capDriverGetCaps宏來得到捕獲驅動的性能:
CAPDRIVERCAPS CapDrvCaps;
SendMessage (hWndC, WM_CAP_DRIVER_GET_CAPS,
sizeof (CAPDRIVERCAPS), (LONG) (LPVOID) &CapDrvCaps);
// Or, use the macro to retrieve the driver capabilities.
// capDriverGetCaps(hWndC, &CapDrvCaps, sizeof (CAPDRIVERCAPS));
5.得到捕獲窗口的狀態(Obtaining the Status of a Capture Window)
???????????????? 下面的例子使用SetWindowPos函數使捕獲窗口與進來的視頻流尺寸保持一致, 視頻流的基本信息是使用capGetStatus宏得到的, 保存在CAPSTATUS結構中.
CAPSTATUS CapStatus;
capGetStatus(hWndC, &CapStatus, sizeof (CAPSTATUS));
SetWindowPos(hWndC, NULL, 0, 0, CapStatus.uiImageWidth,
CapStatus.uiImageHeight, SWP_NOZORDER | SWP_NOMOVE);
6.顯示對話框設置視頻特征(Displaying Dialog Boxes to Set Video Characteristics)
????????????? 每個視頻捕獲卡一般能提供三個不同的對話框用于控制視頻捕獲及數字化處理. 下面的例子說明如何顯示這些對話框, 在顯示這些對話框之前,使用了capDriverGetCaps宏來檢查CAPDRIVERCAPS結構, 以檢測該卡是否有顯示這些對話框:
CAPDRIVERCAPS CapDrvCaps;
capDriverGetCaps(hWndC, &CapDrvCaps, sizeof (CAPDRIVERCAPS));
// Video source dialog box.
if (CapDriverCaps.fHasDlgVideoSource)
capDlgVideoSource(hWndC);
// Video format dialog box.
if (CapDriverCaps.fHasDlgVideoFormat)
{
capDlgVideoFormat(hWndC);
// Are there new image dimensions?
capGetStatus(hWndC, &CapStatus, sizeof (CAPSTATUS));
// If so, notify the parent of a size change.
}
// Video display dialog box.
if (CapDriverCaps.fHasDlgVideoDisplay)
capDlgVideoDisplay(hWndC);
7.得到和設置視頻格式(Obtaining and Setting the Video Format)
?????????????????? BITMAPINFO結構的長度既適應于標準的也適應于壓縮的數據格式, 所有程序必須總是詢問此結構的尺寸以便在得到當前的視頻格式之前分配內存. 下面的例子就是使用capGetVideoFormatSize宏來得到緩沖區尺寸并調用capGetVideoFormat宏來得到當前的視頻格式.
LPBITMAPINFO lpbi;
DWORD dwSize;
dwSize = capGetVideoFormatSize(hWndC);
lpbi = GlobalAllocPtr (GHND, dwSize);
capGetVideoFormat(hWndC, lpbi, dwSize);
// Access the video format and then free the allocated memory.
????????????? 程序可以使用capSetVideoFormat宏(或WM_CAP_SET_VIDEOFORMAT消息)發送一個BITMAPINFO頭結構給捕獲窗口, 因為視頻格式是設備細節, 你的程序應該檢查返回值以便確定此格式是否已被接受.
8. 預覽視頻(Previewing Video)
???????????? 下面的例子使用capPreviewRate宏來設置每66毫秒顯示一幀, 并使用capPreview宏將它放置在捕獲窗口里.
capPreviewRate(hWndC, 66); // rate, in milliseconds
capPreview(hWndC, TRUE); // starts preview
// Preview
capPreview(hWnd, FALSE); // disables preview
9.將視頻設置為overlay模式(Enabling Video Overlay)
??????????? 下面的例子: capDriverGetCaps宏確定此捕獲卡是否有overlay功能, 如果有就使用宏來設置它
CAPDRIVERCAPS CapDrvCaps;
capDriverGetCaps(hWndC, &CapDrvCaps, sizeof (CAPDRIVERCAPS));
if (CapDrvCaps.fHasOverlay)
capOverlay(hWndC, TRUE);
10.命名捕獲文件(Naming the Capture File)
???????????? 下面的例子: 使用capFileSetCaptureFile宏來指定預備文件名為:MYCAP.AVI, capFileAlloc宏預先指定它的大小為5M.
char szCaptureFile[] = "MYCAP.AVI";
capFileSetCaptureFile( hWndC, szCaptureFile);
capFileAlloc( hWndC, (1024L * 1024L * 5));
11.格式化聲頻捕獲(Formatting Audio Capture)
???????????? 下面的例子使用capSetAudioFormat來設置聲頻格式為:11kHz, PCM 8位, 立體聲
WAVEFORMATEX wfex;
wfex.wFormatTag = WAVE_FORMAT_PCM;
wfex.nChannels = 2; // Use stereo
wfex.nSamplesPerSec = 11025;
wfex.nAvgBytesPerSec = 22050;
wfex.nBlockAlign = 2;
wfex.wBitsPerSample = 8;
wfex.cbSize = 0;
capSetAudioFormat(hWndC, &wfex, sizeof(WAVEFORMATEX));
12.改變視頻捕獲設置(Changing a Video Capture Setting)
?????????????? 下面的例子使用capCaptureGetSetup和capCaptureSetSetup宏得將捕獲幀數從缺省的15幀改成每秒10幀.
CAPTUREPARMS CaptureParms;
float FramesPerSec = 10.0;
capCaptureGetSetup(hWndC, &CaptureParms, sizeof(CAPTUREPARMS));
CaptureParms.dwRequestMicroSecPerFrame = (DWORD) (1.0e6 /FramesPerSec);
capCaptureSetSetup(hWndC, &CaptureParms, sizeof (CAPTUREPARMS));
13.捕獲數據(Capturing Data)
??????????????? 下面的例子使用capCaptureSequence宏來開始捕獲視頻并使用capFileSaveAs宏來將捕獲的數據拷貝至NEWFILE.AVI文件中.
char szNewName[] = "NEWFILE.AVI";
// Set up the capture operation.
capCaptureSequence(hWndC);
// Capture.
capFileSaveAs(hWndC, szNewName);
14.增加一個信息塊(Adding an Information Chunk)
?????????????? 如果你需要在你的程序捕獲的聲頻和視頻數據中加入你的其他信息, 你可以創建一個信息塊并將它們插入捕獲文件中, 信息塊可以包含一些典型的信息, 例如:版權信息,視頻來源, 外部定位信息等. 下面的例子使用capFileSetInfoChunk宏來插入一個信息塊, 里面包含了一個SMPTE的時間代碼.
// This example assumes the application controls
// the video source for preroll and postroll.
CAPINFOCHUNK cic;
// .
// .
// .
cic.fccInfoID = infotypeSMPTE_TIME;
cic.lpData = "00:20:30:12";
cic.cbData = strlen (cic.lpData) + 1;
capFileSetInfoChunk (hwndC, &cic);
15.在程序中加入一個回調函數(Adding Callback Functions to an Application)
?????????????? 一個程序可以為捕獲窗口登記一個回調函數以便在以下的這些情況下通知程序.
狀態改變
錯誤發生
視頻框架和聲頻緩沖區變得可用
程序應用在捕獲視頻流的過程中接收
?????????? 下面的例子創建一個捕獲窗口并登記狀態,錯誤,視頻流和框架回調函數在消息處理對列中, 也包括了一個終止回調函數的說明.
case WM_CREATE:
{
char achDeviceName[80]
char achDeviceVersion[100]
char achBuffer[100]
WORD wDriverCount = 0
WORD wIndex
WORD wError
HMENU hMenu
// Create a capture window using the capCreateCaptureWindow macro.
ghWndCap = capCreateCaptureWindow((LPSTR)"Capture Window",
WS_CHILD | WS_VISIBLE, 0, 0, 160, 120, (HWND) hWnd, (int) 0);
// Register the error callback function using the
// capSetCallbackOnError macro.
capSetCallbackOnError(ghWndCap, fpErrorCallback);
// Register the status callback function using the
// capSetCallbackOnStatus macro.
capSetCallbackOnStatus(ghWndCap, fpStatusCallback);
// Register the video-stream callback function using the
// capSetCallbackOnVideoStream macro.
capSetCallbackOnVideoStream(ghWndCap, fpVideoCallback);
// Register the frame callback function using the
// capSetCallbackOnFrame macro.
capSetCallbackOnFrame(ghWndCap, fpFrameCallback);
// Connect to a capture driver
break;
}
case WM_CLOSE:
{
// Use the capSetCallbackOnFrame macro to
// disable the frame callback. Similar calls exist for the other
// callback functions.
capSetCallbackOnFrame(hWndC, NULL);
break;
}
16.創建一個狀態回調函數(Creating a Status Callback Function)
??????????? 下面的例子是創建一個簡單的狀態回調函數,登記此回調函數使用capSetCallbackOnStatus宏.
// StatusCallbackProc: status callback function
// hWnd: capture window handle
// nID: status code for the current status
// lpStatusText: status text string for the current status
//
LRESULT PASCAL StatusCallbackProc(HWND hWnd, int nID,
LPSTR lpStatusText)
{
if (!ghWndMain)
return FALSE;
if (nID == 0) { // Clear old status messages.
SetWindowText(ghWndMain, (LPSTR) gachAppName);
return (LRESULT) TRUE;
}
// Show the status ID and status text...
wsprintf(gachBuffer, "Status# %d: %s", nID, lpStatusText);
SetWindowText(ghWndMain, (LPSTR)gachBuffer);
return (LRESULT) TRUE;
}
17.創建一個錯誤回調函數( Creating an Error Callback Function)
???????????? 下面的例子是創建一個簡單的錯誤回調函數,登記此回調函數使用capsetCallbackOnError宏:
// ErrorCallbackProc: error callback function
// hWnd: capture window handle
// nErrID: error code for the encountered error
// lpErrorText: error text string for the encountered error
//
LRESULT PASCAL ErrorCallbackProc(HWND hWnd, int nErrID,
LPSTR lpErrorText)
{
if (!ghWndMain)
return FALSE;
if (nErrID == 0) // Starting a new major function.
return TRUE; // Clear out old errors.
// Show the error identifier and text.
wsprintf(gachBuffer, "Error# %d", nErrID);
MessageBox(hWnd, lpErrorText, gachBuffer,
MB_OK | MB_ICONEXCLAMATION);
return (LRESULT) TRUE;
}
18.創建一個框架回調函數(Creating a Frame Callback Function)
????????????? 登記此回調函數使用capSetCallbackOnFrame宏:
// FrameCallbackProc: frame callback function
// hWnd: capture window handle
// lpVHdr: pointer to struct containing captured
// frame information
//
LRESULT PASCAL FrameCallbackProc(HWND hWnd, LPVIDEOHDR lpVHdr)
{
if (!ghWndMain)
return FALSE;
wsprintf(gachBuffer, "Preview frame# %ld ", gdwFrameNum++);
SetWindowText(ghWndMain, (LPSTR)gachBuffer);
return (LRESULT) TRUE
}
?
六.將四個標準對話框改成函數調用形式
????????????? 系統提供了四個標準的對話框:AudioFormat, VideoFormat, VideoSource, Video Compression,但有時程序希望通過函數控制它們,而不是使用系統提供的那個單一的對話框,此時就應該使用函數調用的方法:
AudioFormat對話框
??????????? 可以通過使用capSetAudioFormat來實現,此時要使用WAVEFORMATEX結構。
例如:改成PCM格式,立體聲,16聲道,12.05kHz,則:
WAVEFORMATEX audioFormat;
// 確定寬度
acmMetrics(NULL, ACM_METRIC_MAX_SIZE_FORMAT,&dwSize);
dwSize = max (dwSize, capGetAudioFormatSize (m_hwCapCapturing));
// 設置參數
audioFormat.wFormatTag = WAVE_FORMAT_PCM;
audioFormat.nChannels = 2;
audioFormat.nSamplesPerSec = 120500;
audioFormat.wBitsPerSample =16;
audioFormat.nBlockAlign = nBitsPerSample * nChannels / 8;
audioFormat.nAvgBytesPerSec =
audioFormat.nBlockAlign * nSamplesPerSec;
// 更新
capSetAudioFormat(ghCapWnd,&audioFormat,dwSize);
VideoFormat對話框
????????????????? 可以通過使用capSetVideoFormat來實現,此時要使用BITMAPINFOHEADER結構。
例如:設置圖片大小為RGB24位歲,大小為230X160
BITMAPINFOHEADER bi;
DWORD dwSize,dw;
bi.biSize = sizeof(BITMAPINFOHEADER);
bi.biWidth = 320; // 起作用
bi.biHeight = 160; // 起作用
bi.biPlanes = 1;
bi.biBitCount = 24;
bi.biCompression = BI_RGB;
bi.biSizeImage = 0;
bi.biXPelsPerMeter = 176;
bi.biYPelsPerMeter = 144;
bi.biClrUsed = 0;
bi.biClrImportant = 0;
dwSize = bi.biSize + ((bi.biBitCount > 8 || bi.biClrUsed) ? (bi.biClrUsed * sizeof(PALETTEENTRY)) : (2 ^ bi.biBitCount * sizeof(PALETTEENTRY)));
dw = capSetVideoFormat(m_hwCapCapturing, &bi, dwSize);
VideoSource對話框
??????????????? 沒有找到現成的方法,但視頻捕獲卡提供的CD里面有一個動態鏈接庫可以實現。
Video Compression對話框
???????????? 可以通過使用ICOpen,ICInfo等函數聯合起來,得到當前系統里面的視頻壓縮驅動的列表,并可選擇其一,MSDN里面有一個程序示范了此用戶,程序名叫:ICWalk。
WINCE聲音驅動模型概述(1)
1.1 WINCE的聲音模型
?
?? 標準的WINDOWS CE下的聲音處理模型。
1)? 聲音應用在使用WAVE接口函數的時候被COREDLL模塊分了類:如果是簡單的PCM數據,則直接進入ADM模塊,進行SoftWare Mixer或者連Mixer也省了,直接調用Driver放音;如果是壓縮模式的數據則進入ACM模塊進行解壓處理后(比如:GSM/G7XX等等…)再次進入ADM模塊。 此外,如果DRIVER支持硬件的Mixer,則應用層直接Bypass掉ACM和ADM,直接進入到驅動進行軟件或者硬件進行混音和放音。
?
2)? ACM、Software Mixer都是可選配單元,可以通過注冊表或者CE內核配置參數添加或者刪除。
?
?
1.2 WINCE的聲音驅動模型
l???????? 分層模型
?
?
?這是一個標準的分層 MDD-PDD 流式接口模型。 應用層的WAVEAPI都使用WaveAPI Manager(WaveApi.DLL)通過DeviceIOControl映射成為WAV_IOControl函數進行處理。
?MDD層是微軟的標準實現,通過DDSI (device driver service-provider interface )和PDD層實現進行連接。MDD通過消息來通知PDD層(PDD_WaveProc處理)
?
MDD層接口函數
·???????????????? WAV_Close
·???????????????? WAV_Deinit
·???????????????? WAV_Init
·???????????????? WAV_IOControl
·???????????????? WAV_Open
·???????????????? WAV_PowerDown
·???????????????? WAV_PowerUp
·???????????????? WAV_Read
·???????????????? WAV_Seek
·???????????????? WAV_Write
DDSI接口函數
·???????????????? PDD_AudioDeinitialize
·???????????????? PDD_AudioGetInterruptType
·???????????????? PDD_AudioInitialize
·???????????????? PDD_AudioMessage
·???????????????? PDD_AudioPowerHandler
·???????????????? PDD_WaveProc
?
但是分層模型也有一定的限制
·???????????????? 只支持一個設備
·???????????????? 不支持多個流
·???????????????? 對循環支持的不好
·???????????????? 對多個流數據支持的不是很好
?
l???????? 單片模型
?
?
對于單片模型,WINCE是希望過渡到這個方案上的。該模型下,驅動的移植
還是很簡單的。同時,該模型也客服了以前只能通過內核的MIXER來支持多個流的局限,在驅動層面即支持輸入和輸出多個流的混音操作。
?
l???????? 分層模型的基本運轉
分層模型支持同時放音和錄音操作。MDD層管理著一個中斷處理程序和多個
DMA BUFFERS,一般放音和錄音都使用雙DMA 緩沖。
比如,放音的時候,應用通過WAVEAPI傳遞過來一些數據,MDD層通過 PDD_WaveProc 函數發送 WPDM_START消息給PDD,PDD層將數據拷貝到DMA緩沖,并啟動硬件放音。當DMA完成一個BUFFER的數據傳送后,會產生一個硬件中斷,PDD層會設定一個 AUDIO_STATE_OUT_PLAYING標志 。如果MDD發現有更多的數據要傳送就會通過WPDM_CONTINUE.消息讓PDD層繼續負責填充DMA緩沖,否則就發送一個WPDM_ENDOFDATA.消息讓PDD層停止DMA。
WINCE聲音驅動模型概述(2)--WINCE WAVE接口模型詳解
WINCE WAVE接口模型詳解
1、標準的WAVE流式驅動程序接口
WAVE的驅動程序提供標準的流式接口給高層,但真正產生關鍵作用的是
WAV_IOControl這個函數。該函數的以下兩個參數最重要:
dwCode
具體的IO控制命令,包括:
IOCTL_WAV_MESSAGE? (處理放音和錄音相關的所有操作)
IOCTL_DSDVR_MESSAGE (DirectSound 動作處理)
IOCTL_MIX_MESSAGE.?? (MIXER的操作)
pBufIn
指向了MMDRV_MESSAGE_PARAMS結構,該結構如下:
????? Struct {
???????????? UINT uDeviceId;
?????? UINT uMsg;
?????? DWORD dwUser;
?????? DWORD dwParam1;
?????? DWORD dwParam2;
}MMDRV_MESSAGE_PARAMS;
????????????????????
???????????????????? uDeviceId: 0,1,2 。。。 如果是0代表全局或者缺省的設備
???????????????????? uMsg :用作(WIDM_*),(WODM_*), (MXDM_*)三種消息
???????????????????? dwUser:實例的具體標識
??
?????? 通過這個函數傳遞的高層命令,最終導致驅動的具體動作,放音、錄音或者混音。
?
2、關鍵的數據結構
??????? WAVE OPEN時候使用的數據結構:
????????????? WAVEFORMATEX{
???????????????????? WORD? wFormatTag;
???????????????????? WORD? wChannels;
???????????????????? DWORD?????? nSamplesPerSec;
???????????????????? DWORD? nAvgBytesPerSec;
???????????????????? WORD? nBlockAlign;
???????????????????? WORD? nBitsPerSample;
???????????????????? WORD? cbSize;
}
該數據結構定義了一個Audio Stream所需要的大部分信息,當做WAVE OPEN的時候可能應用可能會首先嘗試打開,這個時候WAVE_IO_CONTROL 就會傳遞一個WAVE_FORMAT_QUERY 給驅動,而驅動只是簡單的檢查是否真正的支持請求的格式,而不真正打開設備。
?
每次應用添加聲音數據的時候會使用如下數據結構:
Struct {
LPSTR lpData;
DWORD dwBufferLength;
DWORD dwBytesRecorded;
DWORD dwUser;
DWORD dwFlags;
DWORD dwLoops;
Struct wavehdr_tag *lpNext;
DWORD Reserved;
}
其中dwFlags定義了幾個標志:
????????????? WHDR_BEGINLOOP :指明這個BUFFER是否要求被自動重放 (APP設)
????????????? WHDR_DONE????????? :指明這個BUFFER已經處理完畢(驅動設)
????????????? WHDR_ENDLOOP :指明這個BUFFER是否結束重放(APP設)
????????????? WHDR_INQUEUE :指明這個BUFFER入隊列(驅動設)
????????????? WHDR_PREPARED:指明這個BUFFER的確準備好(APP或驅動設)
這幾個標志是應用和驅動通信的關鍵,也是對隊列操作的關鍵。應用和驅動通過設定這些標志位,讓數據不斷在驅動和應用之間流動,從而完成錄放的各種操作。當然,這個過程還要配合一些WAVE 消息。
WINCE聲音驅動模型概述(3)--放音的消息解析
放音的消息解析
WINCE的聲音驅動模型在放音的工作中定義了21個消息(懶了,不再列舉了),但在具體實現中并不是每個消息都必須實現。
消息很多,特別是在具體實現中需要和DMA操作模型配合使用,因此理解消息的用途和推敲它們之間的關系就顯得格外重要了!
通過仔細推敲它們之間的關系,我們可以將放音的整個過程規劃成如下一些狀態,并用狀態遷移圖來理解消息的使用,整個過程的操作就非常簡單了。
WINCE聲音驅動模型概述(4)
混音的處理
?????? 如果要WINDOWS CE的聲音驅動模型支持混音,則要考慮如下問題:
1)? 聲音設備是否支持硬件混音
2)? 聲音設備需要工作在同一種采樣頻率下
3)? 聲音設備要能夠同時支持錄音和放音操作
?
而聲音的驅動要負責完成聲音采集的混音和聲音放音的混音。其基本原理如下:
1)? 將聲音設備設定在一個頻率下,比如:44.1KHZ,16BIT
2)? 驅動允許打開多個音頻流,每個音頻流可以允許不同的采樣率,比如: (A:8KHZ,8BIT??? B: 44.1KHZ,16BIT)
3)? 放音的混音在最后的數據準備階段(即放入到DMA前),用合成算法將所有的流進行數學運算,得出聲音設備采樣頻率下(44.1KHZ,16BIT)的數據。數據通過DMA發送到CODEC中。
4)? 錄音的混音操作,都是從聲音設備采樣頻率下(44.1KHZ,16BIT)下得到采樣的基本數據,然后通過數學運算分配到不同的頻率下的音頻流上。
5)? 要注意的就是合成的時候要注意數據不溢出;分開的時候要注意數據速率的匹配和數據寬度的匹配。
?
中斷和DMA驅動模型
聲音系統一般都使用DMA作為數據傳輸的基本手段,因此DMA的操作模型對于
聲音子系統的處理有著特別關鍵的作用。DMA的聲音操作一般都是用雙BUFFER作為
數據緩沖(錄和放)都如此,一個BUFFER在被DMA占用的時候,另一個BUFFER
就可以被CPU占用,從而提高效率。顯而易見,聲音子系統的硬件操作是一個典型的
生產者消費者模式,因此,對共享資源的控制就特別重要。
DMA的硬件實現,不同的CPU略有不同。有的嵌入式芯片做的簡單,不支持DMA地址的鏈式連接,有的則支持,但不影響具體實現。他們相同的點是:
l???????? 一般一個DMA有N個子CHANNEL
l???????? 每一個CHANNEL都可以互不干擾的獨立運轉或者停機,自有一個狀態機
l???????? DMA使用一個總的DMA 中斷通知CPU,然后由軟件負責查找具體某個CHANNEL。
?
如果一個系統中很多驅動都需要用到DMA,那么就需要一個DMA ENGINE來總
協調DMA的運作,否則就會出現驅動爭用DMA的問題。如果只有一個驅動使用DMA系統,則實現起來的障礙會小不少。
雙聲道系統中的聲音驅動,DMA運作都是采用兩個通道獨立完成錄、放的操作,并且每個通道都采用雙BUFFER的策略,來保證DMA和CPU可以近乎同時的工作,互不影響。而且,一個在高層支持多個流的聲音驅動也要注意對DMA這個硬件設備的操作要保持互斥。
在實際驅動的實現過程中,這種雙通道、雙BUFFER的驅動有很多細節要注意,要注意處理好幾大類關系:
1)? 硬件 DMA和硬件CODEC之間的關系
在整個放音聲音數據傳輸系統中有:
?????? APP buffer ?à DMA buffer ?à I2S(AC97) FIFO ?à CODEC
APP負責提供數據,DMA負責數據傳輸,其實就是要把數據搬運到類似I2S或者AC97間的FIFO中,I2S負責成幀傳遞數據,最后由CODEC還原。錄音則反之。
一般來講,I2S和CODEC要在DMA啟動前準備好,而啟動DMA和I2S的順序,往往會導致一些數據的丟失,這是關注的一點。
?
2)? DMA與APP? buffer之間的關系
還原處理時: APP 往往提供很大的數據包,比如16K,而DMA由于設計的需要,一般取到4K,就比較大了。
啟動DMA開始工作的條件是:
---填充完畢兩個DMA BUFFER
---I2S準備好
?
當DMA完成搬運后,會產生DMA的中斷,而在中斷處理中,要注意一下問題:
---啟動第二個BUFFER,繼續讓DMA工作,如果不能啟動就要DMA停機了。
---CPU負責檢查是否還有足夠的數據給DMA,如果有,夠填充幾個DMA BUFFER
?
DMA停機的條件是:
---沒有更多的BUFFER可供搬運
---強制停機
?
在實踐過程中,要拿捏好DMA啟動和停機的條件,因為驅動一般都是多線程運轉的,一定要注意對DMA操作時的互斥,防止出現異常或者死鎖。
?
另外,DMA BUFFER的大小,直接關系到采樣的頻率,對于一些實時應用很重要。比如:VOIP中需要20ms 的打包周期,就需要限制DMA BUFFER的大小,從而控制中斷時間,及時為VOIP提供周期打包數據。
?
3)? DMA多CHANNEL之間的關系
如果同時啟動錄音和放音CHANNEL,那么在DMA的中斷處理中要注意區分是誰的數據,并且要及時啟動各自CHANNEL的第二個BUFFER,防止采集數據丟失或者放音停頓。
視頻與圖像RGB/YUV格式詳解
計算機彩色顯示器顯示色彩的原理與彩色電視機一樣,都是采用R(Red)、G(Green)、B(Blue)相加混色的原理:通過發射出三種不同強度的電子束,使屏幕內側覆蓋的紅、綠、藍磷光材料發光而產生色彩。這種色彩的表示方法稱為RGB色彩空間表示(它也是多媒體計算機技術中用得最多的一種色彩空間表示方法)。
? 根據三基色原理,任意一種色光F都可以用不同分量的R、G、B三色相加混合而成。
F = r [ R ] + g [ G ] + b [ B ]
? 其中,r、g、b分別為三基色參與混合的系數。當三基色分量都為0(最弱)時混合為黑色光;而當三基色分量都為k(最強)時混合為白色光。調整r、g、b三個系數的值,可以混合出介于黑色光和白色光之間的各種各樣的色光。
? 那么YUV又從何而來呢?在現代彩色電視系統中,通常采用三管彩色攝像機或彩色CCD攝像機進行攝像,然后把攝得的彩色圖像信號經分色、分別放大校正后得到RGB,再經過矩陣變換電路得到亮度信號Y和兩個色差信號R-Y(即U)、B-Y(即V),最后發送端將亮度和色差三個信號分別進行編碼,用同一信道發送出去。這種色彩的表示方法就是所謂的YUV色彩空間表示。
? 采用YUV色彩空間的重要性是它的亮度信號Y和色度信號U、V是分離的。如果只有Y信號分量而沒有U、V分量,那么這樣表示的圖像就是黑白灰度圖像。彩色電視采用YUV空間正是為了用亮度信號Y解決彩色電視機與黑白電視機的兼容問題,使黑白電視機也能接收彩色電視信號。
? YUV與RGB相互轉換的公式如下(RGB取值范圍均為0-255):
Y = 0.299R + 0.587G + 0.114B
U = -0.147R - 0.289G + 0.436B
V = 0.615R - 0.515G - 0.100B
R = Y + 1.14V
G = Y - 0.39U - 0.58V
B = Y + 2.03U
? 在DirectShow中,常見的RGB格式有RGB1、RGB4、RGB8、RGB565、RGB555、RGB24、RGB32、ARGB32等;常見的YUV格式有YUY2、YUYV、YVYU、UYVY、AYUV、Y41P、Y411、Y211、IF09、IYUV、YV12、YVU9、YUV411、YUV420等。作為視頻媒體類型的輔助說明類型(Subtype),它們對應的GUID見表2.3。
表2.3 常見的RGB和YUV格式
????? GUID ????????????????????????????????????? ??格式描述
MEDIASUBTYPE_RGB1????2色,每個像素用1位表示,需要調色板
MEDIASUBTYPE_RGB4????16色,每個像素用4位表示,需要調色板
MEDIASUBTYPE_RGB8????256色,每個像素用8位表示,需要調色板
MEDIASUBTYPE_RGB565????每個像素用16位表示,RGB分量分別使用5位、6位、5位
MEDIASUBTYPE_RGB555????每個像素用16位表示,RGB分量都使用5位(剩下的1位不用)
MEDIASUBTYPE_RGB24????每個像素用24位表示,RGB分量各使用8位
MEDIASUBTYPE_RGB32????每個像素用32位表示,RGB分量各使用8位(剩下的8位不用)
MEDIASUBTYPE_ARGB32????每個像素用32位表示,RGB分量各使用8位(剩下的8位用于表示Alpha通道值)
MEDIASUBTYPE_YUY2????YUY2格式,以4:2:2方式打包
MEDIASUBTYPE_YUYV????YUYV格式(實際格式與YUY2相同)
MEDIASUBTYPE_YVYU????YVYU格式,以4:2:2方式打包
MEDIASUBTYPE_UYVY????UYVY格式,以4:2:2方式打包
MEDIASUBTYPE_AYUV????帶Alpha通道的4:4:4 YUV格式
MEDIASUBTYPE_Y41P????Y41P格式,以4:1:1方式打包
MEDIASUBTYPE_Y411????Y411格式(實際格式與Y41P相同)
MEDIASUBTYPE_Y211????Y211格式
MEDIASUBTYPE_IF09????IF09格式
MEDIASUBTYPE_IYUV????IYUV格式
MEDIASUBTYPE_YV12????YV12格式
MEDIASUBTYPE_YVU9????YVU9格式
下面分別介紹各種RGB格式。
¨ RGB1、RGB4、RGB8都是調色板類型的RGB格式,在描述這些媒體類型的格式細節時,通常會在BITMAPINFOHEADER數據結構后面跟著一個調色板(定義一系列顏色)。它們的圖像數據并不是真正的顏色值,而是當前像素顏色值在調色板中的索引。以RGB1(2色位圖)為例,比如它的調色板中定義的兩種顏色值依次為0x000000(黑色)和0xFFFFFF(白色),那么圖像數據001101010111…(每個像素用1位表示)表示對應各像素的顏色為:黑黑白白黑白黑白黑白白白…。
¨ RGB565使用16位表示一個像素,這16位中的5位用于R,6位用于G,5位用于B。程序中通常使用一個字(WORD,一個字等于兩個字節)來操作一個像素。當讀出一個像素后,這個字的各個位意義如下:
???? 高字節??????????????低字節
R R R R R G G G???? G G G B B B B B
可以組合使用屏蔽字和移位操作來得到RGB各分量的值:
#define RGB565_MASK_RED????0xF800
#define RGB565_MASK_GREEN??0x07E0
#define RGB565_MASK_BLUE?? 0x001F
R = (wPixel & RGB565_MASK_RED) >> 11;?? // 取值范圍0-31
G = (wPixel & RGB565_MASK_GREEN) >> 5;??// 取值范圍0-63
B =??wPixel & RGB565_MASK_BLUE;???????? // 取值范圍0-31
¨ RGB555是另一種16位的RGB格式,RGB分量都用5位表示(剩下的1位不用)。使用一個字讀出一個像素后,這個字的各個位意義如下:
???? 高字節???????????? 低字節
X R R R R G G?????? G G G B B B B B?????? (X表示不用,可以忽略)
可以組合使用屏蔽字和移位操作來得到RGB各分量的值:
#define RGB555_MASK_RED????0x7C00
#define RGB555_MASK_GREEN??0x03E0
#define RGB555_MASK_BLUE?? 0x001F
R = (wPixel & RGB555_MASK_RED) >> 10;?? // 取值范圍0-31
G = (wPixel & RGB555_MASK_GREEN) >> 5;??// 取值范圍0-31
B =??wPixel & RGB555_MASK_BLUE;???????? // 取值范圍0-31
¨ RGB24使用24位來表示一個像素,RGB分量都用8位表示,取值范圍為0-255。注意在內存中RGB各分量的排列順序為:BGR BGR BGR…。通常可以使用RGBTRIPLE數據結構來操作一個像素,它的定義為:
typedef struct tagRGBTRIPLE {
??BYTE rgbtBlue;????// 藍色分量
??BYTE rgbtGreen;?? // 綠色分量
??BYTE rgbtRed;???? // 紅色分量
} RGBTRIPLE;
¨ RGB32使用32位來表示一個像素,RGB分量各用去8位,剩下的8位用作Alpha通道或者不用。(ARGB32就是帶Alpha通道的RGB32。)注意在內存中RGB各分量的排列順序為:BGRA BGRA BGRA…。通常可以使用RGBQUAD數據結構來操作一個像素,它的定義為:
typedef struct tagRGBQUAD {
??BYTE????rgbBlue;??????// 藍色分量
??BYTE????rgbGreen;???? // 綠色分量
??BYTE????rgbRed;?????? // 紅色分量
??BYTE????rgbReserved;??// 保留字節(用作Alpha通道或忽略)
} RGBQUAD;
? 下面介紹各種YUV格式。YUV格式通常有兩大類:打包(packed)格式和平面(planar)格式。前者將YUV分量存放在同一個數組中,通常是幾個相鄰的像素組成一個宏像素(macro-pixel);而后者使用三個數組分開存放YUV三個分量,就像是一個三維平面一樣。表2.3中的YUY2到Y211都是打包格式,而IF09到YVU9都是平面格式。(注意:在介紹各種具體格式時,YUV各分量都會帶有下標,如Y0、U0、V0表示第一個像素的YUV分量,Y1、U1、V1表示第二個像素的YUV分量,以此類推。)
¨ YUY2(和YUYV)格式為每個像素保留Y分量,而UV分量在水平方向上每兩個像素采樣一次。一個宏像素為4個字節,實際表示2個像素。(4:2:2的意思為一個宏像素中有4個Y分量、2個U分量和2個V分量。)圖像數據中YUV分量排列順序如下:
Y0 U0 Y1 V0????Y2 U2 Y3 V2 …
¨ YVYU格式跟YUY2類似,只是圖像數據中YUV分量的排列順序有所不同:
Y0 V0 Y1 U0????Y2 V2 Y3 U2 …
¨ UYVY格式跟YUY2類似,只是圖像數據中YUV分量的排列順序有所不同:
U0 Y0 V0 Y1????U2 Y2 V2 Y3 …
¨ AYUV格式帶有一個Alpha通道,并且為每個像素都提取YUV分量,圖像數據格式如下:
A0 Y0 U0 V0????A1 Y1 U1 V1 …
¨ Y41P(和Y411)格式為每個像素保留Y分量,而UV分量在水平方向上每4個像素采樣一次。一個宏像素為12個字節,實際表示8個像素。圖像數據中YUV分量排列順序如下:
U0 Y0 V0 Y1????U4 Y2 V4 Y3????Y4 Y5 Y6 Y8 …
¨ Y211格式在水平方向上Y分量每2個像素采樣一次,而UV分量每4個像素采樣一次。一個宏像素為4個字節,實際表示4個像素。圖像數據中YUV分量排列順序如下:
Y0 U0 Y2 V0????Y4 U4 Y6 V4 …
¨ YVU9格式為每個像素都提取Y分量,而在UV分量的提取時,首先將圖像分成若干個4 x 4的宏塊,然后每個宏塊提取一個U分量和一個V分量。圖像數據存儲時,首先是整幅圖像的Y分量數組,然后就跟著U分量數組,以及V分量數組。IF09格式與YVU9類似。
¨ IYUV格式為每個像素都提取Y分量,而在UV分量的提取時,首先將圖像分成若干個2 x 2的宏塊,然后每個宏塊提取一個U分量和一個V分量。YV12格式與IYUV類似。
¨ YUV411、YUV420格式多見于DV數據中,前者用于NTSC制,后者用于PAL制。YUV411為每個像素都提取Y分量,而UV分量在水平方向上每4個像素采樣一次。YUV420并非V分量采樣為0,而是跟YUV411相比,在水平方向上提高一倍色差采樣頻率,在垂直方向上以U/V間隔的方式減小一半色差采樣