您的位置:首页 > 其它

dshow中使用Sample Grabber filter抓取图像

2013-08-22 20:18 232 查看
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(¤tPos);

//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

没实现
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