下面是根据msdn(参见http://msdn.microsoft.com/en-us/library/dd407288(v=VS.85).aspx)
的帮助文档写的抓取视频某一帧的程序,并将帧以.bmp文件的形式存储起来。开始的时候,由于
不知道directshow版本的差异,花了n久的时间来配置开发环境。
一种是实现ISampleGrabberCB接口,然后调用ISampleGrabber的SetCallBack
方法,详情请参见SDK中的示例程序(DXSDK ROOT/Samples/C++/DirectShow/Editing/GrabBitmaps)。
这样当视频流经过filter时,会自动调用在SetCallBack设置的对应的方法进行数据处理。
另一种是直接使用ISampleGrabber的GetCurrentBuffer的法获取数据然后写入文件,其实二者
的本质是相同的。
环境
vs2008
directshow版本
dxsdk_feb2005.exe
dxsdk_feb2005_extras.exe
//grabber.h
#pragma once
#include "qedit.h"
#include "dshow.h"
class VideoFrameGrabber
{
private:
IGraphBuilder *pGraph;
IBaseFilter *pGrabberF;
IBaseFilter *pNullRender;
IMediaControl *pControl;
IMediaEvent *pEvent;
IMediaSeeking *pSeeking;
ISampleGrabber *pGrabber;
IBaseFilter *pSource;
IEnumPins *pEnum;
AM_MEDIA_TYPE mt;
LONGLONG totalFrame;
boolean initFailed;
CString source;
bool graphStatus;
private:
HRESULT Init();
HRESULT IsPinConnected(IPin *pPin, bool *connected);
HRESULT IsPinDirection(IPin *pPin, PIN_DIRECTION pDir, bool *result);
HRESULT FindUnconnectedPin(IBaseFilter *pFilter, PIN_DIRECTION pDir, IPin **ppPin);
HRESULT MatchPin(IPin *pPin, PIN_DIRECTION pinDir, bool connected, bool *result);
HRESULT ConnectFilters(IGraphBuilder *pGraph, IPin *pPout, IBaseFilter *pDest);
HRESULT ConnectFilters(IGraphBuilder *pGraph, IBaseFilter *pSrc, IPin *pPin);
HRESULT ConnectFilters(IGraphBuilder *pGraph, IBaseFilter *pSrc, IBaseFilter *pDest);
HRESULT WriteBitmap(CString dest, BITMAPINFOHEADER *pBMI, size_t cbBMI,
LONG *pData, size_t cbData);
public:
/*VideoFrameGrabber(void);*/
VideoFrameGrabber(CString source);
~VideoFrameGrabber(void);
void ReleaseResource();
HRESULT GrabberGrameFromVideo(CString dest, LONGLONG *frame);
LONGLONG GetTotalFrame();
};
//grabber.cpp
#include "StdAfx.h"
#include "grabber.h"
template
void SafeRelease(T **ppT)
{
if (*ppT)
{
(*ppT)->Release();
*ppT = NULL;
}
}
void FreeMediaType(AM_MEDIA_TYPE &mt)
{
if (mt.cbFormat != 0)
{
CoTaskMemFree((PVOID)mt.pbFormat);
mt.cbFormat = 0;
mt.pbFormat = NULL;
}
if (mt.pUnk != NULL)
{
mt.pUnk->Release();
mt.pUnk = NULL;
}
}
// VideoFrameGrabber::VideoFrameGrabber(void)
// {
// }
/*
* graphStatus = true means graph is running, else isn't running
*/
VideoFrameGrabber::VideoFrameGrabber(CString source)
{
initFailed = false;
this->source = source;
graphStatus = false;
totalFrame = 0;
HRESULT hr = Init();
if(FAILED(hr))
{
ReleaseResource();
}
}
VideoFrameGrabber::~VideoFrameGrabber(void)
{
ReleaseResource();
}
/*
* initializes interface and create object
*/
HRESULT VideoFrameGrabber::Init()
{
if (!AfxOleInit())
{
AfxMessageBox(100);
AfxMessageBox(CString("can not initialize ole controls for application"));
return S_FALSE;
}
AfxEnableControlContainer();
CString frames;
HRESULT hr;
hr = CoCreateInstance(CLSID_FilterGraph, NULL,CLSCTX_INPROC_SERVER, IID_IGraphBuilder, (void**)&pGraph);
if(FAILED(hr))
goto done;
hr = pGraph->QueryInterface(IID_IMediaControl, (void**)&pControl);
if(FAILED(hr))
goto done;
hr = pGraph->QueryInterface(IID_IMediaEvent, (void **)&pEvent);
if(FAILED(hr))
goto done;
hr = pGraph->QueryInterface(IID_IMediaSeeking, (void **)&pSeeking);
if(FAILED(hr))
goto done;
// create the Sample Grabber filter.
hr =CoCreateInstance(CLSID_SampleGrabber, NULL,CLSCTX_INPROC_SERVER, IID_IBaseFilter, (void**)&pGrabberF);
if(FAILED(hr))
goto done;
hr = pGraph->AddFilter(pGrabberF, L"Sample Grabber");
if(FAILED(hr))
goto done;
hr = pGrabberF->QueryInterface(IID_ISampleGrabber, (void**)&pGrabber);
if(FAILED(hr))
goto done;
//set grabber media type
ZeroMemory(&mt, sizeof(mt));
mt.majortype = MEDIATYPE_Video;
mt.subtype = MEDIASUBTYPE_RGB24;
hr = pGrabber->SetMediaType(&mt);
if(FAILED(hr))
goto done;
//add source filter and connect filter to filter
hr = pGraph->AddSourceFilter(source, _T("Source Filter"), &pSource);
if(FAILED(hr))
goto done;
hr = pSource->EnumPins(&pEnum);
if(FAILED(hr))
goto done;
IPin *pPin = NULL;
while(S_OK == pEnum->Next(1, &pPin, NULL))
{
hr = ConnectFilters(pGraph, pPin, pGrabberF);
SafeRelease(&pPin);
if(SUCCEEDED(hr))
break;
}
//create null renderer and add to filter graph
hr = CoCreateInstance(CLSID_NullRenderer, NULL,CLSCTX_INPROC_SERVER, IID_IBaseFilter, (void**)&pNullRender);
if(FAILED(hr))
goto done;
hr = pGraph->AddFilter(pNullRender, L"Null Renderer");
if(FAILED(hr))
goto done;
hr = ConnectFilters(pGraph, pGrabberF, pNullRender);
if(FAILED(hr))
goto done;
hr = pGrabber->SetOneShot(true);
if(FAILED(hr))
goto done;
hr = pGrabber->SetBufferSamples(true);
if(FAILED(hr))
goto done;
/*
* it is pivotal to put this statement here, but i don't know the reason.
*/
hr = pSeeking->SetTimeFormat(&TIME_FORMAT_FRAME);
if(FAILED(hr))
goto done;
hr = pSeeking->GetDuration(&totalFrame);
if(FAILED(hr))
goto done;
frames.Format(_T("%I64d"), totalFrame);
AfxMessageBox(frames);
done:
if(FAILED(hr))
{
initFailed = true;
ReleaseResource();
}
return hr;
}
HRESULT VideoFrameGrabber::GrabberGrameFromVideo(CString dest, LONGLONG *frame)
{
if(initFailed)
{
AfxMessageBox(CString("an error occured whern initialization"));
return S_FALSE;
}
HRESULT hr = pSeeking->SetPositions(frame, AM_SEEKING_AbsolutePositioning, NULL, AM_SEEKING_NoPositioning);
if(FAILED(hr))
{
goto done;
}
if(graphStatus == false)
hr = pControl->Run();
if(FAILED(hr))
{
goto done;
}
long ecode;
hr = pEvent->WaitForCompletion(INFINITE, &ecode);
if(FAILED(hr))
{
goto done;
}
long bufferSize;
hr = pGrabber->GetCurrentBuffer(&bufferSize, NULL);
if(FAILED(hr))
{
goto done;
}
long *pBuffer = new long[bufferSize];
hr = pGrabber->GetCurrentBuffer(&bufferSize, (long*)pBuffer);
if(FAILED(hr))
{
goto done;
}
hr = pGrabber->GetConnectedMediaType(&mt);
if(FAILED(hr))
{
goto done;
}
if((mt.formattype == FORMAT_VideoInfo) &&
(mt.cbFormat >= sizeof(VIDEOINFOHEADER)) &&
(mt.pbFormat != NULL))
{
VIDEOINFOHEADER *pVih = (VIDEOINFOHEADER*)mt.pbFormat;
hr = WriteBitmap(dest, &pVih->bmiHeader,
mt.cbFormat - SIZE_PREHEADER, pBuffer, bufferSize);
}
else
{
// Invalid format.
hr = VFW_E_INVALIDMEDIATYPE;
}
delete pBuffer;
pBuffer = NULL;
done:
if(FAILED(hr))
{
ReleaseResource();
CString msg;
msg.Format(_T("%I64d"), *frame);
msg = CString("an error occured when grabber frame ") + msg;
AfxMessageBox(msg);
}
return S_OK;
}
HRESULT VideoFrameGrabber::WriteBitmap(CString dest, BITMAPINFOHEADER *pBMI, size_t cbBMI,
LONG *pData, size_t cbData)
{
HANDLE hFile = CreateFile(dest, GENERIC_WRITE, 0, NULL,
CREATE_ALWAYS, 0, NULL);
if (hFile == NULL)
{
return HRESULT_FROM_WIN32(GetLastError());
}
BITMAPFILEHEADER bmf = { };
bmf.bfType = 'MB';
bmf.bfSize = cbBMI + cbData + sizeof(bmf);
bmf.bfOffBits = sizeof(bmf) + cbBMI;
DWORD cbWritten = 0;
BOOL result = WriteFile(hFile, &bmf, sizeof(bmf), &cbWritten, NULL);
if (result)
{
result = WriteFile(hFile, pBMI, cbBMI, &cbWritten, NULL);
}
if (result)
{
result = WriteFile(hFile, pData, cbData, &cbWritten, NULL);
}
HRESULT hr = result ? S_OK : HRESULT_FROM_WIN32(GetLastError());
CloseHandle(hFile);
return hr;
}
/*
* judge whether a pin is connected to another a pin
* by calling ConnectedTo(IPin **ppPin) method of IPin
*/
HRESULT VideoFrameGrabber::IsPinConnected(IPin *pPin, bool *connected)
{
IPin *pTmp = NULL;
HRESULT hr = pPin->ConnectedTo(&pPin);
if(SUCCEEDED(hr))
*connected = true;
else if(VFW_E_NOT_CONNECTED == hr) // this pin is not connected. it's not an error for our purpose,
{
*connected = false;
hr = S_OK;
}
SafeRelease(&pTmp);
return hr;
}
/*
*judge whether a pin has a specficed direction(input or output)
*/
HRESULT VideoFrameGrabber::IsPinDirection(IPin *pPin, PIN_DIRECTION Dir, bool *result)
{
PIN_DIRECTION tmpDir;
HRESULT hr = pPin->QueryDirection(&tmpDir);
if(SUCCEEDED(hr))
{
*result = (Dir == tmpDir);
}
return hr;
}
/*
* judge whether a pin is matched by pin direction and connection status
*/
HRESULT VideoFrameGrabber::MatchPin(IPin *pPin, PIN_DIRECTION pinDir, bool connected, bool *result)
{
bool isConnected = false;
bool match = false;
HRESULT hr = IsPinConnected(pPin, &isConnected);
if(SUCCEEDED(hr))
{
if(isConnected == connected)
hr = IsPinDirection(pPin, pinDir, &match);
}
if(SUCCEEDED(hr))
*result = match;
return hr;
}
/*
* find the first unconnected input pin or ouput pin of a base filter
*/
HRESULT VideoFrameGrabber::FindUnconnectedPin(IBaseFilter *pFilter, PIN_DIRECTION pDir, IPin **ppPin)
{
IEnumPins *pEunm;
IPin *pPin = NULL;
bool found = false;
HRESULT hr = pFilter->EnumPins(&pEunm);
if(FAILED(hr))
goto done;
while(S_OK == pEunm->Next(1, &pPin, NULL))
{
hr = MatchPin(pPin, pDir, false, &found);
if(FAILED(hr))
goto done;
if(found)
{
*ppPin = pPin;
(*ppPin)->AddRef();
break;
}
SafeRelease(&pPin);
}
if (!found)
{
hr = VFW_E_NOT_FOUND;
}
done:
SafeRelease(&pPin);
SafeRelease(&pEunm);
return hr;
}
void VideoFrameGrabber::ReleaseResource()
{
SafeRelease(&pEnum);
SafeRelease(&pNullRender);
SafeRelease(&pSeeking);
SafeRelease(&pSource);
SafeRelease(&pGrabberF);
SafeRelease(&pGrabber);
SafeRelease(&pControl);
SafeRelease(&pEvent);
SafeRelease(&pGraph);
}
/*
*connect a out pin to a filter
*/
HRESULT VideoFrameGrabber::ConnectFilters(IGraphBuilder *pGraph, IPin *pOut, IBaseFilter *pDest)
{
IPin *pIn = NULL;
// Find an input pin on the downstream filter.
HRESULT hr = FindUnconnectedPin(pDest, PINDIR_INPUT, &pIn);
if (SUCCEEDED(hr))
{
// Try to connect them.
hr = pGraph->Connect(pOut, pIn);
pIn->Release();
}
return hr;
}
/*
* find a input pin of a filter and then connect it to a pin
* in other word, it connects a filter to a pin
*/
HRESULT VideoFrameGrabber::ConnectFilters(IGraphBuilder *pGraph, IBaseFilter *pSrc, IPin *pPin)
{
IPin *pOut = NULL;
HRESULT hr = FindUnconnectedPin(pSrc, PINDIR_OUTPUT, &pOut);
if(SUCCEEDED(hr))
{
hr = pGraph->Connect(pOut, pPin);
pOut->Release();
}
return hr;
}
/*
* connect a filter to a filter
*/
HRESULT VideoFrameGrabber::ConnectFilters(IGraphBuilder *pGraph, IBaseFilter *pSrc, IBaseFilter *pDest)
{
IPin *pOut = NULL;
HRESULT hr = FindUnconnectedPin(pSrc, PINDIR_OUTPUT, &pOut);
if(SUCCEEDED(hr))
{
hr = ConnectFilters(pGraph, pOut, pDest);
pOut->Release();
}
return hr;
}
转自:http://hi.baidu.com/johnzhjfly/blog/item/b0477bd73f65edd1a144dfb7.html