今天跟大家唠唠我捣鼓 `ISampleGrabberCB` 的那些事儿,这玩意儿听着就挺唬人,用起来也真不简单,不过搞明白还是挺有意思的。
我接到个任务,需要从摄像头获取视频流,并且要能实时处理每一帧图像。这听起来简单,但实际上坑不少。查半天资料,发现 `DirectShow` 里的 `Sample Grabber` 过滤器是个不错的选择,它能截取视频流里的每一帧数据。而 `ISampleGrabberCB` 接口,就是用来接收这些数据的关键。
我得先搭建好整个 `DirectShow` 的 filter graph。这部分比较繁琐,包括创建 graph manager,添加摄像头 filter,还有最重要的 `Sample Grabber` filter。这一步我参考不少网上的例子,主要是确保摄像头能正常工作,并且数据能顺利地流到 `Sample Grabber`。
重头戏来,实现 `ISampleGrabberCB` 接口。这接口里有两个方法需要实现:`SampleCB` 和 `BufferCB`。一开始我有点懵,不知道该用哪个。查些资料,`SampleCB` 是每次有新的 sample 到达时被调用,而 `BufferCB` 则是直接操作 buffer 的。考虑到我需要对每一帧图像进行处理,`SampleCB` 更适合我。
我创建一个类,继承 `ISampleGrabberCB`,然后实现 `SampleCB` 方法。在这个方法里,我获取 `IMediaSample` 接口,然后通过 `GetPointer` 方法拿到图像数据的指针。拿到指针后,就可以对图像数据进行各种处理,比如转格式、做一些图像识别等等。
c++
class CSampleGrabberCB : public ISampleGrabberCB
public:
STDMETHODIMP SampleCB(double SampleTime, IMediaSample pSample)
BYTE pBuffer;
pSample->GetPointer(&pBuffer);
long bufferLen = pSample->GetSize();
// 在这里处理图像数据,比如转格式、图像识别等
// ...
return S_OK;
STDMETHODIMP BufferCB(double Time, BYTE pBuffer, long bufferLen)
// 这个方法暂时不用
return S_OK;
STDMETHODIMP QueryInterface(REFIID riid, void ppv) {
if (__uuidof(ISampleGrabberCB) == riid) {
ppv = static_cast
return S_OK;
return E_NOINTERFACE;
ULONG STDMETHODCALLTYPE AddRef() { return 1; }
ULONG STDMETHODCALLTYPE Release() { return 1; }
这段代码只是一个简单的框架,实际应用中需要根据具体的需求来填充 `SampleCB` 里的内容。
在实现 `ISampleGrabberCB` 接口之后,我还需要把这个接口设置给 `Sample Grabber` filter。这需要先拿到 `ISampleGrabber` 接口,然后调用 `SetCallback` 方法。
c++
CComPtr
CComPtr
CSampleGrabberCB m_pGrabberCB = new CSampleGrabberCB();
// 创建 Sample Grabber filter
hr = CoCreateInstance(CLSID_SampleGrabber, NULL, CLSCTX_INPROC_SERVER, IID_IBaseFilter, (void)&pSampleGrabberFilter);
hr = pGraph->AddFilter(pSampleGrabberFilter, L"Sample Grabber");
hr = pSampleGrabberFilter->QueryInterface(IID_ISampleGrabber, (void)&m_pSampleGrabber);
// 设置回调
hr = m_pSampleGrabber->SetCallback(m_pGrabberCB, 0);
这里需要注意,`SetCallback` 方法的第二个参数是用来控制回调类型的。`0` 表示使用 `SampleCB` 方法,`1` 表示使用 `BufferCB` 方法。
一切就绪后,就可以开始运行 filter graph 。运行过程中,`Sample Grabber` 会不断地截取视频帧,并且调用我实现的 `SampleCB` 方法。这样,我就能实时地获取到每一帧图像的数据,进行各种处理。
实际过程中肯定遇到不少坑。比如,图像格式不正确,导致处理失败;回调函数执行速度太慢,导致视频卡顿等等。这些问题都需要耐心调试和优化。
`ISampleGrabberCB` 是一个强大的接口,可以用来实现各种复杂的视频处理功能。虽然上手有点难度,但只要搞明白原理,就能灵活运用。希望我的实践经历能对大家有所帮助。