다음을 통해 공유


3단계: Frame-Grabbing 함수 구현

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

[이 API는 지원되지 않으며 나중에 변경되거나 사용할 수 없습니다.]

이 항목은 포스터 프레임 잡기의 3단계입니다.

다음 단계는 Media Detector를 사용하여 포스터 프레임을 잡는 GetBitmap 함수를 구현하는 것입니다. 이 함수 내에서 다음 단계를 수행합니다.

  1. Media Detector를 만듭니다.
  2. 미디어 파일을 지정합니다.
  3. 파일의 각 스트림을 검사합니다. 비디오 스트림인 경우 비디오의 기본 차원을 가져옵니다.
  4. 검색 시간과 대상 차원을 지정하여 포스터 프레임을 가져옵니다.

CoCreateInstance를 호출하여 Media Detector 개체를 만듭니다.

CComPtr<IMediaDet> pDet;
hr = pDet.CoCreateInstance(__uuidof(MediaDet));

IMediaDet::p ut_Filename 메서드를 사용하여 파일 이름을 지정합니다. 이 메서드는 BSTR 매개 변수를 사용합니다.

hr = pDet->put_Filename(bstrFilename);

스트림 수를 가져와서 미디어 유형을 확인하는 각 스트림을 반복합니다. IMediaDet::get_OutputStreams 메서드는 스트림 수를 검색하고 IMediaDet::p ut_CurrentStream 메서드는 검사할 스트림을 지정합니다. 첫 번째 비디오 스트림에서 루프를 종료합니다.

long lStreams;
bool bFound = false;
hr = pDet->get_OutputStreams(&lStreams);
for (long i = 0; i < lStreams; i++)
{
    GUID major_type;
    hr = pDet->put_CurrentStream(i);
    hr = pDet->get_StreamType(&major_type);
    if (major_type == MEDIATYPE_Video)  // Found a video stream.
    {
        bFound = true;
        break;
    }
}
if (!bFound) return VFW_E_INVALIDMEDIATYPE;

비디오 스트림을 찾을 수 없으면 함수가 종료됩니다.

이전 코드에서 IMediaDet::get_StreamType 메서드는 주 형식 GUID만 반환합니다. 전체 미디어 형식을 검사할 필요가 없는 경우 편리합니다. 그러나 비디오 차원을 얻으려면 전체 미디어 형식이 필요하도록 형식 블록을 검사해야 합니다. AM_MEDIA_TYPE 구조를 채우는IMediaDet::get_StreamMediaType 메서드를 호출하여 검색할 수 있습니다. Media Detector는 VIDEOINFOHEADER 형식 블록을 사용하여 모든 비디오 스트림을 압축되지 않은 형식으로 변환합니다.

long width = 0, height = 0; 
AM_MEDIA_TYPE mt;
hr = pDet->get_StreamMediaType(&mt);
if (mt.formattype == FORMAT_VideoInfo) 
{
    VIDEOINFOHEADER *pVih = (VIDEOINFOHEADER*)(mt.pbFormat);
    width = pVih->bmiHeader.biWidth;
    height = pVih->bmiHeader.biHeight;
    
    // We want the absolute height, and don't care about up-down orientation.
    if (height < 0) height *= -1;
}
else {
    return VFW_E_INVALIDMEDIATYPE; // This should not happen, in theory.
}
FreeMediaType(mt);

get_StreamMediaType 메서드는 호출자가 해제해야 하는 형식 블록을 할당합니다. 이 예제에서는 기본 클래스 라이브러리의 FreeMediaType 함수를 사용합니다.

이제 포스터 프레임을 가져올 준비가 되었습니다. 먼저 버퍼에 대한 NULL 포인터를 사용하여 IMediaDet::GetBitmapBits 메서드를 호출합니다.

long lSize;
hr = pDet->GetBitmapBits(0, &lSize, NULL, width, height);

이 호출은 lSize 매개 변수에 필요한 버퍼 크기를 반환합니다. 첫 번째 매개 변수는 검색할 스트림 시간을 지정합니다. 이 예제에서는 시간 0을 사용합니다. 너비와 높이의 경우 이 예제에서는 이전에 가져온 네이티브 비디오 차원을 사용합니다. 다른 값을 지정하는 경우 Media Detector는 일치하는 비트맵을 늘입니다.

메서드가 성공하면 버퍼를 할당하고 GetBitmapBits를 다시 호출합니다.

if (SUCCEEDED(hr)) 
{
    char *pBuffer = new char[lSize];
    if (!pBuffer) return E_OUTOFMEMORY;
    hr = pDet->GetBitmapBits(0, NULL, pBuffer, width, height);
    if (FAILED(hr))
        delete [] pBuffer;
}

다음은 다소 더 나은 오류 처리와 함께 전체 함수입니다.

HRESULT GetBitmap(LPTSTR pszFileName, BITMAPINFOHEADER** ppbmih)
{
    HRESULT hr;
    CComPtr<IMediaDet> pDet;
    hr = pDet.CoCreateInstance(__uuidof(MediaDet));
    if (FAILED(hr)) return hr;

    // Convert the file name to a BSTR.
    CComBSTR bstrFilename(pszFileName);
    hr = pDet->put_Filename(bstrFilename);
    if (FAILED(hr)) return hr;

    long lStreams;
    bool bFound = false;
    hr = pDet->get_OutputStreams(&lStreams);
    if (FAILED(hr)) return hr;

    for (long i = 0; i < lStreams; i++)
    {
        GUID major_type;
        hr = pDet->put_CurrentStream(i);
        if (SUCCEEDED(hr))
        {
            hr = pDet->get_StreamType(&major_type);
        }
        if (FAILED(hr)) break;

        if (major_type == MEDIATYPE_Video)
        {
            bFound = true;
            break;
        }
    }
    if (!bFound) return VFW_E_INVALIDMEDIATYPE;

    long width = 0, height = 0; 
    AM_MEDIA_TYPE mt;
    hr = pDet->get_StreamMediaType(&mt);
    if (SUCCEEDED(hr)) 
    {
        if ((mt.formattype == FORMAT_VideoInfo) && 
            (mt.cbFormat >= sizeof(VIDEOINFOHEADER)))
        {
            VIDEOINFOHEADER *pVih = (VIDEOINFOHEADER*)(mt.pbFormat);
            width = pVih->bmiHeader.biWidth;
            height = pVih->bmiHeader.biHeight;
        
            // We want the absolute height, don't care about orientation.
            if (height < 0) height *= -1;
        }
        else
        {
            hr = VFW_E_INVALIDMEDIATYPE; // Should not happen, in theory.
        }
        FreeMediaType(mt);
    }
    if (FAILED(hr)) return hr;
    
    // Find the required buffer size.
    long size;
    hr = pDet->GetBitmapBits(0, &size, NULL, width, height);
    if (SUCCEEDED(hr)) 
    {
        char *pBuffer = new char[size];
        if (!pBuffer) return E_OUTOFMEMORY;
        try {
            hr = pDet->GetBitmapBits(0, NULL, pBuffer, width, height);
            if (SUCCEEDED(hr))
            {
                // Delete the old image, if any.
                if (*ppbmih) delete[] (*ppbmih);
                (*ppbmih) = (BITMAPINFOHEADER*)pBuffer;
            }
            else
            {
                delete [] pBuffer;
            }
        }
        catch (...) {
            delete [] pBuffer;
            return E_OUTOFMEMORY;
        }
    }
    return hr;
}

다음: 4단계: 클라이언트 영역에서 비트맵 그리기

포스터 프레임 잡기