美文网首页
DShow获取虚拟摄像头

DShow获取虚拟摄像头

作者: SeatonLv | 来源:发表于2021-12-29 15:44 被阅读0次

1.公司有个需求,要获取虚拟摄像头画面实时监测,之前同事好像是用dshow创建的虚拟摄像机。有个问题 通过QT的Qcamera和opencv的VideoCapture存在问题,尤其就是在分辨率对不上的时候,拿不到画面。

2.最后看了下dshow,初学记录,参考https://www.cnblogs.com/linuxAndMcu/p/12068978.html

附带解决编译问题:https://blog.csdn.net/playstudy/article/details/6661868

感谢作者,自己在获取对应摄像机上做了些小修改
虚拟摄像头获取结果


image.png

capture.h

#pragma once

#include <Windows.h>

#include <dshow.h>

#include <string>

#include <opencv2/opencv.hpp>

using namespace std;

// 用于确保安全释放的宏

#define SAFE_RELEASE(x) { if (x) x->Release(); x = NULL; }

class CCapture

{

public:

CCapture();

~CCapture();

HRESULT Init(HWND hwnd); // 初始化

HRESULT FindCaptureDevice(); // 寻找视频采集设备

HRESULT AddToGraph(); // 将base filter添加到filter graph中

HRESULT Render(); // 渲染并预览视频

void DestroyGraph(); // 销毁先前创建的filter

void ResizeWindow(); // 重设窗口

//BOOL GrabberPic();//截图

//抓取一帧,返回的IplImage不可手动释放!

//返回图像数据的为RGB模式的Top-down(第一个字节为左上角像素),即IplImage::origin=0(IPL_ORIGIN_TL)

//IplImage * QueryFrame();

private:

// 窗口句柄

HWND    m_hwnd;

// 视频采集预览相关

IGraphBuilder   *m_pGraph; // filter granph(manager)

ICaptureGraphBuilder2   *m_pCapture; // capture granph

IMediaControl   *m_pMediaC; // 媒体控制接口

IMediaEventEx   *m_pMediaE; // 媒体事件接口

IVideoWindow    *m_pVideoW; // 视频窗口接口

IBaseFilter *m_pFilter; // 基类filter

IBasicVideo *m_pBasicVideo;

};

capture.cpp

#include "capture.h"

CCapture::CCapture()

{

}

CCapture::~CCapture()

{

}

// 初始化

HRESULT CCapture::Init(HWND hwnd)

{

HRESULT hr;

// 创建filter graph

hr = CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC, IID_IGraphBuilder, (void **)&m_pGraph);

if (FAILED(hr))

return hr;

// 创建capture granph

hr = CoCreateInstance(CLSID_CaptureGraphBuilder2, NULL, CLSCTX_INPROC,IID_ICaptureGraphBuilder2, (void **)&m_pCapture);

if (FAILED(hr))

return hr;

// 查询graph中各IID参数标识的接口指针

hr = m_pGraph->QueryInterface(IID_IMediaControl, (LPVOID *)&m_pMediaC);

if (FAILED(hr))

return hr;

hr = m_pGraph->QueryInterface(IID_IMediaEventEx, (LPVOID *)&m_pMediaE);

if (FAILED(hr))

return hr;

hr = m_pGraph->QueryInterface(IID_IVideoWindow, (LPVOID *)&m_pVideoW);

if (FAILED(hr))

return hr;

// 为capture graph指定要使用的filter graph

hr = m_pCapture->SetFiltergraph(m_pGraph);

if (FAILED(hr))

return hr;

// 获得窗口句柄

m_hwnd = hwnd;

return hr;

}

// 寻找视频采集设备

HRESULT CCapture::FindCaptureDevice()

{

HRESULT hr = S_OK;

ICreateDevEnum *pDevEnum = NULL;

IEnumMoniker *pClassEnum = NULL; // 用于视频采集设备的枚举

IMoniker* pMoniker = NULL; // 设备Moniker号

// 创建系统设备枚举

hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC,IID_ICreateDevEnum, (void **)&pDevEnum);

if (FAILED(hr))

