编写自定义DirectShow SampleGrabber过滤器

在最近的一个项目中,需要在.NET CF中进行实时视频分析。然而,.NET API只允许拍摄静态图片或录制视频,并没有提供直接访问视频帧缓冲区的方法,以便能即时解析来自摄像头的帧。因此,决定使用DirectShowAPI,它提供了更好的视频流控制,但在Windows完整版DirectShow库中才有的ISampleGrabber接口在.NET CF中并不可用。剩下的选择是编写一个自定义的DirectShow过滤器,该过滤器实现了ISampleGrabber接口,允许开发者访问视频缓冲区数据。在本文中,将尝试解释在这个过程中所学到的知识。

可以使用示例代码作为编写自己的过滤器的起点,或者直接使用准备好的SampleGrabber过滤器在项目中。虽然在项目中使用了多个在线参考资料,但据所知,目前还没有一个完整的“如何”指南来解决这个问题。

设置Visual Studio项目

在设置Visual Studio项目之前,需要安装Windows Mobile SDK、Windows CE 5.0和Windows CE 5.0 Platform Builder,后者包含了BaseClasses库。一般来说,不一定需要使用BaseClasses,但它绝对可以让工作变得更加轻松,所以利用了它。

首先创建一个支持ATL的智能设备DLL项目。还需要将Windows Mobile SDK和Platform Builder的头文件添加到包含路径中。

编写过滤器

在这个例子中,将使用TransInPlaceFilter,这是TransformFilter的简化版本。不需要以任何方式更改数据,只需要将其传递给客户端应用程序。因此创建了一个CSampleGrabber类,它还实现了自定义接口以及任何附加函数。添加了RegisterCallback函数,该函数从客户端应用程序传递一个函数指针。这个函数将在MediaSample(一个视频帧)通过过滤器时被调用,因此客户端可以复制数据并进行一些处理。

class CSampleGrabber : public CTransInPlaceFilter, public ISampleGrabber { private: MANAGEDCALLBACKPROC callback; long m_Width; long m_Height; long m_SampleSize; long m_Stride; public: // instantiation CSampleGrabber(IUnknown *pOuter, HRESULT *phr, BOOL ModifiesData); ~CSampleGrabber(); static CUnknown *WINAPI CreateInstance(LPUNKNOWN punk, HRESULT *phr); // IUnknown DECLARE_IUNKNOWN; STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, void **ppv); // CTransInPlaceFilter HRESULT CheckInputType(const CMediaType *pmt); HRESULT SetMediaType(PIN_DIRECTION direction, const CMediaType *pmt); HRESULT Transform(IMediaSample *pMediaSample); HRESULT DecideBufferSize(IMemAllocator *pAlloc, ALLOCATOR_PROPERTIES *pProperties); HRESULT GetMediaType(int iPosition, CMediaType *pMediaType); HRESULT CheckTransform(const CMediaType *mtIn, const CMediaType *mtOut) { return NOERROR; } // ISampleGrabber STDMETHODIMP RegisterCallback(MANAGEDCALLBACKPROC mdelegate); };

可以像实现任何其他过滤器一样实现所有接口。有趣的部分是Transform和RegisterCallback函数:

STDMETHODIMP CSampleGrabber::RegisterCallback(MANAGEDCALLBACKPROC mdelegate) { // Set pointer to managed delegate callback = mdelegate; return S_OK; } HRESULT CSampleGrabber::Transform(IMediaSample *pMediaSample) { long Size = 0; BYTE *pData; if (!pMediaSample) return E_FAIL; // Get pointer to the video buffer data if (FAILED(pMediaSample->GetPointer(&pData))) return E_FAIL; Size = pMediaSample->GetSize(); // invoke managed delegate if (callback) callback(pData, Size); return S_OK; }

使用代码

可以直接在DirectShow应用程序中使用SampleGrabber过滤器,就像使用任何其他过滤器一样。在尝试实例化它之前,重要的是要注册DLL。

// Create and initialize the SampleGrabber filter CoCreateInstance(CLSID_SampleGrabber, NULL, CLSCTX_INPROC, IID_IBaseFilter, (void**)&pSampleGrabber); m_pFilterGraph->AddFilter(pSampleGrabber, FILTERNAME); // Get a pointer to the ISampleGrabber interface pSampleGrabber->QueryInterface(IID_ISampleGrabber, (void**)&m_pISampleGrabber); // Register the client callback if (m_pISampleGrabber) m_pISampleGrabber->RegisterCallback(&CGraphBuilder::OnSampleProcessed);

当客户端接收到帧样本时,它应该尽快将其复制到本地缓冲区并返回。这允许过滤器继续进行,而不必等待客户端处理数据,这将显著减慢整个图。

也可以创建一个C++ DLL,该DLL创建过滤器图并管理它,并通过P/Invoke从.NET CF应用程序中调用它。直接在.NET CF中创建过滤器图可能会更棘手,因为.NET CF缺乏C++ .NET支持,但也许是可能的。

一篇详细介绍编写DirectShowTransform过滤器的DirectShow和COM文章。

来自RadScorpion的博客的DirectShow信息。

来自Microsoft的Windows Mobile开发者中心。

来自MSDN的创建Transform过滤器。

来自MSDN的如何从Microsoft DirectShow过滤器图获取数据。

2008年8月23日 创建文章

2008年8月26日 v1.1 创建。

修复:

  • 现在可以正常构建发布版,添加了NonDelegatingRelease的定义
  • 包括C++和C#客户端应用程序,展示了如何使用过滤器
  • 示例代码现在可以在WM SDK 5下编译
沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485