DirectShow抓取视频帧 ISampleGrabber

下面是根据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

你可能感兴趣的:(DirectShow抓取视频帧 ISampleGrabber)