return hr;

// 创建一个指定视频采集设备的枚举

hr = pDevEnum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory, &pClassEnum, 0);

if (FAILED(hr) || pClassEnum == NULL)

{

SAFE_RELEASE(pDevEnum);

return hr;

}

#pragma region 找到想要的摄像机

while (pClassEnum->Next(1, &pMoniker, NULL) == S_OK)

{

IPropertyBag *pPropBag;

hr = pMoniker->BindToStorage(0, 0, IID_IPropertyBag,

(void**)(&pPropBag));

if (FAILED(hr)) {

pMoniker->Release();

continue;  // Skip this one, maybe the next one will work.

}

// Find the description or friendly name.

VARIANT varName;

VariantInit(&varName);

hr = pPropBag->Read(L"Description", &varName, 0);

if (FAILED(hr)) hr = pPropBag->Read(L"FriendlyName", &varName, 0);

if (SUCCEEDED(hr))

{

hr = pPropBag->Read(L"FriendlyName", &varName, 0);

int count = 0;

char tmp[255] = { 0 };

//int maxLen = sizeof(deviceNames[0]) / sizeof(deviceNames[0][0]) - 2;

while (varName.bstrVal[count] != 0x00 && count < 255)

{

tmp[count] = (char)varName.bstrVal[count];

count++;

}

string cameName(tmp);

//RazerCAM_DM  DM_AiliveCAM

if (cameName == "RazerCAM_DM")

{

break;

}

//deviceNames[deviceCounter][count] = 0;

//if (!silent) DebugPrintOut("SETUP: %i) %s\n", deviceCounter, deviceNames[deviceCounter]);

}

pPropBag->Release();

pPropBag = NULL;

}

#pragma endregion

// 使用第一个找到的视频采集设备(只适用于单摄像头的情况)

//hr = pClassEnum->Next(1, &pMoniker, NULL);

if (hr == S_FALSE)

{

SAFE_RELEASE(pDevEnum);

SAFE_RELEASE(pClassEnum);

return hr;

}

// 绑定找到摄像头的moniker到filter graph

hr = pMoniker->BindToObject(0, 0, IID_IBaseFilter, (void**)&m_pFilter);

if (FAILED(hr))

{

SAFE_RELEASE(pDevEnum);

SAFE_RELEASE(pClassEnum);

SAFE_RELEASE(pMoniker);

return hr;

}

// 增加filter graph的引用计数

m_pFilter->AddRef();

IAMStreamConfig  *pSC = NULL;

VIDEOINFOHEADER  *vinfo = 0;

hr = m_pCapture->FindInterface(&PIN_CATEGORY_CAPTURE,

&MEDIATYPE_Video, m_pFilter, IID_IAMStreamConfig, (void **)&pSC);

if (FAILED(hr))

{

return hr;

}

AM_MEDIA_TYPE * mmt = NULL;

pSC->GetFormat(&mmt);    //取得默认参数

VIDEOINFOHEADER * pvih = (VIDEOINFOHEADER*)mmt->pbFormat;

pvih->bmiHeader.biHeight = 1920;  //修改采集视频的高为240

pvih->bmiHeader.biWidth = 1080;  //修改采集视频的宽为320

mmt->pbFormat = (unsigned char *)pvih;

pSC->SetFormat(mmt);  //重新设置参数

return hr;

}

// 将base filter添加到filter graph中

HRESULT CCapture::AddToGraph()

{

HRESULT hr = m_pGraph->AddFilter(m_pFilter, L"Video capture");

if (FAILED(hr))

{

m_pFilter->Release();

return hr;

}

return hr;

}

// 渲染并预览视频

HRESULT CCapture::Render()

