从网络摄像头预览中捕获图像的最佳DirectShow方式? SampleGrabber已弃用

时间:2012-07-09 15:55:56

标签: c++ directshow

我开发了DirectShow C ++应用程序,它成功地将网络摄像头视图预览到提供的窗口中。现在我想从这个实时网络摄像头预览中捕获图像。我已经使用了图形管理器,ICaptureGraphBuilder2,IMoniker等。 我搜索并找到以下选项: WIA&样品采集器。 许多人建议使用SampleGrabber,但根据MS的msdn文档SampleGrabber已弃用,不应使用。而且我不想使用WIA API。

那么哪种DirectShow最佳方式可以从实时网络摄像头预览中捕获图像?

3 个答案:

答案 0 :(得分:3)

以下是DxSnap sample from DirectShow.NET library的引用:

  

使用DirectShow从捕获的Still引脚拍摄快照   设备。请注意,MS会鼓励您使用WIA,但如果您这样做   想要使用DirectShow和C#,这是怎么做的。

     

请注意,此示例仅适用于输出的设备   未压缩的视频为RBG24。这将包括大多数网络摄像头,但   可能是零电视调谐器。

这是C#代码,但你应该明白接口是完全相同的。关于如何在C ++中使用Sample Grabber Filter还有其他示例。

不推荐使用示例Grabber,从几个最新的SDK中删除标题,但是运行时组件都在那里,并且将在那里存在很长时间,否则会破坏大量应用程序(例如视频聊天)在浏览器托管的GMail正在使用Sample Grabber)。因此,基本上,Sample Grabber仍然是一种从网络摄像头捕获快照的简单方法,或者如果您更喜欢使用最新的MS API - 您可能需要查看Media Foundation( 2016年7月9日更新:新的Windows Server安装可能需要一个添加“Media Foundation”和/或“桌面体验”功能,以使Media Foundation API与DirectShow一起使用,而DirectShow编辑服务,Sample Grabber是其中的一部分。默认安装不提供qedit .dll开箱即用)。

同样在C ++中,您当然不必使用Sample Grabber Filter。您可以使用DirectShow BaseClasses开发自定义过滤器作为自定义转换过滤器或自定义渲染器,它们接受传入的视频源并从DirectShow管道导出帧。另一种选择是使用来自旧版SDK之一的Sample Grabber示例源代码(这不是OS Sample Grabber的确切来源,但它正在做同样的事情)。然而,与Windows一起提供的Sample Grabber仍然是一个不错的选择。

答案 1 :(得分:1)

在微软网站上列出的是如何使用IVMRWindowlessControl9 :: GetCurrentImage捕获帧的示例......以下是一种方法:

IBaseFilter*            vmr9ptr; // I'm assuming that you got this pointer already
IVMRWindowlessControl9* controlPtr = NULL;

vmr9ptr->QueryInterface(IID_IVMRWindowlessControl9, (void**)controlPtr);
assert ( controlPtr != NULL );

// Get the current frame
BYTE*   lpDib = NULL;
hr = controlPtr->GetCurrentImage(&lpDib);

// If everything is okay, we can create a BMP
if (SUCCEEDED(hr))
{
    BITMAPINFOHEADER*   pBMIH = (BITMAPINFOHEADER*) lpDib;
    DWORD               bufSize = pBMIH->biSizeImage;

    // Let's create a bmp
    BITMAPFILEHEADER    bmpHdr;
    BITMAPINFOHEADER    bmpInfo;
    size_t              hdrSize     = sizeof(bmpHdr);
    size_t              infSize     = sizeof(bmpInfo);

    memset(&bmpHdr, 0, hdrSize);
    bmpHdr.bfType                   = ('M' << 8) | 'B';
    bmpHdr.bfOffBits                = static_cast<DWORD>(hdrSize + infSize);
    bmpHdr.bfSize                   = bmpHdr.bfOffBits + bufSize;

    // Builder the bit map info.
    memset(&bmpInfo, 0, infSize);
    bmpInfo.biSize                  = static_cast<DWORD>(infSize);
    bmpInfo.biWidth                 = pBMIH->biWidth;
    bmpInfo.biHeight                = pBMIH->biHeight;
    bmpInfo.biPlanes                = pBMIH->biPlanes;
    bmpInfo.biBitCount              = pBMIH->biBitCount;

    // boost::shared_arrays are awesome!
    boost::shared_array<BYTE> buf(new BYTE[bmpHdr.bfSize]);//(lpDib);
    memcpy(buf.get(),                       &bmpHdr,    hdrSize); // copy the header
    memcpy(buf.get() + hdrSize,             &bmpInfo,   infSize); // now copy the info block
    memcpy(buf.get() + bmpHdr.bfOffBits,    lpDib,      bufSize);

    // Do something with your image data ... seriously...
    CoTaskMemFree(lpDib);

} // All done!

答案 2 :(得分:1)

jeez ......这么多信息。如果你在directshow图表中预览,那么它取决于你正在预览的内容。捕获滤波器具有1,2或3个引脚。如果它有1个引脚,则很可能是“捕获”引脚(没有预览引脚)。为此,如果您想同时捕获和预览,您应该放入一个“Smart Tee”过滤器,并将VMR从预览引脚上连接起来,然后从捕获引脚上取下“抓取帧的东西”。因为你不想愚弄DirectShow的crummy引脚启动/停止(相反,只是简单地控制整个图形的启动/停止状态)。你不需要使用SampleGrabber,它是一个简单的过滤器,你可以在几个小时内编写它(我应该知道,我是写它的那个)。它只是一个CTransInPlace过滤器,你可以设置一个强制媒体类型让它接受,你可以在它上面设置一个回调接口,以便在收到样本时回电。编写一个NullRenderer实际上更简单,它在收到样本时会回调你,你可以很容易地写出来。

如果捕获滤波器有2个引脚,则很可能是捕获引脚和静止引脚。在这种情况下,你仍然需要一个连接到信号源捕捉引脚的Smart Tee,并且需要预览智能T形接头的预览引脚,并从智能T形接头的引脚上捕捉样品。

(如果您不知道SmartTee是什么,它是一个过滤器,可以播放分配器技巧,只有当捕获引脚没有超级陷入困境时才会向预览引脚发送样本。它的工作是提供一个路径要渲染的VMR不会破坏捕获过滤器和捕获过滤器下游过滤器之间的分配器。

如果捕获过滤器同时具有捕获和预览引脚,我认为您可以找出您需要做的事情。

无论如何,摘要:SampleGrabber只是一个CTransInPlaceFilter。您也可以将它写为Null渲染器,只需确保在CheckInputType中填写一些垃圾,然后在DoRenderSample中调用回调。