다음을 통해 공유


스틸 이미지 핀에서 이미지 캡처

[이 페이지와 연결된 기능인 DirectShow는 레거시 기능입니다. MediaPlayer, IMFMediaEngineMedia Foundation의 오디오/비디오 캡처로 대체되었습니다. 이러한 기능은 Windows 10 및 Windows 11 최적화되었습니다. 가능한 경우 새 코드에서 DirectShow 대신 MediaPlayer, IMFMediaEngine오디오/비디오 캡처를 사용하는 것이 좋습니다. 가능한 경우 레거시 API를 사용하는 기존 코드를 다시 작성하여 새 API를 사용하도록 제안합니다.]

일부 카메라는 캡처 스트림과 별도로 스틸 이미지를 생성할 수 있으며, 종종 스틸 이미지는 캡처 스트림에서 생성된 이미지보다 더 높은 품질입니다. 카메라에 하드웨어 트리거 역할을 하는 단추가 있거나 소프트웨어 트리거를 지원할 수 있습니다. 스틸 이미지를 지원하는 카메라는 고정 범주 PIN_CATEGORY_STILL 스틸 이미지 핀을 노출합니다.

디바이스에서 스틸 이미지를 가져오는 권장 방법은 WIA(Windows 이미지 획득) 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)을 렌더링해야 할 수 있습니다.

 

예: 샘플 그래버 필터 사용

이미지를 캡처하는 한 가지 방법은 샘플 그래버 필터를 사용합니다. 샘플 그래버에서는 애플리케이션 정의 콜백 함수를 사용하여 이미지를 처리합니다. 샘플 그래버 필터에 대한 자세한 내용은 샘플 그래버 사용을 참조하세요.

다음 예제에서는 스틸 핀이 압축되지 않은 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 메서드를 사용하여 한 메서드 호출에서 세 필터를 모두 연결하고, 여전히 핀에서 샘플 그래버로, 샘플 그래버에서 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을 구현하지만 참조 수는 유지하지 않습니다. 애플리케이션이 스택에 개체를 만들고 개체가 필터 그래프의 수명 동안 scope 유지되므로 안전합니다.

모든 작업은 새 샘플을 얻을 때마다 샘플 그래버에 의해 호출되는 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;

    }
};

비디오 캡처 작업