{

HRESULT hr;

// 用ICaptureGraphBuilder2接口构建预览的filter链路

hr = m_pCapture->RenderStream(&PIN_CATEGORY_PREVIEW, &MEDIATYPE_Video, m_pFilter, NULL, NULL);

if (FAILED(hr))

{

m_pFilter->Release();

return hr;

}

// 同时构建一个写文件的filter链路

IBaseFilter *pMux;

hr = m_pCapture->SetOutputFileName(&MEDIASUBTYPE_Avi, L"D:\\example.avi", &pMux, NULL); // 设置输出视频文件位置

hr = m_pCapture->RenderStream(&PIN_CATEGORY_CAPTURE, &MEDIATYPE_Video, m_pFilter, NULL, pMux); // 将m_pFilter的输出pin连接到pMux

//GrabberPic();

// 使用完就可以释放base filter了

pMux->Release();

m_pFilter->Release();

// 显示窗口 , 预览采集图形

hr = m_pVideoW->put_Owner((OAHWND)m_hwnd);

if (FAILED(hr))

return hr;

hr = m_pVideoW->put_WindowStyle(WS_CHILD | WS_CLIPCHILDREN);

if (FAILED(hr))

return hr;

ResizeWindow(); // 重设窗口

hr = m_pVideoW->put_Visible(OATRUE);

if (FAILED(hr))

return hr;

hr = m_pMediaC->Run();

return hr;

}

// 销毁先前创建的filter

void CCapture::DestroyGraph()

{

if (m_pMediaC)

m_pMediaC->StopWhenReady();

if (m_pVideoW)

{

m_pVideoW->put_Visible(OAFALSE);

m_pVideoW->put_Owner(NULL);

}

// 确保接口都安全释放了

SAFE_RELEASE(m_pMediaC);

SAFE_RELEASE(m_pMediaE);

SAFE_RELEASE(m_pVideoW);

SAFE_RELEASE(m_pGraph);

SAFE_RELEASE(m_pCapture);

}

// 重设窗口

void CCapture::ResizeWindow()

{

RECT rc;

if (m_pVideoW)

{

GetClientRect(m_hwnd, &rc);

m_pVideoW->SetWindowPosition(0, 0, rc.right, rc.bottom);

}

}

//

//BOOL CCapture::GrabberPic()

//{

//

//  if (m_pGraph == NULL)

//  return FALSE;

//  else

//  m_pGraph->QueryInterface(IID_IBasicVideo, (void **)&m_pBasicVideo);

//

//  HRESULT hr;

//

//  if (m_pBasicVideo == NULL)

//  return FALSE;

//  long bitmapSize = 0;

//  BOOL bResult = FALSE;

//  hr = m_pBasicVideo->GetCurrentImage(&bitmapSize, 0);

//  if (SUCCEEDED(hr))

//  {

//  unsigned char * buffer = new unsigned char[bitmapSize];

//

//  hr = m_pBasicVideo->GetCurrentImage(&bitmapSize, (long *)buffer);

//  if (SUCCEEDED(hr))

//  {

//  BITMAPFILEHEADER hdr;

//  LPBITMAPINFOHEADER lpbi;

//  lpbi = (LPBITMAPINFOHEADER)buffer;

//  int nColors = 0;

//  if (lpbi->biBitCount <= 8)

//  nColors = 1 << lpbi->biBitCount;

//  hdr.bfType = ((WORD)('M' << 8) | 'B'); //always is "BM"

//  hdr.bfSize = bitmapSize + sizeof(hdr);

//  hdr.bfReserved1 = 0;

//  hdr.bfReserved2 = 0;

//  hdr.bfOffBits = (DWORD)(sizeof(BITMAPFILEHEADER) + lpbi->biSize +

//  nColors * sizeof(RGBQUAD));

//

//  // save DIB file as Bitmap.

//  // This sample saves image as bitmap to help

//  // understanding the sample.

//  HANDLE fh;

//  BITMAPFILEHEADER bmphdr;

//  BITMAPINFOHEADER bmpinfo;

//  DWORD nWritten;

//  memset(&bmphdr, 0, sizeof(bmphdr));

//  memset(&bmpinfo, 0, sizeof(bmpinfo));

//  bmphdr.bfType = ('M' << 8) | 'B';

//  bmphdr.bfSize = sizeof(bmphdr) + sizeof(bmpinfo) + bitmapSize;

//  bmphdr.bfOffBits = sizeof(bmphdr) + sizeof(bmpinfo);

