本代码可以同时开启多个高帧率摄像头并进行录制
学习起因:
由于学习需要,我买了最高帧率为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那样从底层解决问题,遂进行了自己的理解学习之路。
参考文献:
- 机器视觉学习笔记(2)——基于DirectShow的多摄像头视频采集
- 使用DirectShow采集图像
- 《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;
}