6

I am developing an C++ application which should use an USB camera to capture high resolution photos. It should have same behavior as the Camera application in Windows 10. I am trying to use DirectShow for doing it. Now I am only able to take high resolution photo which is delayed or take a photo in time but low resolution. Also I am very confused from MS documentation, lot of things are deprecated and nowhere mentioned what replaces them. I'll describe my hopeless steps awaiting there will be somebody who could be able to show me a way.

Let's start from beginning...

Knowing nothing about video capturing in Window I started by searching suitable library. After some googling I found there are four main libraries for capturing video in Windows.

  1. Video for Windows
  2. DirectShow
  3. Windows Media Foundation
  4. OpenCV

Let's observe:

  1. Video for Windows
    This library is unfortunately marked as deprecated but it seems it still works. I have written "unfortunately", because I think this is the only which is easy to use. There are only a few lines of code needed for seeing video from camera. The only think I miss here is a "TakePhoto" function. You can use VFW for capture a video or single frames to an avi file. Or am I missing something?

  2. DirectShow
    This is much more complicated library. You need hundreds of lines of code to see a video preview. But you can obtain this code on MS Docs. Ok, now I have a video preview and I need only to take a photo. One would expect this should be just one function call. But where is the function? I did not find it.

You can simply use GetCurrentImage from IVMRWindowlessControl but this takes only one frame from preview with low resolution. If you set a higher resolution for preview the video is not fluent.

Best approach I could achieve is from an article called "Capturing an Image From a Still Image Pin" available here https://learn.microsoft.com/en-us/windows/desktop/directshow/capturing-an-image-from-a-still-image-pin. When I had found this site I thought I won and my task was almost finished. But it wasn't.

The first advice which the article gives you is not to use it: "The recommended way to get still images from the device is to use the Windows Image Acquisition (WIA) APIs. For more information, see "Windows Image Acquisition" in the Platform SDK documentation. However, you can also use DirectShow to capture an image." I tried to explore the WIA. But this stopped to work on Vista. I continued to study the article.
Everything seems to be clear but you need to implement your class which inherits ISampleGrabberCB marked as deprecated here https://learn.microsoft.com/en-us/windows/desktop/directshow/isamplegrabbercb. Why???? Where to find some alternative?
I found an acceptable solution here https://social.msdn.microsoft.com/Forums/windowsdesktop/en-US/2ab5c212-5824-419d-b5d9-7f5db82f57cd/qedith-missing-in-current-windows-sdk-v70?forum=windowsdirectshowdevelopment. You need to add header file from elder SDK. (BTW This is an advice almost ten years old.) After I compiled the application with this header I was able to read high resolution picture but I need to wait a few seconds which is unacceptable. I know the problem is not in camera, because in the it works in the Camera application. Furthermore the image is obtained in function SampleCB instead of BufferCB and is in some strange format. I can save it as jpg but it is not compressed enough.

  1. Windows Media Foundation
    I think MS doesn't like programmers and that's why it released WMF. I understand nothing. I found this tutorial https://www.dreamincode.net/forums/topic/347938-a-new-webcam-api-tutorial-in-c-for-windows/. It works but it only stores one frame from preview and this is not what I want.
    Next I explored some WMF interfaces on MS Docs. IMFCapturePhotoSink interface should do the stuff. But how implement it. The documentation is useless.

  2. OpenCV
    During my research I found also this library. But again I'm not able to take a high resolution photo. It only stores one frame from preview.

Could someone tell me what should I focus on? I believe it cannot be so difficult. There are tens and hundreds of applications for webcams. How could other programmers implement them? What's wrong with me? I'd like to find an easy way to implement an easy task. Thank a lot for any help.

StackzOfZtuff
  • 2,534
  • 1
  • 28
  • 25
radimoid
  • 199
  • 1
  • 9

2 Answers2

0

You question is not related to the topic - the question must be related to the code - but I faced with the similar problem many years ago and I had found solution:

DirectShow is declared as deprecated for Windows 10 and it has problem with supporting of the USB web cam. In Windows 10 there is USB Video Class which is supported only by Media Foundation.

So, I have wrote a simple C++ wrapper around Media Foundation code which simplify getting of the raw images Capturing Video from Web-camera on Windows 7 and 8 by using Media Foundation

Also, there is project CaptureManager SDK - it is DLL COM component with the simple interfaces, huge functionality and with many demo programs on C++, Python, C#, Java.

