DirectShow DirectShow 튜토리얼 포스터 프레임의 포착 순서 3: 프레임 포착 함수를 처리 한다   [목차열람] [주소복사] [슬롯비우기]
순서 3: 프레임 포착 함수를 처리 한다
 
Microsoft DirectX 9.0

순서 3: 프레임 포착 함수를 처리 한다

다음의 순서에서는, GetBitmap 함수를 처리 한다. 이 함수는 미디어 detector를 사용해 포스터 프레임을 포착한다. 이 함수의 내부에서, 이하의 순서를 실행한다.

  1. 미디어 detector를 생성 한다.
  2. 미디어 파일을 지정한다.
  3. 파일내의 각 스트림을 조사한다. 스트림이 비디오 스트림의 경우는, 비디오의 네이티브 넓이를 얻어온다.
  4. 시크 시간과 타겟 넓이를 지정해, 포스터 프레임을 얻어온다.

CoCreateInstance 를 호출해, 미디어 detector 개체를 생성 한다.

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

IMediaDet::put_Filename 메서드를 사용해, 파일명을 지정한다. 이 메서드는,BSTR 형의 파라미터를 받는다.

hr = pDet->put_Filename(bstrFilename);

스트림의 수를 취득해, 각 스트림을 루프 해 미디어 타입을 조사한다. IMediaDet::get_OutputStreams 메서드는 스트림의 수를 취득해,IMediaDet::put_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)  // 비디오 스트림이 발견되었다.
    {
        bFound = true;
        break;
    }
}
if (! bFound) return VFW_E_INVALIDMEDIATYPE;

비디오 스트림이 발견되지 않으면, 함수는 종료한다.

전의 코드에서는,IMediaDet::get_StreamType 메서드가 돌려주는 것은 메이저 타입 GUID 뿐인다. 완전한 미디어 타입을 조사할 필요가 없는 경우는, 이 메서드가 편리하다. 다만, 비디오 넓이를 얻어오려면 포맷 블록을 조사하지 않으면 안 되기 때문에, 완전한 미디어 타입이 필요하다. 완전한 미디어 타입을 얻어오려면,IMediaDet::get_StreamMediaType 메서드를 호출한다. 이 메서드는 AM_MEDIA_TYPE 구조체에 정보를 설정한다. 미디어 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;
    
    // 높이의 절대치가 필요하고, 상하의 방향은 문제는 아니다.
    if (height < 0) height *= -1;
}
else {
    return VFW_E_INVALIDMEDIATYPE; // 이론상은 일어나지 않을 것.
}
FreeMediaType(mt);

get_StreamMediaType 메서드는 포맷 블록을 할당하지만, 이 블록은 호출해 원이 반드시 릴리즈 하는 것. 이 예에서는, base class 라이브러리의 FreeMediaType 함수를 사용하고 있다.

여기까지로, 포스터 프레임을 얻어온다 준비가 완료했다. 최초로, 버퍼용으로 NULL 포인터를 지정해,IMediaDet::GetBitmapBits 메서드를 호출한다.

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

필요한 버퍼 사이즈가 lSize 파라미터에 의해 반환된다. 최초의 파라미터로 시크 하는 스트림 타임을 지정한다. 이 예에서는 타임 0 을 지정하고 있다. 폭과 높이에 대해서는, 이 예에서는 전에 취득한 네이티브 비디오 넓이를 사용하고 있다. 다른 값을 지정 하면, 미디어 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;

    // 파일명을 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;
        
            // 높이의 절대치가 필요하고, 상하의 방향은 문제는 아니다.
            if (height < 0) height *= -1;
        }
        else
        {
            hr = VFW_E_INVALIDMEDIATYPE; // 이론상은 일어나지 않을 것.
        }
        FreeMediaType(mt);
    }
    if (FAILED(hr)) return hr;
    
    // 필요한 버퍼 사이즈를 얻어온다.
    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))
            {
                // 낡은 이미지가 있는 경우는 삭제한다.
                if (*ppbmih) delete[] (*ppbmih);
                (*ppbmih) = (BITMAPINFOHEADER*) pBuffer;
            }
            else
            {
                delete [] pBuffer;
            }
        }
        catch (...) {
            delete [] pBuffer;
            return E_OUTOFMEMORY;
        }
    }
    return hr;
}
↑TOP