//  bmpinfo.biSize = sizeof(bmpinfo);

//  bmpinfo.biWidth = 1080;

//  bmpinfo.biHeight = 1920;

//  bmpinfo.biPlanes = 1;

//  bmpinfo.biBitCount = 32;

//

//  fh = CreateFile(L"D:\\result.bmp",

//  GENERIC_WRITE, 0, NULL,

//  CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);

//  WriteFile(fh, &bmphdr, sizeof(bmphdr), &nWritten, NULL);

//  WriteFile(fh, &bmpinfo, sizeof(bmpinfo), &nWritten, NULL);

//  WriteFile(fh, buffer, bitmapSize, &nWritten, NULL);

//  CloseHandle(fh);

//

//

//  bResult = TRUE;

//  }

//  bResult = FALSE;

//  delete[] buffer;

//  return bResult;

//

//  }

//  if (m_pBasicVideo != NULL)

//  {

//  m_pBasicVideo->Release();

//  m_pBasicVideo = NULL;

//  }

//

//

//

//  return bResult;

//}

main

#include <Windows.h>

#include "capture.h"

// 窗口类名称

#define WIN_CLASS_NAME  TEXT("VideoCapture\0")

// 窗口名称

#define WIN_NAME    TEXT("Video capture Preview\0")

// 窗口宽和高

#define WIN_WIDTH   1080

#define WIN_HEIGHT  1920

LRESULT CALLBACK WndMainProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)

{

switch (message)

{

case WM_SIZE:

//capture.ResizeWindow();

break;

case WM_WINDOWPOSCHANGED:

break;

case WM_CLOSE:

ShowWindow(hwnd, SW_HIDE); // 在屏幕中显示窗口

break;

case WM_DESTROY:

PostQuitMessage(0); // 将"退出"消息插入消息队列

return 0;

}

return DefWindowProc(hwnd, message, wParam, lParam); // 执行默认的消息处理

}

int PASCAL WinMain(HINSTANCE hInstance, HINSTANCE hInstP, PSTR szCmdLine, int nCmdShow)

{

HWND hwnd = 0;

CCapture capture;

HRESULT hr = S_OK;

WNDCLASS wc;

// 1、初始化COM库

hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);

if (FALSE(hr))

return 1;

// 注册窗口类

ZeroMemory(&wc, sizeof(wc));

wc.lpfnWndProc = WndMainProc;

wc.hInstance = hInstance;

wc.lpszClassName = WIN_CLASS_NAME;

wc.lpszMenuName = NULL;

wc.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);

wc.hCursor = LoadCursor(NULL, IDC_ARROW);

wc.hIcon = NULL;

if (!RegisterClass(&wc)) // 为应用程序的窗口注册一个窗口类

{

CoUninitialize();

return 1;

}

// 基于窗口类创建一个窗口

hwnd = CreateWindow(WIN_CLASS_NAME, WIN_NAME,

WS_OVERLAPPEDWINDOW | WS_CAPTION | WS_CLIPCHILDREN,

CW_USEDEFAULT, CW_USEDEFAULT, WIN_WIDTH, WIN_HEIGHT, 0, 0, hInstance, 0);

if (hwnd)

{

// 2、创建各filter Graph

capture.Init(hwnd);

// 3、找到视频采集设备

capture.FindCaptureDevice();

// 4、将基类filter添加到filter granph Manager中

capture.AddToGraph();

// 5、渲染并预览视频

capture.Render();

ShowWindow(hwnd, SW_SHOW); // 在屏幕中显示窗口

// 从消息队列获取消息

MSG msg = { 0 };

while (GetMessage(&msg, NULL, 0, 0))

{

TranslateMessage(&msg); // 翻译一些键盘消息

DispatchMessage(&msg); // 将消息发送给窗口过程

}

}

// 6、销毁先前创建的filter Graph

capture.DestroyGraph();

// 7、释放COM

CoUninitialize();

return 0;

}


相关文章

网友评论

      本文标题:DShow获取虚拟摄像头

      本文链接:https://www.haomeiwen.com/subject/hcepqrtx.html