3단계: Frame-Grabbing 함수 구현
[이 페이지와 연결된 기능인 DirectShow는 레거시 기능입니다. MediaPlayer, IMFMediaEngine 및 Media Foundation의 오디오/비디오 캡처로 대체되었습니다. 이러한 기능은 Windows 10 및 Windows 11 최적화되었습니다. 가능한 경우 새 코드가 DirectShow 대신 Media Foundation에서 MediaPlayer, IMFMediaEngine 및 오디오/비디오 캡처를 사용하는 것이 좋습니다. 가능한 경우 레거시 API를 사용하는 기존 코드를 다시 작성하여 새 API를 사용하도록 제안합니다.]
[이 API는 지원되지 않으며 나중에 변경되거나 사용할 수 없습니다.]
이 항목은 포스터 프레임 잡기의 3단계입니다.
다음 단계는 Media Detector를 사용하여 포스터 프레임을 잡는 GetBitmap 함수를 구현하는 것입니다. 이 함수 내에서 다음 단계를 수행합니다.
- Media Detector를 만듭니다.
- 미디어 파일을 지정합니다.
- 파일의 각 스트림을 검사합니다. 비디오 스트림인 경우 비디오의 기본 차원을 가져옵니다.
- 검색 시간과 대상 차원을 지정하여 포스터 프레임을 가져옵니다.
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;
}
관련 항목