Accessing webcam via DirectShow using COM with Python
Asked Answered
V

2

7

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.

Victoriavictorian answered 14/8, 2018 at 14:11 Comment(7)
This question has answers linking tools that can be used to explore COM objects and their properties like CLSID/IID. I haven't used any of the tools but this may be a good place to start.Bibby
Following up on my previous comment, I explored oleview and if you expand the Interfaces item, you should be able to find the CLSID and IID of any interface available on your system. Be sure to run oleview.exe as an admin for it to work successfully.Bibby
I also had a look but was not able to identify the IID / CLSID needed for webcam access. Will try again with admin rights.Victoriavictorian
I could find IMediaControl in the Interfaces tab. This is the only one I saw in the docs about webcam stuff. This pages states "The WDM Video Capture filter exposes IAMVideoProcAmp". But I have no clue where to start looking and get a foot in the door...Victoriavictorian
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]github.com/Alexpux/mingw-w64/blob/master/mingw-w64-headers/…), noted as the required header for the interface, I found that IID_IAMVideoProcAmp is C6E13360-30AC-11d0-A18C-00A0C9118956Bibby
I have expanded on my comment in an answer that I hope will set you in the right direction to figure this out. I may have time in the next couple days to explore a Python COM library and attempt it myself, but in the meantime if you get it working in Python please add your approach in a new answer :)Bibby
related: stackoverflow.com/a/38608076Keratinize
V
1

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))
Victoriavictorian answered 4/10, 2018 at 13:59 Comment(1)
I am stuck trying to use IFileOperation from ctypes, could you perhaps advise?Ulphiah
B
1

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.

Bibby answered 15/8, 2018 at 10:22 Comment(0)
V
1

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))
Victoriavictorian answered 4/10, 2018 at 13:59 Comment(1)
I am stuck trying to use IFileOperation from ctypes, could you perhaps advise?Ulphiah

© 2022 - 2024 — McMap. All rights reserved.