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
没实现