DirectShow DirectShow 튜토리얼 샘플 grabber의 사용법   [목차열람] [주소복사] [슬롯비우기]
샘플 grabber의 사용법
 
Microsoft DirectX 9.0

샘플 grabber의 사용법

샘플 grabber 필터는 변환 필터로, 이 필터를 사용해 스트림이 필터 그래프를 통과할 경우에 스트림으로부터 미디어 샘플을 포착할 수 있다.

다만 비디오 파일로부터 비트 맵을 포착할 뿐(만큼)의 경우는,미디어 detector (MediaDet) 개체를 사용하는 것이 간단하다. 더 자세한 정보는, 「포스터 프레임의 포착」을 참조할것. 다만, 샘플 grabber가 유연하다. 왜냐하면, 샘플 grabber는 거의 모든 미디어 타입 (예외에 대해서는, 「ISampleGrabber::SetMediaType 」를 참조할것)을 조작해, 애플리케이션을 제어할 수 있는 범위를 보다 많이 제공하기 때문이다.

필터 그래프에 샘플 grabber를 추가한다

최초로, 샘플 grabber 필터의 인스턴스를 생성 해, 그 인스턴스를 필터 그래프에 추가한다.

// 샘플 grabber를 생성 한다.
IBaseFilter *pGrabberF = NULL;
hr = CoCreateInstance(CLSID_SampleGrabber, NULL, CLSCTX_INPROC_SERVER,
    IID_IBaseFilter, (void**) &pGrabberF);
