Best DirectShow way to capture image from web cam preview ? SampleGrabber is deprecated
Asked Answered
G

3

5

I have developed DirectShow C++ app which successfully previews web cam view into provided window. Now I want to capture image from this live web cam preview. I have used graph manager, ICaptureGraphBuilder2, IMoniker etc. for that. I have searched and found following options: WIA & Sample Grabber. Many recommends using SampleGrabber but as per MS's msdn document SampleGrabber is deprecated and one should not use. And I don't want to use WIA API.

So which is the best DirectShow way to capture image from live web cam preview?

Gasser answered 9/7, 2012 at 15:55 Comment(0)
T
4

Here is a quote from DxSnap sample from DirectShow.NET library:

Use DirectShow to take snapshots from the Still pin of a capture device. Note the MS encourages you to use WIA for this, but if you want to do in with DirectShow and C#, here's how.

Note that this sample will only work with devices that output uncompressed video as RBG24. This will include most webcams, but probably zero tv tuners.

This is C# code, but you should get the idea as the interfaces are all the same. And there are other samples on how to use Sample Grabber Filter in C++.

Sample Grabber is deprecated, the headers are removed from a couple of latest SDKs, however the runtime components are all there and are going to be there for a long time, or otherwise a multitude of application would be broken (e.g. Video Chat in browser hosted GMail is using Sample Grabber). So basically Sample Grabber is still an easy way to capture snapshots from a web camera, or if you alternatively prefer to follow the latest MS APIs - you would want to look into Media Foundation (09 Jul 2016 update: new Windows Server installations might need one to add "Media Foundation" and/or "Desktop Experience" features to make Media Foundation API available along with DirectShow, and DirectShow Editing Services, Sample Grabber is a part of which. Default installation does not offer qedit.dll out of the box).

Also in C++ you certainly don't have to use Sample Grabber Filter. You can develop a custom filter using DirectShow BaseClasses to be a custom transformation filter or a custom renderer, which which accept incoming video feed and export the frames from the DirectShow pipeline. Another option is to use Sample Grabber sample source code from one of the older SDKs (which is not exact source for OS Sample Grabber, but it is doing the same thing). The point however that Sample Grabber shipped with Windows is still a good option.

Tymon answered 9/7, 2012 at 18:20 Comment(5)
Might be worth mentioning that the linked sample code uses the Sample Grabber. Also, the C++ code is much much simpler, since you have smart pointers to manage reference counting.Luthanen
Thanks Roman.R. I wrote WIA code to capture image for few hours and then was getting error to initiate WIAVideo object and found that "WIA does not support video devices in Windows Server 2003, Windows Vista, or later.". Now I think the only option is DirectShow and using SampleGrabber which is deprecated. So bad MS.Gasser
You have the pointers above, sample grabber or sample grabber like private filter - it is all up to you to choose.Tymon
SampleGrabber is deprecated. I know, though, people use it but it is not recommended by Microsoft. So any other way except ISampleGrabber?Gasser
It's safe to use SampleGrabber, it will most likely never be removed. VFW has been deprecated for years and is still supported. I recently used it in a project. However your only alternative is to develop a custom filter as Roman stated, create a transform filter and grab the images and then do whatever you want with them.Mixie
R
1

Listed on Microsoft's website is an example of how to capture a frame using IVMRWindowlessControl9::GetCurrentImage ... Here's one way of doing it:

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!
Rockbound answered 31/7, 2012 at 1:42 Comment(2)
A link to where this came from would be awesomeAshkhabad
The example provided by Microsoft is located at msdn.microsoft.com/en-us/library/windows/desktop/… The example by Microsoft did not include the bitmap copying code; I got that code from the examples packaged in the older DirectX SDK.Rockbound
N
1

jeez... so much dis-information. if you're previewing in a directshow graph, then it depends on what you're previewing off of. Capture filters have 1, 2, or 3 pins. If it has 1 pin, it's most likely a "capture" pin (no preview pin). For this, if you want to capture and preview at the same time, you should put in a "Smart Tee" filter, and connect the VMR off of the preview pin, and hook up "something that grabs frames" off the capture pin. since you don't want to fool around with DirectShow's crummy pin start/stop stuff (instead, just simply controlling the entire graph's start/stop state). You don't need to use a SampleGrabber, it's a dead-simple filter and you could write it in a few hours (I should know, I'm the one that wrote it). it's simply a CTransInPlace filter that you can set a forced media type for it to accept, and you can set a callback interface on it to call you back when it receives a sample. It's actually simpler to write a NullRenderer which calls you back when it receives a sample, you could write this quite easily.

If the capture filter has 2 pins, it's most likely a capture pin and a still pin. in this case you still need a Smart Tee connected to the source's capture pin, and need to preview off the smart tee's preview pin, and capture samples off the smart tee's capture pin.

(If you don't know what a SmartTee is, it's a filter that plays allocator tricks and only sends a sample down the preview pin if the capture pin isn't super bogged down. It's job is to provide a path for the VMR to render from which won't botch up the allocators between the capture filter and the filters downstream of the capture filter)

If the capture filter has both capture and preview pins, I think you can figure out what you need to do then.

Anyhow, summary: The SampleGrabber is simply a CTransInPlaceFilter. You could write it as a Null Renderer, too, just make sure to fill out some junk in CheckInputType, and to call your callback back in DoRenderSample.

Nasty answered 13/2, 2014 at 7:2 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.