手機直播系統偶爾會需要到的:Windows 下視訊採集技術
Windows下視訊採集的方法
在 Windows 下主要有兩種方法來採集視訊: 一種是通過 Media Foundation,另一種是通過 DirectShow。
Meida Foundation 是 Windows 從 vista 之後推出的一套全新的 多媒體SDK,簡單方便,從 Win7 開始成熟起來。
另一種是 DirectShow,它主要用於 win7 之前的採集視訊。使用 DirectShow 編寫程式碼比較麻煩,主要是因為 Windows 工程師按照邏輯電路的思維方式設計了 DirectsShow 的開發介面,引入了什麼 filter, pin之類的概念。這些老掉牙的東西現在估計沒幾個人能搞明白,除非你是從那個時代過來的,哈哈。
這也解釋了為啥現在很少有人學習 Windows 程式開發了,就是因為跟不上時代。你看人家 Android/iOS做視訊採集多簡單,你整的這麼麻煩,誰還願意學!
Media Foundation的一些概念
DirectShow 方案我們放到以後再分析,今天我們主要講下 MediaFoundation 如何進行視訊採集。
在講之前,我們先要補充一些基本概念。這些概念大家可以從Media Foundation Programming Guide 找到。下面的文字基本是翻譯的 Windows 的官方文件。
MF(MediaFoundation)的整體結構圖如下:
MF 提供了兩種不同的程式設計模型。第一種是上圖的左半部分,媒體資料通過端到端的管道傳遞。Application首先初始化管道,然後呼叫相應方法控制管道中的流。第二種如上圖的右半部分,Application可以從 Source Reader拉資料,也可以向 Sink Writer 推資料。這種模型對於處理資料非常有用。
Primitives 和 Platfrom
圖底部的 Primitives 是一些輔助API:
- Attributes: 相當於一個 Map, 由 key/value 組成。
- Media Type: 描述媒體資料流的格式。
- Media Buffers: 存放一段媒體資料。
- Media Samples: 存放 Media Buffers 的容器,相當於一個 Buffter List。
MF Platform 提供了一些核心功能的API。例如非同步呼叫、工作佇列。
Media Pipeline
Media Pipeline 包括三種類型物件:Media Sources、MFTs(Media Foundation Transfors)、Media Sink。
- Media Sources: 將資料引入到管道里。資料可以來自本地檔案,網路流或都是硬體裝置。
- MFTs: 處理流資料。在 MFTs 裡實現了編解碼器。
- Media Sink: 消費資料。顯示視訊到顯示屏上,播放聲音或寫資料到媒體檔案。
Media Session 通過管道控制資料流。如質量控制,音訊/視訊同步,格式的改變。
Source Reader 和 Sink Writer
Source Reader 和 Sink Writer提供了使用 Media Foundation 的另一種方法(相較於 media source, transforms, media sink)。
- Source Reader 控制著 media source 和 多個解碼器。
- Sink Writer 控制著 media sink 和 多個編碼器。
你可以使用 Source Reader 從 media source 獲取到壓縮或未壓縮的資料,並使用 Sinker Writer 編碼資料併發送給 media sink。
下面我們就來看看 MF 是如何採集視訊資料的。
採集視訊資料
通過上面的介紹,我們基本可以知道 MF 採用 從源採集資料,編解碼,輸出渲染這種架構來處理多媒體。這種方式通俗易懂,使用起來非常方便。
MF採集視訊的基本步驟
MF採集資料使用的是架構中的第二種程式設計模型,其步驟如下:
- 初始化 COM 元件。
- 獲取視訊裝置列表。
- 啟用某個視訊裝置,獲取該裝置的 Media Source。
- 根據請求命令和 Media Source 建立 Source Reader。
- 為 Source Reader 設定 Media Type。
- 通過 Source Reader 從裝置中讀取 Media Type 格式的視訊資料。
以上就是 MF 從視訊裝置採集數所的基本步驟,下面我們來詳細介紹每一步。
詳細分析
由於每一步的程式碼都實分簡單,我這裡就不做過多的文字描述了,通過下面的程式碼及其註釋大家很容易理解其中的每一步。
初始化 COM 元件並啟動 MF
...
CoInitializeEx(NULL, COINIT_APARTMENTTHREAD | COINIT_DISABLE_OLEDDE)
MFStartup(MF_VERSION)
...
獲取所有的視訊裝置
IMFAttributes *videoCmd = NULL;
IMFActivate **videoDevices = NULL;
UINT32 videoDeviceCount = 0;
...
//設定獲取視訊裝置的命令
MFCreateAttributes(videoCmd, 1/*表示只分配一項*/);
videoCmd->setGUID(
MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE, //key
MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_GUID); //value
//獲取視訊裝置列表
MFEnumDeviceSources(
videoCmd,
&videoDevices, //這裡是裝置列表
&videoDeviceCount); //這裡存放的是裝置的個數
...
啟用某個視訊裝置
IMFMediaSource *mediaSource = NULL;
...
//啟用第一個視訊裝置,併為該設定備生成邏輯上的媒體源(Media Source)
videoDevices[0]->ActivateObject(IID_PPV_ARGS(&mediaSource));
...
建立 Source Reader
IMFSourceReader *soureReader = NULL;
...
//通過媒體源和請求命令,可以獲取source reader。(第二種開發模型)
MFCreateSourceReaderFromMediaSource(
mediaSource,
videoCmd,
&sourceReader);
...
設定 Media Type
IMFMediaType *mediaType = NULL;
...
MFCreateMediaType(&mediaType);
//設定媒體為視訊
mediaType->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video);
//YUV格式為 I420
mediaType->SetGUID(MF_MT_SUBTYPE, WMMEDIASUBTYPE_I420);
//每個視訊幀的大小為 640 * 480
MFSetAttributeSize(mediaType, MF_MT_FRAME_SIZE, 640, 480);
sourceReader->SetCurrentMediaType(MF_SOURCE_READER_FIRST_VIDEO_STREAM,
NULL,
mediaType);
讀取資料
IMFSample *sample = NULL;
DWORD index, flags;
LONGLONG llVideoTs;
...
while(runing){
sourceReader->ReadSample(
MF_SOURCE_READER_FIRST_VIDEO_STREAM,
0,
&index, //實際流的index
&flags, //staus flags
&llVideoTs, //時間戳
&sample); //存放採集到的視訊資料
}
通過上面簡單的幾步,就可以輕鬆的從視訊裝置裡取到視訊資料了。MF相對於 DirectShow真是簡單太多了。
上面介紹的是使用同步方式使用MF採集視訊資料,MF還提供了效率更高的非同步方式獲取視訊資料,有興趣的朋友可以以本篇文章為基礎去學習它的非同步方式。
小結
今天向大家介紹了在 Windows下使用 MF 如何採集視訊的方法。通過以下 6 步即可做到:
- 初始化 COM 元件。
- 獲取視訊裝置列表。
- 啟用某個視訊裝置,獲取該裝置的 Media Source。
- 根據請求命令和 Media Source 建立 Source Reader。
- 為 Source Reader 設定 Media Type。
- 通過 Source Reader 從裝置中讀取 Media Type 格式的視訊資料。
另外, MF 的採集方案只適用於 Win7 以後的系統,對於之前的系統還是要使用 DirectShow 方案。我也會在後面再為大家介紹如何使用 DirectShow 採集視訊。