if (FAILED(hr))
{
    // 에러를 돌려준다.
}
hr = pGraph->AddFilter(pGrabberF, L"Sample Grabber");
if (FAILED(hr)
{
    // 에러를 돌려준다.
}

샘플 grabber에 ISampleGrabber 인터페이스의 유무를 문의한다.

ISampleGrabber *pGrabber;
pGrabberF->QueryInterface(IID_ISampleGrabber, (void**) &pGrabber);

미디어 타입을 설정한다

샘플 grabber를 최초로 생성 했을 때는, 우선 미디어 타입은 설정되지 않다. 이것은, 그래프내의 거의 모든 필터에 접속은 할 수 있지만, 받는 데이터 타입을 제어 성과인 이토우것이다. 따라서, 나머지의 그래프를 생성 하기 전에,ISampleGrabber::SetMediaType 메서드를 호출해, 샘플 grabber에 대해서 미디어 타입을 설정하는 것.

샘플 grabber는, 접속했을 때에 다른 필터가 제공하는 미디어 타입과 이 설정된 미디어 타입을 비교한다. 조사하는 필드는, 메이저 타입, 서브 타입, 포맷 타입 뿐이다. 이러한 필드에서는, 값 GUID_NULL 는 "모든 값을 받아들인다" 라고 하는 의미이다. 보통은, 메이저 타입과 서브 타입을 설정한다. 예를 들어, 이하의 코드에서는 비압축 24 비트 RGB 비디오를 지정한다.

AM_MEDIA_TYPE mt;
ZeroMemory(&mt, sizeof(AM_MEDIA_TYPE));
mt.majortype = MEDIATYPE_Video;
mt.subtype = MEDIASUBTYPE_RGB24;
hr = pGrabber->SetMediaType(&mt);

다음의 예에서는, 디스플레이의 비트 깊이에 근거해 미디어 타입을 설정한다.

// 현재의 비트 깊이를 얻어온다.
HDC hdc = GetDC(NULL);
int iBitDepth = GetDeviceCaps(hdc, BITSPIXEL);
ReleaseDC(NULL, hdc);

// 미디어 타입을 설정한다.
mt.majortype = MEDIATYPE_Video;
switch (iBitDepth)
{
case 8:
    mt.subtype = MEDIASUBTYPE_RGB8;
    break;
case 16:
    mt.subtype = MEDIASUBTYPE_RGB555;
    break;
case 24:
    mt.subtype = MEDIASUBTYPE_RGB24;
    break;
case 32:
    mt.subtype = MEDIASUBTYPE_RGB32;
    break;
default:
    return E_FAIL;
}
hr = pGrabber->SetMediaType(&mt);

필터 그래프를 생성 한다

이것으로, 나머지의 필터 그래프를 생성 할 수 있다. 샘플 grabber는, 지정된 미디어 타입을 사용해 접속할 뿐(만큼)이므로, 그래프를 생성 할 경우에 필터 그래프 매니저의인텔리전트 접속 메카니즘을 이용할 수 있다.

예를 들어, 비압축 비디오를 지정했을 경우, 소스 필터를 샘플 grabber에 접속 하면, 필터 그래프 매니저는 자동적으로 파일 파서와 디코더를 추가한다. 다음의 예에서는, ConnectFilters 헬퍼-함수를 사용한다. 이 헬퍼-함수는 「2 개의 필터의 접속」에 기재되어 있다.

IBaseFilter *pSrc;
hr = pGraph->AddSourceFilter(wszFileName, L"Source", &pSrc);
if (FAILED(hr))
{
    // 에러 코드를 돌려준다.
}
hr = ConnectFilters(pGraph, pSrc, pGrabberF);

샘플 grabber는 변환 필터이므로, 출력 핀은 다른 필터에 접속해 둘 필요가 있다. 보통, 다 사용한 샘플은 파기하는 것만으로 있다. 그러한 경우, 샘플 grabber를 Null 렌더링 필터에 접속한다. Null 렌더링 필터는, 받은 데이터를 파기한다.

조심하지 않으면 안 되는 것은, 샘플 grabber를 비디오 디코더와 비디오 렌더러의 사이에 배치 하면, 렌더링의 퍼포먼스가 현저하게 저하한다고 하는 점이다. 샘플 grabber는 변환 인 플레이스 필터이다. 이것은, 출력 버퍼가 입력 버퍼와 같다라고 말하는 의미이다. 비디오의 렌더링의 경우, 출력 버퍼는 그래픽 카드상에 있을 가능성이 높지만, 그래픽 카드에서의 읽기 속도는 메인 메모리에서의 읽기 속도와 비교해 극히 늦어진다.

그래프를 실행한다

샘플 grabber가 기능하는 모드는 2 개 있다.

여기에서는, 버퍼링 모드에 대해 설명한다. 콜백 모드를 사용하기에 즈음해서는, 콜백 함수의 사용은 상당 제한해야 한다라고 말하는 점에 주의 해야 한다. 제한하지 않으면 퍼포먼스가 큰폭으로 저하하는지, 경우에 따라서는 데드 록이 일어날 가능성이 있다. 더 자세한 정보는,ISampleGrabber::SetCallback 」를 참조할것. 버퍼링 모드를 액티브하게 하려면 , 값 TRUE 를 설정해 ISampleGrabber::SetBufferSamples 메서드를 호출한다.

옵션으로, 값 TRUE 를 설정해 ISampleGrabber::SetOneShot 메서드를 호출한다. 이 메서드를 호출하면, 샘플 grabber는 최초의 미디어 샘플을 받은 다음에 그래프를 정지한다. 이것은 스트림으로부터 1 개의 프레임을 포착하고 싶은 경우에 편리하다. 목적의 시간에 시크 해, 그래프를 실행해,EC_COMPLETE 이벤트를 기다린다. 프레임아큐레이트의 레벨은, 소스에 따라서 다르다. 예를 들어, MPEG 파일의 시크는 보통은 프레임아큐레이트는 아니다.

// one-shot 모드와 버퍼링을 설정한다.
hr = pGrabber->SetOneShot(TRUE);
hr = pGrabber->SetBufferSamples(TRUE);

pControl->Run(); // 그래프를 실행한다.
pEvent->WaitForCompletion(INFINITE, &evCode); // 완료할 때까지 대기한다.

할 수 있는 한 빨리 그래프를 실행하려면 , 「그래프 클럭의 설정」으로 설명하도록(듯이) 그래프 클럭을 오프로 한다.

샘플을 포착한다

버퍼링 모드에서는, 샘플 grabber는 각 샘플의 복사를 보존한다. ISampleGrabber::GetCurrentBuffer 메서드는, 버퍼를 호출해 원 할당하고 끝난 배열에 복사 한다. 필요한 배열의 사이즈를 판단하려면 , 최초로 배열 주소에 대해서 NULL 포인터를 지정해 GetCurrentBuffer 를 호출한다.

// 필요한 버퍼 사이즈를 얻어온다.
long cbBuffer = 0;
hr = pGrabber->GetCurrentBuffer(&cbBuffer, NULL);

배열을 할당하고 나서, 이 메서드를 다시 호출해, 버퍼를 복사 한다.

char *pBuffer = new char[cbBuffer];
if (! pBuffer) 
{
    // 메모리 부족. 에러 코드를 돌려준다.
}
hr = pGrabber->GetCurrentBuffer(&cbBuffer, (long*) pBuffer);

ISampleGrabber::GetConnectedMediaType 메서드는, 버퍼의 포맷을 돌려준다.

AM_MEDIA_TYPE mt;
hr = pGrabber->GetConnectedMediaType(&mt);
if (FAILED(hr))
{
    // 에러 코드를 돌려준다.
}
// 포맷 블록을 조사한다.
VIDEOINFOHEADER *pVih;
if ((mt.formattype == FORMAT_VideoInfo) && 
    (mt.cbFormat >= sizeof(VIDEOINFOHEADER)) &&
    (mt.pbFormat != NULL) ) 
{
    pVih = (VIDEOINFOHEADER*) mt.pbFormat;
}
else 
{
    // 포맷이 잘못되어 있다. 포맷 블록을 릴리즈 해, 에러를 돌려준다.
    FreeMediaType(mt);
    return VFW_E_INVALIDMEDIATYPE; 
}
// 미디어 타입을 사용해 BITMAPINFOHEADRE 정보에 액세스 할 수 있다.
// 예를 들어, 다음 코드는 GDI 를 사용해 비트 맵을 그린다.
SetDIBitsToDevice(
    hdc, 0, 0, 
    pVih->bmiHeader.biWidth,
    pVih->bmiHeader.biHeight,
    0, 0, 
    0,
    pVih->bmiHeader.biHeight,
    pBuffer,
    (BITMAPINFO*) &pVih->bmiHeader,
    DIB_RGB_COLORS
);

// 완료하면 포맷 블록을 릴리즈 한다.
FreeMediaType(mt);
↑TOP