USBカメラを静止画で取得する(サンプルグラバ)
DirectXのテクスチャにDirectShowを出力する方法を試してみた。
最初に、
を参考にしたのだが、
strmbase.lib(strmbasd.lib)をVC2010でビルドするのに失敗して
コンパイルを通すことがそもそもできなかったorz
仕方ないのでDirectShow基本ライブラリに含まれるCBaseVideoRendererを使うのはおいておいて、SampleGrabberを使う手法をやってみた。
SampleGrabberを使う
こちらを参考にした。
qedit.hが無い
VC2010とは別にインストールした古いWindowsSDKに含まれていた
C:\Program Files\Microsoft SDKs\Windows\v6.1\Include\qedit.h
昔のDirectX(DirectShowが含まれているやつ)でもいいらしい
dxtrans.hが無い
C:\Program Files\Microsoft SDKs\Windows\v6.1\Include\qedit.h
を直接修正するらしい
ようやく実装開始
DirectShowは含まれるライブラリが転々としているせいで、どこにインクルードやリンクがあるのか探すので苦労する・・・
入力と出力を持つフィルタを作って、これらをグラフに追加して連結する仕組みだとわかった。
ということで、出力を持つUSBカメラフィルタと入力をもつサンプルグラバを連結した。
ComMovie.h
#pragma once #include <atlbase.h> #include <dshow.h> #include <qedit.h> #pragma comment(lib, "strmiids") #include <vector> class Com { public: Com() { CoInitialize(NULL); } ~Com() { CoUninitialize(); } }; class ComMovieSource { Com m_com; CComPtr<IGraphBuilder> m_graphBuilder; CComQIPtr<IMediaControl> m_mediaControl; CComPtr<ICaptureGraphBuilder2> m_captureGraphBulider; // 出力 CComPtr<IBaseFilter> m_sampleGrabberFilter; CComQIPtr<ISampleGrabber> m_sampleGrabber; // 入力 CComPtr<IBaseFilter> m_deviceFilter; int m_w; int m_h; std::vector<BYTE> m_buffer; public: ComMovieSource(); ~ComMovieSource(); HRESULT Initialize(); int GetWidth(){ return m_w; } int GetHeight(){ return m_h; } HRESULT Update(); void *GetSample(){ return m_buffer.empty() ? 0 : &m_buffer[0]; } size_t GetSampleSize(){ return m_buffer.size(); } };
ComMovie.cpp
#include "ComMovie.h" ComMovieSource::ComMovieSource() : m_w(0), m_h(0) { } ComMovieSource::~ComMovieSource() { } HRESULT ComMovieSource::Initialize() { if(m_graphBuilder){ return S_OK; } // filter graph if (FAILED(m_graphBuilder.CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC))){ return E_FAIL; } // add sample grabber if(FAILED(m_sampleGrabberFilter.CoCreateInstance(CLSID_SampleGrabber, NULL, CLSCTX_INPROC))){ return E_FAIL; } m_sampleGrabberFilter.QueryInterface(&m_sampleGrabber); if(!m_sampleGrabber){ return E_FAIL; } AM_MEDIA_TYPE amt; ZeroMemory(&amt, sizeof(amt)); amt.majortype = MEDIATYPE_Video; amt.subtype = MEDIASUBTYPE_RGB32; amt.formattype = FORMAT_VideoInfo; m_sampleGrabber->SetMediaType(&amt); if(FAILED(m_graphBuilder->AddFilter(m_sampleGrabberFilter, L"Sample Grabber"))){ return E_FAIL; } // add capture filter if(FAILED(m_captureGraphBulider.CoCreateInstance(CLSID_CaptureGraphBuilder2, NULL, CLSCTX_INPROC))){ return E_FAIL; } if(FAILED(m_captureGraphBulider->SetFiltergraph(m_graphBuilder))){ return E_FAIL; } // デバイスを列挙するためのCreateDevEnumを生成 CComPtr<ICreateDevEnum> createDevEnum; if(FAILED(createDevEnum.CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC_SERVER))){ return E_FAIL; } CComPtr<IEnumMoniker> enumMoniker; createDevEnum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory, &enumMoniker, 0); if(!enumMoniker){ // no device return E_FAIL; } enumMoniker->Reset(); while(true){ CComPtr<IMoniker> moniker ; DWORD nFetched; if(FAILED(enumMoniker->Next(1, &moniker, &nFetched))){ return E_FAIL; } if(nFetched<1){ break; } // ToDo: 条件に合うデバイスをバインドする { // 一個目のUSBカメラ moniker->BindToObject(0, 0, IID_IBaseFilter, (void**)&m_deviceFilter); break; } } if(FAILED(m_graphBuilder->AddFilter(m_deviceFilter, L"Video Input Device"))){ return E_FAIL; } // デバイスとグラバフィルタを連結する if(FAILED(m_captureGraphBulider->RenderStream(&PIN_CATEGORY_PREVIEW, NULL, m_deviceFilter, NULL, m_sampleGrabberFilter))){ return E_FAIL; } // 画像サイズ if(FAILED(m_sampleGrabber->GetConnectedMediaType(&amt))){ return E_FAIL; } VIDEOINFOHEADER *pVideoInfoHeader = (VIDEOINFOHEADER *)amt.pbFormat; m_w=pVideoInfoHeader->bmiHeader.biWidth; m_h=pVideoInfoHeader->bmiHeader.biHeight; m_buffer.resize(amt.lSampleSize); // 開始 m_sampleGrabber->SetBufferSamples(TRUE); m_graphBuilder.QueryInterface(&m_mediaControl); if(!m_mediaControl){ return E_FAIL; } if(FAILED(m_mediaControl->Run())){ return E_FAIL; } return S_OK; } HRESULT ComMovieSource::Update() { if(m_buffer.empty()){ return E_FAIL; } // 現在表示されている映像を静止画として取得 long nBufferSize=m_buffer.size(); HRESULT hr=m_sampleGrabber->GetCurrentBuffer(&nBufferSize, (long*)&m_buffer[0]); if(SUCCEEDED(hr) && nBufferSize==m_buffer.size()){ return S_OK; } return E_FAIL; }