为 Windows Mobile 创建自定义 DirectShow SampleGrabber 过滤器






4.73/5 (22投票s)
本文介绍了如何为 Windows Mobile 编写自定义的 DirectShow SampleGrabber 过滤器。
引言
本文介绍了如何为 Windows Mobile 编写自定义的 DirectShow SampleGrabber
过滤器。
背景
在我最近的一个项目中,我需要在 .NET CF 中进行一些实时视频分析。然而,.NET API 只允许拍摄静态图像或录制视频,但它没有提供访问视频帧缓冲区的方法,因此我无法即时解析来自摄像头的帧。所以我决定使用 DirectShow
API,它对视频流的控制更好,但仍然缺少 Windows 完整 DirectShow
库中可用的 ISampleGrabber
接口。剩下的选择是编写一个自定义的 DirectShow
过滤器,它实现了 ISampleGrabber
接口,允许开发者访问视频缓冲区数据。在本文中,我将尝试解释我在这个过程中学到的东西。
您可以将示例代码作为编写自己过滤器的起点,或者直接使用现成的 SampleGrabber
过滤器并在您的项目中进行使用。网上有许多我用来完成项目的参考资料,但据我所知,目前还没有关于如何完整解决这个问题的“操作指南”。
设置您的 Visual Studio 项目
在您设置 Visual Studio 项目之前,您需要安装 Windows Mobile SDK、Windows CE 5.0 和 Windows CE 5.0 Platform builder,其中包含 BaseClasses
库。通常情况下,您不一定会用到 BaseClasses
,但它确实能让您的工作轻松很多,所以我加以利用了。
首先创建一个支持 ATL 的 Smart Devices DLL 项目。您还需要将 Windows Mobile SDK 和 Platform Builder 中的头文件添加到您的 include 路径中。
编写过滤器
在这个示例中,我们将使用 TransInPlaceFilter
,它是 TransformFilter
的简化版本。我们不需要以任何方式更改数据,只需要将其传递给客户端应用程序。因此,我们创建了一个 CSampleGrabber
类,它还实现了我们自定义的接口以及任何额外的函数。我只添加了 RegisterCallback
函数,它传递来自客户端应用程序的函数指针。每当一个 MediaSample
(一个视频帧)通过过滤器时,就会调用此函数,以便客户端可以复制数据并进行一些处理。
// define the filter class
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
函数。
// Set the client callback
STDMETHODIMP
CSampleGrabber::RegisterCallback( MANAGEDCALLBACKPROC mdelegate )
{
// Set pointer to managed delegate
callback = mdelegate;
return S_OK;
}
// Get the pointer to the raw data and pass it to the applications
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;
}
Using the Code
您可以像使用其他任何过滤器一样,直接在您的 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 来创建和管理过滤器图,并通过 P/Invoke 从您的 .NET CF 应用程序中调用它。直接在 .NET CF 中创建过滤器图可能会有些棘手,因为 .NET CF 缺乏 C++ .NET 支持,但也许是可能的。
在线参考
- 一篇很棒的 DirectShow 和 COM 文章,详细介绍了编写
DirectShow Transform
过滤器。 - 来自 RadScorpion's Blog 的精彩
DirectShow
信息。 - Microsoft 的 Windows Mobile Developer Center。
- MSDN 的 创建 Transform Filter。
- MSDN 的 如何从 Microsoft DirectShow 过滤器图中获取数据。
历史
- 2008年8月23日 创建文章
- 2008年8月26日 v1.1 创建。
修复- Release 现在可以成功构建,添加了
NonDelegatingRelease
的定义。 - 包含 C++ 和 C# 客户端应用程序,展示了如何使用该过滤器。
- 示例代码现在可以在 WM SDK 5 下编译。
- Release 现在可以成功构建,添加了
- 28.11.2008
- 添加了更多示例代码:
CameraCaptureDLL
项目包含CameraCapture
示例和内置的SampleGrabber
过滤器。TestCameraCapture
项目是一个 C# 应用程序示例,它使用该过滤器来获取实时帧数据。 - 注意:示例代码演示了基本概念,但其中存在一些问题。例如,在尝试停止过滤器图时可能会卡住。这是由于在您尝试停止时过滤器仍在处理数据(抱歉,我没有时间修复这个问题)。尽管存在 bug,但您应该能够获取到摄像头的图像数据。
- 添加了更多示例代码: