Directshow获取高帧率无驱摄像头

本代码可以同时开启多个高帧率摄像头并进行录制

学习起因:

        由于学习需要,我买了最高帧率为240的摄像头,最开始使用opencv进行调用,发现opencv中的帧率设置后获得的帧率仍只有30fps,经调查后发现,opencv目前似乎并不支持这种帧率的设置。我买到的无驱usb摄像头是可以用amcap直接打开的,amcap是微软使用Directshow写的小程序,由于可能需要在获得摄像头接口后直接进行视频的处理,于是开始学习Directshow,这是一种很早的技术,如无需要,尽量不要花太多时间学习。

       本代码在参考文献【2】的基础上结合对Directshow的学习理解写成,仅此一段即可运行。参考文献【2】是用Directshow写的工具函数,其中的代码写的是很好的,参考文献【1】就是对【2】使用的实例,之所以我没有直接使用文献【2】提供的函数,是因为发现将其中QueryFrame()函数像【1】那样使用时,获取的每帧的时间间隔并不稳定,即虽然帧率是240fps,但是获取的帧是忽快忽慢的,这达不到我的需求,而amcap在使用中每帧的时间间隔则很稳定,分析后发现【2】中提供的函数QueryFrame()使用的是Directshow的获取一张的图像编写的,这种获取的稳定程度取决于电脑的运行状况,而无法像Direcshow那样从底层解决问题,遂进行了自己的理解学习之路。

参考文献: 

  1. 机器视觉学习笔记(2)——基于DirectShow的多摄像头视频采集
  2. 使用DirectShow采集图像
  3. 《Directshow开发指南》——陆其明

代码运行环境:

VS2013 ,需对工程属性进行设置,VC++目录的引用目录和库目录中添加Directshow的相关文件夹,链接器的输入中添加附加依赖项如图

  

代码: 

#include<tchar.h>
#include <strsafe.h>
#include <windows.h>
#include <dshow.h>
#include <dbt.h>
#include <mmreg.h>
#include <msacm.h>
#include <fcntl.h>
#include <io.h>
#include <stdio.h>
#include <commdlg.h>
#include <atlbase.h>
#include<atlconv.h>
#include<iostream>
#include <math.h>
#include<cstdlib>
#include <sstream>
using namespace std;
#define MYFREEMEDIATYPE(mt)	{if ((mt).cbFormat != 0)		\
{CoTaskMemFree((PVOID)(mt).pbFormat);	\
	(mt).cbFormat = 0;						\
	(mt).pbFormat = NULL;					\
}											\
if ((mt).pUnk != NULL)						\
{											\
	(mt).pUnk->Release();					\
	(mt).pUnk = NULL;						\
}}


class CCameraDS
{
private:

	IGraphBuilder *pGraph;
	IMediaControl *pControl;
	IMediaEvent   *pEvent;
	IVideoWindow *pVideoWindow;
	ICaptureGraphBuilder2 *pBuild;
	IBaseFilter *pCap;
	IBaseFilter *pMux;
	IAMStreamConfig *pConfig;
	HRESULT hr;
	TCHAR FileName;
	string name;


public:

	CCameraDS(){

		pGraph = NULL;
		pControl = NULL;
		pEvent = NULL;
		pVideoWindow = NULL;
		pBuild = NULL;
		pCap = NULL;
		pConfig = NULL;

		CoInitialize(NULL);
	};

	virtual ~CCameraDS(){
		CloseCamera();
		CoUninitialize();
	};

