共用方式為


從仍然影像釘選擷取影像

[與此頁面相關的功能 DirectShow是舊版功能。 它已被 MediaPlayerIMFMediaEngineMedia Foundation 中的音訊/視訊擷取取代。 這些功能已針對Windows 10和Windows 11進行優化。 Microsoft 強烈建議新程式碼盡可能使用 MediaPlayerIMFMediaEngine音訊/視訊擷取 ,而不是 DirectShow。 Microsoft 建議使用舊版 API 的現有程式碼盡可能重寫為使用新的 API。

有些相機可以產生與擷取資料流程不同的仍然影像,而且仍然影像的品質通常高於擷取資料流程所產生的影像。 相機可能有做為硬體觸發程式的按鈕,或可能支援軟體觸發。 支援仍然影像的相機會公開仍然影像釘選,也就是釘選類別PIN_CATEGORY_STILL。

從裝置取得仍然映射的建議方式是使用 Windows 映像取得 (WIA) API。 For more information, see "Windows Image Acquisition" in the Platform SDK documentation. 不過,您也可以使用 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) ,才能連接仍然釘選。

 

範例:使用範例擷取器篩選

擷取影像的其中一種方式是使用 範例擷取器 篩選準則。 範例擷取器會使用應用程式定義的回呼函式來處理影像。 如需範例擷取器篩選的詳細資訊,請參閱 使用範例擷取器

下列範例假設仍釘選會傳遞未壓縮的 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;

類別的實作很快就會描述。

接下來,將仍然釘選連接到範例擷取器,並將範例擷取器連接到 Null 轉譯器 篩選準則。 Null 轉譯器只會捨棄它收到的媒體範例;實際工時將會在回呼內完成。 (Null 轉譯器的唯一原因是將範例擷取器的輸出釘選連接到 something.) 呼叫 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 方法來連接一個方法呼叫中的所有三個篩選,從仍然釘選到 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 介面來設定範例擷取器,以便緩衝範例:

// 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 方法中發生,每當取得新的樣本時,範例擷取器就會呼叫此方法。 在下列範例中,方法會將點陣圖寫入檔案:

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;

    }
};

影片擷取工作