从静止图像图钉捕获图像

某些相机可以生成与捕获流分开的静止图像,并且通常仍图像的质量高于捕获流生成的图像。 相机可能具有充当硬件触发器的按钮,或者它可能支持软件触发。 支持静止图像的相机将公开固定图像图钉,这是固定类别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) 呈现捕获图钉。

 

示例:使用示例抓取器筛选器

捕获图像的一种方法是使用 示例抓取器 筛选器。 Sample Grabber 使用应用程序定义的回调函数来处理图像。 有关示例抓取器筛选器的详细信息,请参阅 “使用示例抓取器”。

以下示例假定仍固定提供未压缩的 RGB 图像。 首先,定义一个类,该类将实现 Sample Grabber 的回调接口 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;

不久将介绍类的实现。

接下来,将仍然固定连接到 Sample Grabber,并将 Sample Grabber 连接到 Null 呈现器 筛选器。 Null 呈现器只放弃接收的媒体样本;实际工作将在回调中完成。 (Null 呈现器的唯一原因是将 Sample Grabber 的输出引脚连接到 something.) Call CoCreateInstance 以创建 Sample Grabber 和 Null Renderer 筛选器,并调用 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 方法在一个方法调用中连接所有三个筛选器,从仍然固定到 Sample Grabber,从 Sample Grabber 连接到 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 接口配置 Sample Grabber,以便对示例进行缓冲:

// 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);

下面是回调类的示例。 请注意,该类实现 IUnknown,它通过 ISampleGrabber 接口继承它,但它不保留引用计数。 这是安全的,因为应用程序在堆栈上创建对象,并且该对象在筛选器图的整个生存期内保留在范围内。

所有工作都在 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;

    }
};

视频捕获任务