StackzOfZtuff
  • 2,534
  • 1
  • 28
  • 25
Evgeny Pereguda
  • 553
  • 1
  • 4
  • 9
  • Thank you for your reply. I am playing with your code now. It works fine for video preview, but I am not able to get any interface for taking photo. – radimoid Oct 14 '18 at 12:44
  • I found IMFCaptureEngine::TakePhoto method on MS Docs https://learn.microsoft.com/cs-cz/windows/desktop/api/mfcaptureengine/nf-mfcaptureengine-imfcaptureengine-takephoto. I need an instance of MFCaptureEngine, but how could I create the MFCaptureEngine object to cooperate with all other stuff? – radimoid Oct 14 '18 at 12:48
  • According to MS Docs, I need to call MFCaptureEngineClassFactory::CreateInstance method. For that I need MFCaptureEngineClassFactory object. But how could I create this object? – radimoid Oct 14 '18 at 12:50
  • Hi, in web cam there is NOT special output for photo. IMFCaptureEngine::TakePhoto has description - Captures a still image from the video stream. - it means that MF opens video stream (sequence of images) from web cam and then extract current video image as photo - my project [Capturing Video from Web-camera on Windows 7 and 8 by using Media Foundation](https://www.codeproject.com/Tips/559437/Capturing-Video-from-Web-camera-on-Windows-and-by) do it. – Evgeny Pereguda Oct 14 '18 at 22:54
  • About `IMFCaptureEngineClassFactory` - object with this interface must be created from [MFCreateCaptureEngine](https://docs.microsoft.com/en-gb/windows/desktop/medfound/mfcreatecaptureengine). However, this function ` is not supported and may be altered or unavailable in the future.` - it needs invoke this function from MFCaptureEngine.dll as a `C` function. Do you know how work with `C` DLL? `This function has no associated import library and is not declared in a public header file. You must use the LoadLibrary and GetProcAddress functions to dynamically link to MFCaptureEngine.dll.` – Evgeny Pereguda Oct 14 '18 at 22:59
  • 1
    About `IMFCaptureEngineClassFactory` - it is possible create object with this interface as COM component. Do you know how work with COM components? [IMFCaptureEngineClassFactory](https://docs.microsoft.com/en-gb/windows/desktop/api/mfcaptureengine/nn-mfcaptureengine-imfcaptureengineclassfactory), [CaptureEngine video capture sample](https://code.msdn.microsoft.com/windowsdesktop/Media-Foundation-Capture-78504c83) - // Create the factory object for the capture engine. hr = CoCreateInstance(CLSID_MFCaptureEngineClassFactory, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pFactory)); – Evgeny Pereguda Oct 14 '18 at 23:48
  • I don't know how it works in WMF, but as I understand in DirectShow there are three pins from the webcam: CAPTURE_PIN, PREVIEW_PIN and STILL_PIN. You can watch preview from the preview pin in low resolution (let's say 640 x 480) and this video is perfectly fluent. Then you trigger a STILL_PIN and get a high resolution photo. But in my case this photo is delayed. – radimoid Oct 15 '18 at 08:03
  • Maybe it is delayed because this is absolutely wrong way how to take photos from webcam and it is simply not possible to do all stuff after triggering in time. I don't know. Considering this I simply tried to take the video in highest resolution directly from preview pin. Then I can really save one frame from the stream as a high resolution photo. But here is another problem. I am not able to display the preview in this high resolution (2592x1944) fluently. And I know it is possible, because the Camera application does it. – radimoid Oct 15 '18 at 08:04
  • I expect that the TakePhoto method could do the job like the STILL PIN in DirectShow but in time. Of course I don|t know because I am not able to run it. – radimoid Oct 15 '18 at 08:06
  • 1
    You try compare two different media technology: DirectShow and media Foundation (Camera application) - they use different code, different drivers and you have got different result. DirectShow is depreciated media technology and it has problem with supporting of the modern devices. You can invoke method IMFCaptureEngine::TakePhoto from object which is created from IMFCaptureEngineClassFactory::CreateInstance from object which is created from CoCreateInstance(CLSID_MFCaptureEngineClassFactory, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pFactory)); – Evgeny Pereguda Oct 15 '18 at 10:42
  • I have run the example application. It does nearly what I want except it takes only low resolution photo. I'll try to explore it more preciselly maybe it will be just one parameter. But for now I can say I need hundreds of lines of code to make almost same job like in VFW in 10 lines. – radimoid Oct 15 '18 at 15:46
  • 1
    Yes, you have got low resolution photo - web cam supports MANY OUTPUT RESOLUTIONS - and code in the example uses the default one - low resolution. If you need photo with maximum resolution - you must write code which enumerates all supported output resolutions by IMFCaptureSource::GetAvailableDeviceMediaType method, take the needed one and set it into hr = pPhoto->AddStream((DWORD)MF_CAPTURE_ENGINE_PREFERRED_SOURCE_STREAM_FOR_PHOTO, pMediaType2, NULL, &dwSinkStreamIndex); as pMediaType2. – Evgeny Pereguda Oct 15 '18 at 22:32
  • `almost same job like in VFW in 10 lines` - if you would like work with VFW - you must work on Windows 98. Media Foundation is powerful and complex SDK - for resolving of this complexity there are C++ wrappers like [Capturing Live-video from Web-camera on Windows 7 and Windows 8](https://www.codeproject.com/Articles/776058/Capturing-Live-video-from-Web-camera-on-Windows-an) and [CaptureManager SDK](https://www.codeproject.com/Articles/1017223/CaptureManager-SDK). – Evgeny Pereguda Oct 15 '18 at 22:46
  • Many, many thanks. It works now. 1/ Download the [CaptureEngine video capture sample](https://code.msdn.microsoft.com/windowsdesktop/Media-Foundation-Capture-78504c83) 2/ Edit CaptureManager::TakePhoto method. Add the code to find highest resolution media type just before "CreatePhotoMediaType(pMediaType, &pMediaType2);" – radimoid Oct 16 '18 at 15:45
  • I'm just curious. Is it possible to implement something like the TakePhoto method into your sample application where is no MFCaptureEngine object. Is seems theese two application solve same problem by completely different way. – radimoid Oct 16 '18 at 16:10
  • Both applications are developed for wide range Windows OSs from 7 to 10. TakePhoto functionality in Windows OS there is since Windows 8 release. – Evgeny Pereguda Oct 17 '18 at 00:11
  • So it is not possible to take high resolution photo using just the interface for Win7 (that meas to use classes and interfaces used in your example)? – radimoid Oct 17 '18 at 08:19
  • I think that it is not possible - Win7 was the first OS with web cam supporting by Media Foundation and It has limitation for supporting of the functionality. – Evgeny Pereguda Oct 17 '18 at 10:56
  • Ok. Thank you very much for your help. – radimoid Oct 17 '18 at 14:23
  • @EvgenyPeregud: You wrote *"DirectShow is declared as deprecated for Windows 10"*. What's your source for that? – StackzOfZtuff Sep 18 '19 at 15:01
0

Thanks to Evgeny.

Recapitulation:

  1. Download the CaptureEngine video capture sample

  2. Edit CaptureManager::TakePhoto method. Add the code to find highest resolution media type just before CreatePhotoMediaType(pMediaType, &pMediaType2); line

Extra code for setup the photo stream to highest resolution:

DWORD dwMediaTypeIndex = 0;
UINT32 maxSize = 0;
DWORD maxSizeIndex = 0;
while (1) {
    IMFMediaType* pMediaType = NULL;
    hr = pSource->GetAvailableDeviceMediaType((DWORD)MF_CAPTURE_ENGINE_PREFERRED_SOURCE_STREAM_FOR_PHOTO, dwMediaTypeIndex, &pMediaType);
    if (hr == MF_E_NO_MORE_TYPES)
        break;

    UINT32 w, h;
    MFGetAttributeSize(pMediaType, MF_MT_FRAME_SIZE, &w, &h);
    UINT32 size = w * h;
    if (size > maxSize) {
        maxSize = size;
        maxSizeIndex = dwMediaTypeIndex;
    }
    SafeRelease(&pMediaType);
    dwMediaTypeIndex++;
}

SafeRelease(&pMediaType);
pSource->GetAvailableDeviceMediaType((DWORD)MF_CAPTURE_ENGINE_PREFERRED_SOURCE_STREAM_FOR_PHOTO, maxSizeIndex, &pMediaType);
StackzOfZtuff
  • 2,534
  • 1
  • 28
  • 25
radimoid
  • 199
  • 1
  • 9