위의 그림은 동영상을 캡쳐 하기 위한 필터 그래프이다.
사실 위 그림만으로 모든것이 설명이 되지만 ..
그래도 혹시 내가 잊어버릴까봐 부가설명을 ;;
// GraphBuilder와 CaptureGraphBuilder2를 생성한다.
// 비디오 캡쳐를 위해서는 일반적으로 CaptureGraphBuilder2를 이용하는것 같다. (amcap예제 참고)
RETURNIF(CoInitializeEx(NULL, COINIT_APARTMENTTHREADED));
RETURNIF(CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, IID_IGraphBuilder, (void**)&m_videoRecGraph));
RETURNIF(CoCreateInstance (CLSID_CaptureGraphBuilder2 , NULL, CLSCTX_INPROC, IID_ICaptureGraphBuilder2, (void **) &m_vCapBuilder2));
CoUninitialize();
/* 위의 필터 그래프대로 필터들을 생성하고 graph에 추가해 준다.
여기서 사용한 mp4 writer필터는 공개된 mp4 write 필터로서http://www.avideosoft.com/ 에서 구할 수 있다.
상용으로 쓰기 위해서는http://www.3ivx.com/ 에서 제공하는 필터가 더 파워풀하다. 위 사이트의 필터는 640×480이
한계이고 그 이상이 넘어가면 내 테스트 카메라에서는 화면이 일부 깨지는 현상이 발생한다.
상용 필터의 경우는 1600×1200까지 깔끔하게 나오는 위력을 발휘한다. (역시 돈이 있어야 .. 😉
그리고 참고적으로 위의 무료 필터는 640×480으로 캡쳐할 경우 framerate을 수동으로 맞추어야 한다는 단점이 있다.
물론 프로그램 상에서 한번만 하면 되는것이지만.. 왠지 좀 그렇다. ; 320×240은 무리 없이 잘된다.
*/
RETURNIF(CreateFilter("", &m_pCameraFilter, FILTERCG_VIDEOCAP));
RETURNIF(CreateFilter("",&pAudioInForSpeaker,FILTERCG_AUDIOCAP));
RETURNIF(m_videoRecGraph->AddFilter(m_pCameraFilter,L"Camera"));
RETURNIF(CreateFilter("SampleGrabber", &m_pGrabber, FILTERCG_DSHOW));
RETURNIF(m_videoRecGraph->AddFilter(m_pGrabber, L"Grabber"));
RETURNIF(CreateFilter("MP4 Writer", &pMP4Encoder, FILTERCG_DSHOW));
CComQIPtr<IFileSinkFilter> pFileSinkFilter(pMP4Encoder);
pFileSinkFilter->SetFileName(fileName.AllocSysString(),NULL);
RETURNIF(m_videoRecGraph->AddFilter(pMP4Encoder, L"MP4 Writer"));
// 카메라 아웃 핀을 가져온다.
RETURNIF(m_pCameraFilter->EnumPins(&pEnumPins));
while (pEnumPins->Next(1, &pPin, 0) == S_OK)
{
RETURNIF(pPin->QueryDirection(&direction));
if (direction == PINDIR_OUTPUT)
{
// 미디어 스트림에서 기본 값의 포맷을 읽어서 내가 원하는 설정값으로 변경해 준다.
// 일반적으로 내가 테스트 한 웹 카메라에서는 필터 그래프를 렌더한 후에는 해당 설정값이
// 바뀌지 않았다. 따라서 그래프를 렌더 하기 전에 원하는 해상도와 framerate을 설정하였다.
AM_MEDIA_TYPE *mt;
CComQIPtr<IAMStreamConfig> pAM(pPin);
pAM->GetFormat(&mt);
VIDEOINFOHEADER *pVih = (VIDEOINFOHEADER*)mt->pbFormat;
pVih->bmiHeader.biWidth = 640;
pVih->bmiHeader.biHeight = 480;
pVih->AvgTimePerFrame = 1200000;
pAM->SetFormat(mt);
SAFE_RELEASE(pPin);
break;
}
SAFE_RELEASE(pPin);
}
// CaptureBuilder2에 위의 GraphBuilder를 설정한다.
RETURNIF(m_vCapBuilder2->SetFiltergraph(m_videoRecGraph));
//CaptureBuilder2를 이용하여 스트림 렌더링을 한다.
// 렌더 스트림을 할 경우 자동으로 스마트 티가 붙는다. Capture pin에는 MP4 Encoder를 붙이고,
// preview pin에는 NULL 렌더링을 수행한다. 이때 화면 캡쳐를 위해 preview 핀과 렌더러 사이에 grabber를 추가하였다.
m_vCapBuilder2->RenderStream(&PIN_CATEGORY_CAPTURE, &MEDIATYPE_Video, m_pCameraFilter, NULL, pMP4Encoder);
m_vCapBuilder2->RenderStream(&PIN_CATEGORY_PREVIEW, &MEDIATYPE_Video, m_pCameraFilter, m_pGrabber, NULL);
// parent window의 hWnd를 가지고 owner 설정과 윈도우 스타일 설정을 한다.
CComQIPtr<IVideoWindow>pVW(m_videoRecGraph);
if (pVW != NULL)
{
CComQIPtr<IBasicVideo> pBV(m_videoRecGraph);
CRect rect,videoRect;
RETURNIF(pVW->put_Owner((OAHWND)hWnd));
RETURNIF(pVW->put_WindowStyle(WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN));
long width, height;
hr = pBV->GetVideoSize(&width, &height);
this->OnSize(m_parentHwnd);
}
// 추가로 위에서 만든 필터 그래프에 오디오 녹음을 위해 audio 장치를 연결한다.
RETURNIF(m_videoRecGraph->AddFilter(pAudioInForSpeaker,L"Camera"));
RETURNIF(ConnectFilters(m_videoRecGraph, pAudioInForSpeaker, pMP4Encoder));