次の方法で共有


静止画像ピンから画像をキャプチャする

[このページに関連付けられている機能 DirectShow は、従来の機能です。 MediaPlayer、IMFMediaEngine、Media Foundation のオーディオ/ビデオ キャプチャに置き換わりました。 これらの機能は、Windows 10とWindows 11用に最適化されています。 新しいコードでは、可能であれば、DirectShow ではなく Media Foundation で MediaPlayerIMFMediaEngineAudio/Video Capture を使用することを強くお勧めします。 Microsoft は、レガシ API を使用する既存のコードを、可能であれば新しい API を使用するように書き換えるよう提案しています。]

一部のカメラでは、キャプチャ ストリームとは別の静止画像を生成できます。多くの場合、静止画像はキャプチャ ストリームによって生成される画像よりも高品質です。 カメラには、ハードウェア トリガーとして機能するボタンがある場合や、ソフトウェアトリガーをサポートしている場合があります。 静止画像をサポートするカメラは、静止画像ピンを公開します。これはピン カテゴリ PIN_CATEGORY_STILLです。

デバイスから静止画像を取得する推奨される方法は、Windows イメージ取得 (WIA) API を使用することです。 詳細については、プラットフォーム SDK のドキュメントの「Windows イメージの取得」を参照してください。 ただし、DirectShow を使用してイメージをキャプチャすることもできます。

引き続きピンをトリガーするには、次のように、グラフの実行中に IAMVideoControl::SetMode メソッドを使用します。

IAMVideoControl *pAMVidControl = NULL;

hr = pControl->Run(); // Run the graph.
if (FAILED(hr))
{
    // Handle error.
}

hr = pCap->QueryInterface(IID_IAMVideoControl, (void**)&pAMVidControl);

if (SUCCEEDED(hr))
{
    // Find the still pin.
    IPin *pPin = NULL;

    // pBuild is an ICaptureGraphBuilder2 pointer.

    hr = pBuild->FindPin(
        pCap,                  // Filter.
        PINDIR_OUTPUT,         // Look for an output pin.
        &PIN_CATEGORY_STILL,   // Pin category.
        NULL,                  // Media type (don't care).
        FALSE,                 // Pin must be unconnected.
        0,                     // Get the 0'th pin.
        &pPin                  // Receives a pointer to thepin.
        );

    if (SUCCEEDED(hr))
    {
        hr = pAMVidControl->SetMode(pPin, VideoControlFlag_Trigger);
        pPin->Release();
    }
    pAMVidControl->Release();
}

IAMVideoControl のキャプチャ フィルターに対してクエリを実行します。 インターフェイスがサポートされている場合は、前の例に示すように ICaptureGraphBuilder2::FindPin メソッドを呼び出して、引き続きピンの IPin インターフェイスへのポインターを取得します。 次に、IPin ポインターとVideoControlFlag_Trigger フラグを使用して IAMVideoControl::SetMode を呼び出します。

注意

カメラによっては、まだピンが接続される前にキャプチャ ピン (PIN_CATEGORY_CAPTURE) のレンダリングが必要になる場合があります。

 

例: サンプル グラバー フィルターの使用

画像をキャプチャする 1 つの方法は、 サンプル グラバー フィルターを使用することです。 サンプル グラバーは、アプリケーション定義のコールバック関数を使用してイメージを処理します。 サンプル グラバー フィルターの詳細については、「 サンプル グラバーの使用」を参照してください。

次の例では、引き続きピンが非圧縮 RGB イメージを提供することを前提としています。 まず、サンプル グラバーのコールバック インターフェイス ISampleGrabberCB を実装するクラスを定義します。

// Class to hold the callback function for the Sample Grabber filter.
class SampleGrabberCallback : public ISampleGrabberCB
{
    // Implementation is described later.
}

// Global instance of the class.
SampleGrabberCallback g_StillCapCB;

クラスの実装については、後で説明します。

次に、引き続きピンをサンプル グラバーに接続し、サンプル グラバーを Null レンダラー フィルターに接続します。 Null レンダラーは、受信したメディア サンプルを破棄するだけです。実際の作業はコールバック内で行われます。 (Null レンダラーの唯一の理由は、サンプル グラバーの出力ピンを何かに接続することです)。 CoCreateInstance を呼び出してサンプル グラバーフィルターと Null レンダラー フィルターを作成し、 IFilterGraph::AddFilter を呼び出して両方のフィルターをグラフに追加します。

// Add the Sample Grabber filter to the graph.
IBaseFilter *pSG_Filter;
hr = CoCreateInstance(
    CLSID_SampleGrabber, 
    NULL, 
    CLSCTX_INPROC_SERVER,
    IID_IBaseFilter, 
    (void**)&pSG_Filter
    );

hr = pGraph->AddFilter(pSG_Filter, L"SampleGrab");

// Add the Null Renderer filter to the graph.
IBaseFilter *pNull;

hr = CoCreateInstance(
    CLSID_NullRenderer, 
    NULL, 
    CLSCTX_INPROC_SERVER,
    IID_IBaseFilter, 
    (void**)&pNull
    );

