Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Accessing webcam via DirectShow using COM with Python

I want to get low level access to webcam properties using DirectShow's IAMVideoProcAmp.

There are several Python modules )pywin32, pywintypes, comtypes, win32com, pythoncom) that are used in this context and they seem to be related somehow. But I have no clue where to start.

I found some examples (here, here, here) but I could not figure out how to get a IID / CLSID to use like

import win32com.client
clsid='{9BA05972-F6A8-11CF-A442-00A0C90A8F39}'
ShellWindows=win32com.client.Dispatch(clsid)

or with a clear name like

import win32com.client
xl = win32com.client.Dispatch("Excel.Application")

or

from comtypes import client, GUID
graph = client.CreateObject(some_CLSID)
graph.QueryInterface(...)

Can someone help me with this?

I found another example (dshow.py), but it has some dependencies that I could not find (interfaces, uuids).

This page from Microsoft lists the procedures as

Call QueryInterface on the capture filter for the IAMVideoProcAmp interface.

or

Query the capture filter for the IAMCameraControl.

and states some C++ code for this:

// Query the capture filter for the IAMVideoProcAmp interface.
IAMVideoProcAmp *pProcAmp = 0;
hr = pCap->QueryInterface(IID_IAMVideoProcAmp, (void**)&pProcAmp);
hr = m_pProcAmp->GetRange(VideoProcAmp_Brightness, &Min, &Max, &Step,
    &Default, &Flags);

Edit: I finally found some code that looks good so far:

jaraco

It seem to do exactly what I am trying to write and uses some elements from DirectShow (see here):

from comtypes.gen.DirectShowLib import (FilterGraph, CaptureGraphBuilder2, ...)

jaraco.video claims to be "a port of the VideoCapture module in pure Python using ctypes and comtypes."

It is using a DirectShow.tlb file (whatever that is) to get the definitions into comtypes

A type library (.tlb) is a binary file that stores information about a COM or DCOM object's properties and methods in a form that is accessible to other applications at runtime.

like image 614
Joe Avatar asked Aug 14 '18 14:08

Joe


2 Answers

Identifying required values to replicate the code

At a second glance at the code excerpt at the end of your post, I realize that you only need the IID and not the CLSID for IAMVideoProcAmp in order to acquire an instance of it.

Looking at line 8733 of this source of strmif.h, noted as the required header for the interface, I found that IID_IAMVideoProcAmp is C6E13360-30AC-11d0-A18C-00A0C9118956.

Above this section of strmif.h, you can identify what integers correspond to which properties in the tagVideoProcAmpProperty enum, such as 0 for VideoProcAmp_Brightness. Below this section of strmif.h, you can identify what integers correspond to which functions in the IAMVideoProcAmpVtbl VTable, such as 3 for GetRange. I am unfamiliar with how to interact with COM objects in Python, but in Java you would need to determine these property and function indices in order to replicate the C++ code excerpts that demonstrate how to acquire an instance of IAmVideoProcAmp.

Acquiring an instance of IAMVideoProcAmp

As you may have noticed, the C++ code excerpt invokes QueryInterface on something called pCap and notes that you need to "Query the capture filter for the IAMVideoProcAmp interface." This sibling of the article you linked explains how to do this:

To create a DirectShow capture filter for the device, call the IMoniker::BindToObject method to get an IBaseFilter pointer. Then call IFilterGraph::AddFilter to add the filter to the filter graph:

IBaseFilter *pCap = NULL;
hr = pMoniker->BindToObject(0, 0, IID_IBaseFilter, (void**)&pCap);
if (SUCCEEDED(hr))
{
    hr = m_pGraph->AddFilter(pCap, L"Capture Filter");
}

Now that you know how to acquire pCap, you notice you need something called pMoniker, which is defined earlier in the same article. The code is fairly long so I omit it here.

Doing all of this in Python

As I noted earlier, I have never used any Python COM library, so I cannot easily whip up an example, but your goal should be to replicate in Python the function invocations made in the C++ examples to acquire an instance of IAMVideoProcAmp and modifying them to suit your needs.

like image 200
FThompson Avatar answered Nov 11 '22 14:11

FThompson


I finally found some example library that is working:

jaraco

It does exactly what I am trying to achive and uses some elements from DirectShow (see here):

from comtypes.gen.DirectShowLib import (FilterGraph, CaptureGraphBuilder2, ...)

jaraco.video claims to be "a port of the VideoCapture module in pure Python using ctypes and comtypes."

It is using a DirectShow.tlb file (whatever that is) to get the definitions into comtypes

A type library (.tlb) is a binary file that stores information about a COM or DCOM object's properties and methods in a form that is accessible to other applications at runtime.

The imports are auto-generated in __init__.py and can be used easily:

from api.objects import ..., IMediaControl, IAMVideoProcAmp, IAMCameraControl, ...

and can be used

def _get_camera_control(self):
    return self._get_graph_builder_interface(IAMCameraControl)

def get_camera_control_property(self, i):

    video_properties = self._get_camera_control()
    return video_properties.Get(i)

Then you can use the functions in combination with the enum stated in the docs, e.g.

# CameraControl_Exposure = 4
print(d.get_camera_control_property(4))
like image 20
Joe Avatar answered Nov 11 '22 14:11

Joe