前言
在Windows上采集摄像头的数据的方法有几种,vfw、directshow、mf。vfw过于老旧,directshow使用比较复杂,mf就是今天要讲的Media Foundation,其使用方法相对容易些,但是官方的例子略微复杂,对于想要实现简单的摄像头数据采集,并不需要那么多个对象及方法耦合在一起变成一个错综复杂的流程。本文将提供Media Foundation最简单的摄像头采集实现,在此基础上根据自己的需求慢慢添加功能显然是比改造一堆复杂的代码要容易的。
一、头文件
最简单的例子只需要3个头文件
#include <mfapi.h>
#include <mfidl.h>
#include <mfreadwrite.h>
lib需要如下几个
#pragma comment(lib, "Mfplat.lib")
#pragma comment(lib, "Mf.lib")
#pragma comment(lib, "mfreadwrite.lib")
#pragma comment(lib, "mfuuid.lib")
二、MF对象
主要对象如下3个
//摄像头设备对象
IMFActivate** devices = NULL;
//底层媒体源对象
IMFMediaSource* source = NULL;
//上层数据读取对象
IMFSourceReader* reader = NULL;
三、示例
#include<exception>
#include <Windows.h>
#include <mfapi.h>
#include <mfidl.h>
#include <mfreadwrite.h>
#pragma comment(lib, "Mfplat.lib")
#pragma comment(lib, "Mf.lib")
#pragma comment(lib, "mfreadwrite.lib")
#pragma comment(lib, "mfuuid.lib")
int main(int argc, char* argv[])
{
CoInitialize(NULL);
IMFAttributes* attributes = NULL;
IMFMediaSource* source = NULL;
IMFSourceReader* reader = NULL;
IMFActivate** devices = NULL;
IMFMediaType* mediaType = NULL;
UINT32 width;
UINT32 height;
GUID subtype;
UINT32 count = 0;
DWORD index = 0;
DWORD flag = 0;
LONGLONG timestamp = 0;
bool exitFlag = false;
//枚举设备
auto hr = MFCreateAttributes(&attributes, 1);
if (FAILED(hr))
{
throw std::exception("create attributes error!");
}
hr = attributes->SetGUID(MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE, MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_GUID);
if (FAILED(hr))
{
throw std::exception("set guid error!");
}
hr = MFEnumDeviceSources(attributes, &devices, &count);
if (FAILED(hr))
{
throw std::exception("enum device error!");
}
//枚举设备-end
if (count < 1)
{
throw std::exception("can not find any video device!");
}
//根据第一个设备创建MediaSource
hr = MFCreateDeviceSource(devices[0], &source);
if (FAILED(hr))
{
throw std::exception("create device source error!");
}
//创建SourceReader
hr = MFCreateSourceReaderFromMediaSource(source, NULL, &reader);
if (FAILED(hr))
{
throw std::exception("create source reader error!");
}
//获取当前默认视频格式(作为最简单例子不展示设置视频格式)
hr = reader->GetCurrentMediaType(MF_SOURCE_READER_FIRST_VIDEO_STREAM, &mediaType);
if (FAILED(hr))
{
throw std::exception("get media type error!");
}
//获取图像格式
hr = mediaType->GetGUID(MF_MT_SUBTYPE, &subtype);
if (FAILED(hr))
{
throw std::exception("get subtype error!");
}
//获取分辨率
hr = MFGetAttributeSize(mediaType, MF_MT_FRAME_SIZE, &width, &height);
if (FAILED(hr))
{
throw std::exception("get frame size error!");
}
//开始采集(同步)
while (!exitFlag)
{
IMFSample* sample;
hr = reader->ReadSample(MF_SOURCE_READER_FIRST_VIDEO_STREAM, 0, &index, &flag, ×tamp, &sample);
if (FAILED(hr))
{
throw std::exception("read sample error!");
}
if (sample)
{
DWORD count = 0;
BYTE* data;
DWORD len;
IMFMediaBuffer* buffer = NULL;
sample->GetBufferCount(&count);
for (int i = 0; i < count; i++)
{
sample->GetBufferByIndex(i, &buffer);
if (buffer)
{
hr = buffer->Lock(&data, NULL, &len);
if (FAILED(hr))
{
throw std::exception("buffer lock error!");
}
//此处获取到图像数据,判断subtype后对数据进行处理
if (IsEqualGUID(subtype, MFVideoFormat_I420))
{
//显示(data,len)
}
else if (IsEqualGUID(subtype, MFVideoFormat_YUY2))
{
//显示(data,len)
}
hr = buffer->Unlock();
if (FAILED(hr))
{
throw std::exception("buffer unlock error!");
}
buffer->Release();
}
}
sample->Release();
}
}
//解除引用
attributes->Release();
source->Release();
reader->Release();
mediaType->Release();
for (int i = 0; i < count; i++)
{
devices[i]->Release();
}
return 0;
}
总结
以上就是今天要讲的内容,之所以发这篇文章,主要是因为笔者自己实现此功能的时候,查找的示例代码都比较复杂,一度以为directshow可能会好用些。经过分析理清示例代码后才发现mf确实要比directshow简单很多,对代码精简后就变得比较好理解了,流程也很清晰了。