hr = pGraph->AddFilter(pNull, L"NullRender");

ICaptureGraphBuilder2::RenderStream メソッドを使用すると、1 つのメソッド呼び出しで 3 つのフィルターすべてを接続できます。このフィルターは、引き続きピンからサンプル グラバーに、サンプル グラバーから Null レンダラーに接続します。

hr = pBuild->RenderStream(
    &PIN_CATEGORY_STILL, // Connect this pin ...
    &MEDIATYPE_Video,    // with this media type ...
    pCap,                // on this filter ...
    pSG_Filter,          // to the Sample Grabber ...
    pNull);              // ... and finally to the Null Renderer.

次に、 ISampleGrabber インターフェイスを使用してサンプル グラバーを構成し、サンプルをバッファーするようにします。

// Configure the Sample Grabber.
ISampleGrabber *pSG = NULL;

hr = pSG_Filter->QueryInterface(IID_ISampleGrabber, (void**)&pSG);
if (SUCCEEDED(hr))
{
    hr = pSG->SetOneShot(FALSE);
    hr = pSG->SetBufferSamples(TRUE);

    ...

コールバック オブジェクトへのポインターを使用してコールバック インターフェイスを設定します。

hr = pSG->SetCallback(&g_StillCapCB, 0); // 0 = Use the SampleCB callback method.

サンプル グラバーとの接続に引き続きピンが使用したメディアの種類を取得します。

// Store the media type for later use.
AM_MEDIA_TYPE g_StillMediaType;

hr = pSG->GetConnectedMediaType(&g_StillMediaType);
pSG->Release();

このメディアの種類には、静止画像の形式を定義する BITMAPINFOHEADER 構造体が含まれます。 アプリケーションが終了する前にメディアの種類を解放します。

// On exit, remember to release the media type.
FreeMediaType(g_StillMediaType);

コールバック クラスの例を次に示します。 クラスは ISampleGrabber インターフェイスを介して継承する IUnknown を実装しますが、参照カウントは保持しません。 これは、アプリケーションがスタック上にオブジェクトを作成し、オブジェクトがフィルター グラフの有効期間を通じてスコープ内に残るため、安全です。

すべての作業は BufferCB メソッドで行われます。このメソッドは、新しいサンプルを取得するたびに Sample Grabber によって呼び出されます。 次の例では、 メソッドはビットマップをファイルに書き込みます。

class SampleGrabberCallback : public ISampleGrabberCB
{
public:
    // Fake referance counting.
    STDMETHODIMP_(ULONG) AddRef() { return 1; }
    STDMETHODIMP_(ULONG) Release() { return 2; }

    STDMETHODIMP QueryInterface(REFIID riid, void **ppvObject)
    {
        if (NULL == ppvObject) return E_POINTER;
        if (riid == __uuidof(IUnknown))
        {
            *ppvObject = static_cast<IUnknown*>(this);
             return S_OK;
        }
        if (riid == __uuidof(ISampleGrabberCB))
        {
            *ppvObject = static_cast<ISampleGrabberCB*>(this);
             return S_OK;
        }
        return E_NOTIMPL;
    }

    STDMETHODIMP SampleCB(double Time, IMediaSample *pSample)
    {
        return E_NOTIMPL;
    }

    STDMETHODIMP BufferCB(double Time, BYTE *pBuffer, long BufferLen)
    {
        if ((g_StillMediaType.majortype != MEDIATYPE_Video) ||
            (g_StillMediaType.formattype != FORMAT_VideoInfo) ||
            (g_StillMediaType.cbFormat < sizeof(VIDEOINFOHEADER)) ||
            (g_StillMediaType.pbFormat == NULL))
        {
            return VFW_E_INVALIDMEDIATYPE;
        }
        HANDLE hf = CreateFile("C:\\Example.bmp", GENERIC_WRITE, 
        FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, 0, NULL);
        if (hf == INVALID_HANDLE_VALUE)
        {
            return E_FAIL;
        }
        long cbBitmapInfoSize = g_StillMediaType.cbFormat - SIZE_PREHEADER;
        VIDEOINFOHEADER *pVideoHeader =
           (VIDEOINFOHEADER*)g_StillMediaType.pbFormat;

        BITMAPFILEHEADER bfh;
        ZeroMemory(&bfh, sizeof(bfh));
        bfh.bfType = 'MB';  // Little-endian for "BM".
        bfh.bfSize = sizeof( bfh ) + BufferLen + cbBitmapInfoSize;
        bfh.bfOffBits = sizeof( BITMAPFILEHEADER ) + cbBitmapInfoSize;
        
        // Write the file header.
        DWORD dwWritten = 0;
        WriteFile( hf, &bfh, sizeof( bfh ), &dwWritten, NULL );
        WriteFile(hf, HEADER(pVideoHeader), cbBitmapInfoSize, &dwWritten, NULL);        
        WriteFile( hf, pBuffer, BufferLen, &dwWritten, NULL );
        CloseHandle( hf );
        return S_OK;

    }
};

ビデオ キャプチャ タスク