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;
}