DirectShow DirectShow 튜토리얼 포스터 프레임의 포착 순서 3: 프레임 포착 함수를 처리 한다 [목차열람] [주소복사] [슬롯비우기] |
Microsoft DirectX 9.0 |
다음의 순서에서는, GetBitmap 함수를 처리 한다. 이 함수는 미디어 detector를 사용해 포스터 프레임을 포착한다. 이 함수의 내부에서, 이하의 순서를 실행한다.
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;
}