dshow中使用Sample Grabber filter抓取圖像

sample Grabber使用兩種模式抓取圖像:緩衝模式和回調模式,緩衝模式向下傳遞採樣時拷貝每個採樣,而回調模式對於每個採樣調用程序定義的回調函數。回調模式是動態加載filter,影響程序性能,甚至引起死鎖。其中的原因是如果採樣是microsoft directdraw surface,在回調期間surface被鎖定。win16 lock可以被好的鎖定,但兩個會引起潛在的死鎖。具體在dshow文件中有詳細描述。

緩衝區激活與否取決於ISampleGrabber::SetBufferSample的函數取值

sample grabber可以從文件源中或實時源時抓取,本文在下面詳細描述。
首先,回調函數是從ISampleGrabberCB中派生出自己的類,然後實現其虛函數,這個代碼在dshow的sdk的示例程序(DXSDKROOT /Samples/C++/DirectShow/Editing/GrabBitmaps)中完整的代碼實現。這裏不再給出。

主要內容:
1. 緩衝區模式從文件源中抓取
2. 緩衝區模式從實時源中抓取
3. 回調模式從文件源中抓取
4. 回調模式從實時源中抓取
5. 通過GetCurrentImage
6. IMediaDet

1. 緩衝區模式從文件源中抓取
效果圖:
 
1) 初始化GraphBuilder
HRESULT hr;
CComPtr<IGraphBuilder>pGraph;
pGraph.CoCreateInstance(CLSID_FilterGraph);
2) 初始化sample grabber
IBaseFilter *pGrabberF = NULL;
hr = CoCreateInstance(CLSID_SampleGrabber, NULL, CLSCTX_INPROC_SERVER,IID_IBaseFilter, (void**)&pGrabberF);
if (FAILED(hr)){
 return;
}
hr = pGraph->AddFilter(pGrabberF, L"Sample Grabber");
if (FAILED(hr)) {
 return;
}
3) 查詢並設置媒體類型
ISampleGrabber *pGrabber;
pGrabberF->QueryInterface(IID_ISampleGrabber, (void**)&pGrabber);

AM_MEDIA_TYPE mt;
ZeroMemory(&mt, sizeof(AM_MEDIA_TYPE));
mt.majortype = MEDIATYPE_Video;
mt.subtype = MEDIASUBTYPE_RGB24;
hr = pGrabber->SetMediaType(&mt);
4) 建立文件源並連接graph, source, grabber
TCHAR wszFileName[MAX_PATH];
wcscpy(wszFileName, L"I://Documents//example.avi");
IBaseFilter *pSrc;
hr = pGraph->AddSourceFilter(wszFileName, L"Source", &pSrc);
if (FAILED(hr)) {
 return;
}
hr = ConnectFilters(pGraph, pSrc, pGrabberF);
5) 把NULL Renderer增加進去
IBaseFilter *pNull = NULL;
hr = CoCreateInstance(CLSID_NullRenderer, NULL, CLSCTX_INPROC_SERVER, IID_IBaseFilter, reinterpret_cast<void**>(&pNull));
hr = pGraph->AddFilter(pNull, L"NullRenderer");
hr = ConnectFilters(pGraph, pGrabberF, pNull);
6) 檢查連接的媒體類型
hr = pGrabber->GetConnectedMediaType(&mt);
if (FAILED(hr)) {
 return;
}
// Examine the format block.
VIDEOINFOHEADER *pVih;
if ((mt.formattype == FORMAT_VideoInfo) &&
 (mt.cbFormat >= sizeof(VIDEOINFOHEADER)) &&
 (mt.pbFormat != NULL) )
{
 pVih = (VIDEOINFOHEADER*)mt.pbFormat;
}
else
{
 // Wrong format. Free the format block and return an error.
 FreeMediaType(mt);
 return ;//VFW_E_INVALIDMEDIATYPE;
}

// Set one-shot mode and buffering.
hr = pGrabber->SetOneShot(TRUE);
hr = pGrabber->SetBufferSamples(TRUE);
7) 搜索到指定的時間
CComQIPtr<IMediaControl, &IID_IMediaControl>pControl(pGraph);
CComQIPtr<IMediaEventEx,&IID_IMediaEvent>pEvent(pGraph);

IMediaSeeking *pSeeking = NULL;
pGraph->QueryInterface(IID_IMediaSeeking, (void**)&pSeeking);
LONGLONG pCurrentPos = ONE_SECOND * 20;//20秒
hr = pSeeking->SetPositions(&pCurrentPos, AM_SEEKING_AbsolutePositioning, NULL, AM_SEEKING_NoPositioning );

pControl->Run(); // Run the graph.
long evCode;
pEvent->WaitForCompletion(INFINITE, &evCode); // Wait till it's done.
8) 取得當前的buffer數據
//find the required buffer size
long cbBuffer = 0;
hr = pGrabber->GetCurrentBuffer(&cbBuffer, NULL);
LONGLONG currentPos;
pSeeking->GetCurrentPosition(&currentPos);
//Allocate the array and call the method a second time to copy the buffer:
char *pBuffer = new char[cbBuffer];
if (!pBuffer) {
 return;
}
hr = pGrabber->GetCurrentBuffer(&cbBuffer, (long*)(pBuffer));
9) 寫到BMP文件中
HANDLE hf = CreateFile(L"C://Example.bmp", GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, 0, NULL);
if (hf == INVALID_HANDLE_VALUE)
{
 return;
}

