Windows使用Media Foundation采集摄像头数据


前言

在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, &timestamp, &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简单很多,对代码精简后就变得比较好理解了,流程也很清晰了。

评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

CodeOfCC

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值
>