	//打开摄像头,nCamID指定打开哪个摄像头,取值可以为0,1,2,...
	void OpenCamera(int nCamID, int nWidth, int nHeight){

		//创建Capture Graph Builder 对象
		hr = CoCreateInstance(CLSID_CaptureGraphBuilder2, NULL,
			CLSCTX_INPROC_SERVER, IID_ICaptureGraphBuilder2, (void**)&pBuild);
		if (SUCCEEDED(hr)){
			// 创建 graph manger 对象.
			hr = CoCreateInstance(CLSID_FilterGraph, 0, CLSCTX_INPROC_SERVER,
				IID_IGraphBuilder, (void**)&pGraph);
			if (SUCCEEDED(hr))
				// 初始化 Capture Graph Builder.
				pBuild->SetFiltergraph(pGraph);
		}
		//各个查询接口.
		hr = pGraph->QueryInterface(IID_IMediaControl, (void **)&pControl);
		hr = pGraph->QueryInterface(IID_IMediaEvent, (void **)&pEvent);
		hr = pGraph->QueryInterface(IID_IVideoWindow, (void**)&pVideoWindow);

		bool Flag = BindFilter(nCamID, &pCap);
		pGraph->AddFilter(pCap, NULL);// 将pCap加入到filter graph


	
		if (1)
		{
			

			hr = pBuild->FindInterface(&PIN_CATEGORY_CAPTURE, 0, pCap, IID_IAMStreamConfig, (void**)&pConfig);
			AM_MEDIA_TYPE *pmt;
			if (pConfig->GetFormat(&pmt) != S_OK)
			{
				printf("GetFormat Failed ! \n");
				return ;
			}
			
			if ((pmt->lSampleSize != (nWidth * nHeight * 3)) && (pmt->formattype == FORMAT_VideoInfo))
			{
				
				VIDEOINFOHEADER *phead = (VIDEOINFOHEADER*)(pmt->pbFormat);
				phead->bmiHeader.biWidth = nWidth;
				phead->bmiHeader.biHeight = nHeight;
				if ((hr = pConfig->SetFormat(pmt)) != S_OK)
				{
					return ;
				}
			}

			pConfig->Release();
			pConfig = NULL;
			MYFREEMEDIATYPE(*pmt);
		}
		hr = pBuild->SetOutputFileName(
			&MEDIASUBTYPE_Avi,  // Specifies AVI for the target file.
			MyFileName(nCamID), // File name.
			&pMux,              // Receives a pointer to the mux.
			NULL);              // (Optional) Receives a pointer to the file sink.
		// Render the stream.
		
		hr = pBuild->RenderStream(&PIN_CATEGORY_CAPTURE, &MEDIATYPE_Video,
			pCap, NULL, pMux);
		//hr = pGraph->AddFilter(pCap, L"Capture Filter");//将filter加入到graph
		hr = pBuild->RenderStream(&PIN_CATEGORY_PREVIEW, &MEDIATYPE_Video, pCap, NULL, NULL);//连接graph
	
	};

	wchar_t *MyFileName(int nCamID){

		string b = to_string(nCamID);
		string c = ".avi";
		string d = name + b + c;
		wchar_t * wc = new wchar_t[d.size()];

		swprintf(wc, 100, L"%S", d.c_str()); //注意大写
		return wc;

	}

	void SetFileName(string filename)
	{
		name = filename;
	}
	//关闭摄像头,析构函数会自动调用这个函数

	void CloseCamera(){

		/*pCap->Release();
		pBuild->Release();
		pControl->Release();
		pEvent->Release();
		pGraph->Release();
		pVideoWindow->Release();
		pConfig->Release();
		CoUninitialize();
		pMux->Release();*/

	};

	void ShowCamera(){
		pVideoWindow->put_AutoShow(OATRUE);//graph自动显示视频窗口 
		hr = pControl->Run();

	}

	void StopShow(){
		hr = pControl->Stop();
	}

	bool BindFilter(int nCamID, IBaseFilter **pFilter)
	{
		if (nCamID < 0)
		{
			return false;
		}

		// enumerate all video capture devices
		CComPtr<ICreateDevEnum> pCreateDevEnum;
		HRESULT hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC_SERVER, IID_ICreateDevEnum, (void**)&pCreateDevEnum);
		if (hr != NOERROR)
		{
			return false;
		}

		CComPtr<IEnumMoniker> pEm;
		hr = pCreateDevEnum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory, &pEm, 0);
		if (hr != NOERROR)
		{
			return false;
		}

		pEm->Reset();
		ULONG cFetched;
		IMoniker *pM;
		int index = 0;
		while (hr = pEm->Next(1, &pM, &cFetched), hr == S_OK, index <= nCamID)
		{
			IPropertyBag *pBag;
			hr = pM->BindToStorage(0, 0, IID_IPropertyBag, (void **)&pBag);
			if (SUCCEEDED(hr))
			{
				VARIANT var;
				var.vt = VT_BSTR;
				hr = pBag->Read(L"FriendlyName", &var, NULL);
				if (hr == NOERROR)
				{
					if (index == nCamID)
					{
						pM->BindToObject(0, 0, IID_IBaseFilter, (void**)pFilter);
					}
					SysFreeString(var.bstrVal);
				}
				pBag->Release();
			}
			pM->Release();
			index++;
		}

		pCreateDevEnum = NULL;
		return true;
	}
};

int main()
{

	CCameraDS camera1, camera2;
	camera1.SetFileName("C:\\Users\\DELL\\Desktop\\flash");
	//camera2.SetFileName("C:\\Users\\DELL\\Desktop\\flash");
	camera1.OpenCamera(0, 1280, 800);
	//camera2.OpenCamera(1,1280,800);
	camera1.ShowCamera();
	//camera2.ShowCamera();
	Sleep(5 * 1000);//延时x秒 
	camera1.StopShow();
	//camera2.StopShow();



	return 0;
}

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值