// Write the file header.
BITMAPFILEHEADER bfh;
ZeroMemory(&bfh, sizeof(bfh));
bfh.bfType = 'MB';  // Little-endian for "MB".
bfh.bfSize = sizeof( bfh ) + cbBuffer + sizeof(BITMAPINFOHEADER);
bfh.bfOffBits = sizeof( BITMAPFILEHEADER ) + sizeof(BITMAPINFOHEADER);
DWORD dwWritten = 0;
WriteFile( hf, &bfh, sizeof( bfh ), &dwWritten, NULL );

// Write the bitmap format
BITMAPINFOHEADER bih;
ZeroMemory(&bih, sizeof(bih));
bih.biSize = sizeof( bih );
bih.biWidth = pVih->bmiHeader.biWidth;
bih.biHeight = pVih->bmiHeader.biHeight;
bih.biPlanes = pVih->bmiHeader.biPlanes;
bih.biBitCount = pVih->bmiHeader.biBitCount;
dwWritten = 0;
WriteFile(hf, &bih, sizeof(bih), &dwWritten, NULL);       

//write the bitmap bits
dwWritten = 0;
WriteFile( hf, pBuffer, cbBuffer, &dwWritten, NULL );
CloseHandle( hf );
10) 釋放相關資源
pControl->Stop();  
SAFE_RELEASE(pSrc);
SAFE_RELEASE(pNull);
SAFE_RELEASE(pGrabberF);
11) 其它
NullRenderer可以用IVideoWindow代替
CComQIPtr< IVideoWindow, &IID_IVideoWindow > pWindow = pGraph;
if (pWindow)
{
 hr = pWindow->put_AutoShow(OAFALSE);
}
2. 緩衝區模式從實時源中抓取
此方法可以參照1、4的實現
3. 回調模式從文件源中抓取
此代碼在DXSDKROOT /Samples/C++/DirectShow/Editing/GrabBitmaps中有完整實現
效果圖
 
4. 回調模式從實時源中抓取
效果圖
 
相關初始化代碼略

1) 創建並增加sample grabber filter到graph中
hr = m_pSampleGrabber.CoCreateInstance(CLSID_SampleGrabber);
if(FAILED(hr)) {
return hr;
}
CComQIPtr<IBaseFilter, &IID_IBaseFilter>pGrabBase(m_pSampleGrabber);
hr = m_pGraphBuilder->AddFilter(pGrabBase, L"Grabber");
hr = m_pCaptureGB2->RenderStream(&PIN_CATEGORY_PREVIEW, &MEDIATYPE_Interleaved, m_pVCap, pGrabBase, NULL);
if(hr == VFW_S_NOPREVIEWPIN) {
}
else if(hr != S_OK)
{
 hr = m_pCaptureGB2->RenderStream(&PIN_CATEGORY_PREVIEW,&MEDIATYPE_Video, m_pVCap, pGrabBase, NULL);
 if(hr == VFW_S_NOPREVIEWPIN) {
 }
 else if(hr != S_OK)
 {
  m_bPreviewGraphBuilt = FALSE;
  return FALSE;
 }
}
2) 設置媒體類型
// 根據顯示器的設備來設置RGB
CDC *pDC = GetDC();
int iBitDepth = GetDeviceCaps(pDC->GetSafeHdc(), BITSPIXEL);

ZeroMemory(&g_MediaType, sizeof(AM_MEDIA_TYPE));
g_MediaType.majortype = MEDIATYPE_Video;

switch(iBitDepth)
{
case 8:
 g_MediaType.subtype = MEDIASUBTYPE_RGB8;
 break;
case 16:
 g_MediaType.subtype = MEDIASUBTYPE_RGB555;
 break;
case 24:
 g_MediaType.subtype = MEDIASUBTYPE_RGB24;
 break;
case 32:
 g_MediaType.subtype = MEDIASUBTYPE_RGB32;
 break;
default:
 return E_FAIL;
}
hr = m_pSampleGrabber->SetMediaType(&g_MediaType);
if ( FAILED( hr) ) {
return hr;
3) 檢查連接的媒體類型並設置回調函數
hr = m_pSampleGrabber->GetConnectedMediaType(&g_MediaType );

hr = m_pSampleGrabber->SetBufferSamples(FALSE);
hr = m_pSampleGrabber->SetOneShot(FALSE);
hr = m_pSampleGrabber->SetCallback( &g_SGCallback, 1 );
4) 連接IVideoWindow
hr = m_pGraphBuilder->QueryInterface(IID_IVideoWindow, (void **)&m_ pWindow);
if (pWindow)
{
 hr = pWindow->put_AutoShow(OAFALSE);
}
5. 通過GetCurrentImage
1) IBasicVideo::GetCurrentImage
在使用DirectDraw加速時renderer會失敗,需要用戶硬件的支持,所以這個方法不可靠。在調用這個接口要先暫停video renderer。可以通過IMediaControl::GetState確定狀態。
2) IVMRWindowlessControl::GetCurrentImage
VMR沒有上述的問題,可以在graph運行、停止或暫停時抓取
在我的“使用VMR9採集n個視頻的一幀到一張位圖”中有實現
6. IMediaDet
沒實現


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章