Skip to content

Instantly share code, notes, and snippets.

@debakarr
Last active March 6, 2024 04:23
Show Gist options
  • Save debakarr/6cd22fe5bf980ae8fa600d46b082af71 to your computer and use it in GitHub Desktop.
Save debakarr/6cd22fe5bf980ae8fa600d46b082af71 to your computer and use it in GitHub Desktop.
Windows studio access using API in Python
import asyncio
import uuid
from ctypes import c_uint32, c_uint64, c_char, Structure, sizeof
from enum import Enum
from winsdk.windows.media.devices import MediaDevice
from winsdk.windows.devices.enumeration import DeviceInformation, Panel
from winsdk.windows.media.capture import (
MediaCapture,
MediaCaptureInitializationSettings,
MediaCaptureSharingMode,
MediaCaptureMemoryPreference,
StreamingCaptureMode,
)
from winsdk.windows.media.capture.frames import MediaFrameSourceGroup
import winsdk.windows.foundation as wf
# GUID equivalent
KSPROPERTYSETID_ExtendedCameraControl = uuid.UUID(
"1CB79112-C0D2-4213-9CA6-CD4FDB927972"
)
KSCAMERA_EXTENDEDPROP_FILTERSCOPE = 0xFFFFFFFF
DEVPKEY_DeviceInterface_IsWindowsCameraEffectAvailable = (
"{6EDC630D-C0D2-4213-9CA6-CD4FDB927972} 4"
)
# Enums
class KsPropertyKind(Enum):
KSPROPERTY_TYPE_GET = 0x00000001
KSPROPERTY_TYPE_SET = 0x00000002
# Structures
class KSPROPERTY(Structure):
_fields_ = [("Set", c_char * 16), ("Id", c_uint32), ("Flags", c_uint32)]
class KSCAMERA_EXTENDEDPROP_HEADER(Structure):
_fields_ = [
("Version", c_uint32),
("PinId", c_uint32),
("Size", c_uint32),
("Result", c_uint32),
("Flags", c_uint64),
("Capability", c_uint64),
]
class KSCAMERA_EXTENDEDPROP_VALUE(Structure):
_fields_ = [("Value", c_uint64)]
class KsBasicCameraExtendedPropPayload(Structure):
_fields_ = [
("header", KSCAMERA_EXTENDEDPROP_HEADER),
("value", KSCAMERA_EXTENDEDPROP_VALUE),
]
# Helper methods for serialization and deserialization
def to_bytes(item):
return bytearray(item)
def get_extended_camera_control_payload(controller, control_id):
prop = KSPROPERTY(
KSPROPERTYSETID_ExtendedCameraControl.bytes_le,
control_id,
KsPropertyKind.KSPROPERTY_TYPE_GET.value,
)
serialized_prop = to_bytes(prop)
return controller.get_device_property_by_extended_id(serialized_prop, None)
def set_extended_camera_control_payload(controller, control_id, flag):
prop = KSPROPERTY(
KSPROPERTYSETID_ExtendedCameraControl.bytes_le,
control_id,
KsPropertyKind.KSPROPERTY_TYPE_SET.value,
)
serialized_prop = to_bytes(prop)
payload = KsBasicCameraExtendedPropPayload()
payload.header.Flags = flag
payload.header.Capability = 0
payload.header.Size = sizeof(KsBasicCameraExtendedPropPayload)
payload.header.Version = 1
payload.header.PinId = KSCAMERA_EXTENDEDPROP_FILTERSCOPE
payload.value.Value = 0
serialized_payload = to_bytes(payload)
return controller.set_device_property_by_extended_id(
serialized_prop, serialized_payload
)
async def get_device_supporting_windows_studio():
devices = await DeviceInformation.find_all_async(
MediaDevice.get_video_capture_selector(),
[DEVPKEY_DeviceInterface_IsWindowsCameraEffectAvailable],
)
selected_device = next(
(
device
for device in devices
if device.enclosure_location.panel == Panel.FRONT
),
None,
)
source_groups = await MediaFrameSourceGroup.find_all_async()
selected_source_group = next(
(
source_group
for source_group in source_groups
if source_group.id == selected_device.id
),
None,
)
return selected_device, selected_source_group
async def get_media_control(device_id, source_group):
capture_settings = MediaCaptureInitializationSettings()
capture_settings.source_group = source_group
capture_settings.video_device_id = device_id
capture_settings.memory_preference = MediaCaptureMemoryPreference.CPU
capture_settings.streaming_capture_mode = StreamingCaptureMode.VIDEO
capture_settings.sharing_mode = MediaCaptureSharingMode.EXCLUSIVE_CONTROL
media_capture = MediaCapture()
await media_capture.initialize_async(capture_settings)
return media_capture
async def main():
(
selected_device,
selected_source_group,
) = await get_device_supporting_windows_studio()
print(f"Device id selected: {selected_device.id}")
media_control = await get_media_control(selected_device.id, selected_source_group)
print(f"Media control selected: {media_control}")
payload = get_extended_camera_control_payload(
media_control.video_device_controller, 40
)
x = wf.IPropertyValue._from(payload.value)
o = KsBasicCameraExtendedPropPayload.from_buffer(x.get_uint8_array())
current_eye_gaze_status = "Enabled" if o.header.Flags == 1 else "Disabled"
print(f"Current eye gaze status: {current_eye_gaze_status}")
payload = get_extended_camera_control = get_extended_camera_control_payload(
media_control.video_device_controller, 41
)
x = wf.IPropertyValue._from(payload.value)
o = KsBasicCameraExtendedPropPayload.from_buffer(x.get_uint8_array())
current_background_blur_status = "Enabled" if o.header.Flags == 1 else "Disabled"
print(f"Current background blur status: {current_background_blur_status}")
payload = get_extended_camera_control_payload(
media_control.video_device_controller, 43
)
x = wf.IPropertyValue._from(payload.value)
o = KsBasicCameraExtendedPropPayload.from_buffer(x.get_uint8_array())
current_automatic_framming_status = "Enabled" if o.header.Flags == 1 else "Disabled"
print(f"Current automatic framing status: {current_automatic_framming_status}")
input("Press enter to enable standard background blur...")
result = set_extended_camera_control_payload(
media_control.video_device_controller, 41, 1
)
input("Press enter to enable portrait background blur...")
result = set_extended_camera_control_payload(
media_control.video_device_controller, 41, 0
)
input("Press enter to disable background blur...")
result = set_extended_camera_control_payload(
media_control.video_device_controller, 41, 0
)
input("Press enter to enable automatic framing...")
result = set_extended_camera_control_payload(
media_control.video_device_controller, 43, 1
)
input("Press enter to disable automatic framing...")
result = set_extended_camera_control_payload(
media_control.video_device_controller, 43, 0
)
if __name__ == "__main__":
asyncio.run(main())
import uuid
from ctypes import *
from enum import Enum
# GUID equivalent
KSPROPERTYSETID_ExtendedCameraControl = uuid.UUID("1CB79112-C0D2-4213-9CA6-CD4FDB927972")
KSCAMERA_EXTENDEDPROP_FILTERSCOPE = 0xFFFFFFFF
# Enums
class ExtendedControlKind(Enum):
KSPROPERTY_CAMERACONTROL_EXTENDED_EYEGAZECORRECTION = 40
KSPROPERTY_CAMERACONTROL_EXTENDED_BACKGROUNDSEGMENTATION = 41
KSPROPERTY_CAMERACONTROL_EXTENDED_DIGITALWINDOW = 43
class BackgroundSegmentationCapabilityKind(Enum):
KSCAMERA_EXTENDEDPROP_BACKGROUNDSEGMENTATION_OFF = 0
KSCAMERA_EXTENDEDPROP_BACKGROUNDSEGMENTATION_BLUR = 1
KSCAMERA_EXTENDEDPROP_BACKGROUNDSEGMENTATION_MASK = 2
KSCAMERA_EXTENDEDPROP_BACKGROUNDSEGMENTATION_SHALLOWFOCUS = 4
class EyeGazeCorrectionCapabilityKind(Enum):
KSCAMERA_EXTENDEDPROP_EYEGAZECORRECTION_OFF = 0
KSCAMERA_EXTENDEDPROP_EYEGAZECORRECTION_ON = 1
KSCAMERA_EXTENDEDPROP_EYEGAZECORRECTION_STARE = 2
class KsPropertyKind(Enum):
KSPROPERTY_TYPE_GET = 0x00000001
KSPROPERTY_TYPE_SET = 0x00000002
KSPROPERTY_TYPE_TOPOLOGY = 0x10000000
# Structures
class KsProperty(Structure):
_fields_ = [("Set", c_ubyte * 16),
("Id", c_uint32),
("Flags", c_uint32)]
def __init__(self, set, id=0, flags=0):
super().__init__()
self.Set = set
self.Id = id
self.Flags = flags
class KSCAMERA_EXTENDEDPROP_HEADER(Structure):
_fields_ = [("Version", c_uint32),
("PinId", c_uint32),
("Size", c_uint32),
("Result", c_uint32),
("Flags", c_uint64),
("Capability", c_uint64)]
class KSCAMERA_EXTENDEDPROP_VALUE(Structure):
_fields_ = [("Value", c_uint64)]
class KsBasicCameraExtendedPropPayload(Structure):
_fields_ = [("header", KSCAMERA_EXTENDEDPROP_HEADER),
("value", KSCAMERA_EXTENDEDPROP_VALUE)]
class KSCAMERA_EXTENDEDPROP_BACKGROUNDSEGMENTATION_CONFIGCAPS(Structure):
_fields_ = [("MaskResolutionX", c_int32),
("MaskResolutionY", c_int32),
("MaxFrameRateNumerator", c_int32),
("MaxFrameRateDenominator", c_int32),
("ResolutionX", c_int32),
("ResolutionY", c_int32),
("SubType", c_ubyte * 16)]
class KsBackgroundCameraExtendedPropPayload(Structure):
_fields_ = [("header", KSCAMERA_EXTENDEDPROP_HEADER),
("value", POINTER(KSCAMERA_EXTENDEDPROP_BACKGROUNDSEGMENTATION_CONFIGCAPS))]
# Helper methods for serialization and deserialization
def to_bytes(item):
return bytearray(item)
def from_bytes(bytes, start_index=0):
return bytes[start_index:]
def set_extended_control_flags(controller, control_kind, flags):
# Create a KsProperty for the specified extended control
ks_prop = KsProperty(
KSPROPERTYSETID_ExtendedCameraControl.bytes,
control_kind.value,
KsProperty.KsPropertyKind.KSPROPERTY_TYPE_SET.value
)
byte_ks_prop = to_bytes(ks_prop)
# Create a payload for the specified extended control that includes the specified flags value
payload = KsBasicCameraExtendedPropPayload()
payload.header.Flags = flags
payload.header.Capability = 0
payload.header.Size = sizeof(KsBasicCameraExtendedPropPayload)
payload.header.Version = 1
payload.header.PinId = KSCAMERA_EXTENDEDPROP_FILTERSCOPE
payload.value.Value = 0
byte_payload = to_bytes(payload)
# Send a SET command with the KsProperty and payload and retrieve the result status
result_status = controller.set_device_property_by_extended_id(byte_ks_prop, byte_payload)
if result_status != "Success": # Assuming "Success" is a possible return value
raise Exception(f"Unexpectedly could not SET flags={flags} for control={control_kind}, status={result_status}")
def get_extended_control_payload(controller, control_kind):
set_bytes = (c_ubyte * 16).from_buffer_copy(KSPROPERTYSETID_ExtendedCameraControl.bytes)
# Create a KsProperty for the specified extended control
ks_prop = KsProperty(
set_bytes,
control_kind.value,
KsPropertyKind.KSPROPERTY_TYPE_GET.value
)
byte_payload = to_bytes(ks_prop)
# Send a GET command with the KsProperty and retrieve the result
result_payload = controller.get_device_property_by_extended_id(byte_payload, None)
if result_payload is None:
raise Exception(f"Unexpectedly could not GET payload for control={control_kind}, null payload")
# Assuming result_payload has a 'Status' attribute and a 'Value' attribute
breakpoint()
if result_payload.status != "Success": # Assuming "Success" is a possible return value
raise Exception(f"Unexpectedly could not GET payload for control={control_kind}, status={result_payload.Status}")
return result_payload.Value
from winsdk.windows.devices.enumeration import DeviceInformation
from winsdk.windows.media.devices import MediaDevice
from winsdk.windows.devices.enumeration import Panel
from winsdk.windows.media.capture import MediaCaptureMemoryPreference
from winsdk.windows.media.capture import StreamingCaptureMode
from winsdk.windows.media.capture import MediaCaptureSharingMode
from winsdk.windows.media.capture import MediaCaptureInitializationSettings
from winsdk.windows.media.capture import MediaCapture, MediaStreamType, MediaCaptureDeviceExclusiveControlReleaseMode
from winsdk.windows.media.capture.frames import MediaFrameSourceKind, MediaFrameSourceGroup
import winsdk.windows.foundation as wf
from ks_helper import KsBasicCameraExtendedPropPayload, to_bytes, KSCAMERA_EXTENDEDPROP_HEADER, KSCAMERA_EXTENDEDPROP_FILTERSCOPE, KSCAMERA_EXTENDEDPROP_VALUE
import asyncio
import uuid
from ctypes import c_uint32
import ctypes
from ctypes import wintypes
KSPROPERTYSETID_ExtendedCameraControl = uuid.UUID('1CB79112-C0D2-4213-9CA6-CD4FDB927972')
DEVPKEY_DeviceInterface_IsWindowsCameraEffectAvailable = "{6EDC630D-C2E3-43B7-B2D1-20525A1AF120} 4"
# Define the KSPROPERTY structure
class KSPROPERTY(ctypes.Structure):
_fields_ = [
('Set', ctypes.c_char * 16), # GUID is represented as a 16-byte array
('Id', ctypes.c_ulong),
('Flags', ctypes.c_ulong)
]
def get_extended_camera_control_payload(controller, control_id):
# Initialize the KSPROPERTY structure
prop = KSPROPERTY()
prop.Set = KSPROPERTYSETID_ExtendedCameraControl.bytes_le
prop.Id = control_id
prop.Flags = 0x00000001 # KSPROPERTY_TYPE_GET
# Serialize to byte buffer
serialized_prop = ctypes.string_at(ctypes.byref(prop), ctypes.sizeof(prop))
# Send the GET command
# Note: This part is highly dependent on the specific COM object and method you're using.
# The following is a placeholder for the actual call.
get_result = controller.get_device_property_by_extended_id(serialized_prop, None)
return get_result
def create_camera_payload(flags):
# Initialize the KSCAMERA_EXTENDEDPROP_HEADER structure
header = KSCAMERA_EXTENDEDPROP_HEADER()
header.Flags = flags
header.Capability = 0
header.Size = ctypes.sizeof(KsBasicCameraExtendedPropPayload)
header.Version = 1
header.PinId = KSCAMERA_EXTENDEDPROP_FILTERSCOPE
# Initialize the KSCAMERA_EXTENDEDPROP_VALUE structure
value = KSCAMERA_EXTENDEDPROP_VALUE()
value.Value = 0
# Combine the header and value into a single payload
payload = KsBasicCameraExtendedPropPayload()
payload.header = header
payload.value = value
return payload
def set_extended_camera_control_payload(controller, control_id, flag):
# Initialize the KSPROPERTY structure
prop = KSPROPERTY()
prop.Set = KSPROPERTYSETID_ExtendedCameraControl.bytes_le
prop.Id = control_id
prop.Flags = 0x00000002 # KSPROPERTY_TYPE_SET
# Serialize to byte buffer
serialized_prop = ctypes.string_at(ctypes.byref(prop), ctypes.sizeof(prop))
payload = create_camera_payload(flag)
serialized_payload = ctypes.string_at(ctypes.byref(payload), ctypes.sizeof(payload))
# Send the SET command
# Note: This part is highly dependent on the specific COM object and method you're using.
# The following is a placeholder for the actual call.
get_result = controller.set_device_property_by_extended_id(serialized_prop, serialized_payload)
return get_result
async def get_device_supporting_windows_studio():
devices = await DeviceInformation.find_all_async(MediaDevice.get_video_capture_selector(), [DEVPKEY_DeviceInterface_IsWindowsCameraEffectAvailable])
selected_device = next((device for device in devices if device.enclosure_location.panel == Panel.FRONT), None)
source_groups = await MediaFrameSourceGroup.find_all_async()
selected_source_group = next((source_group for source_group in source_groups if source_group.id == selected_device.id), None)
return selected_device, selected_source_group
async def get_media_control(device_id, source_group):
capture_settings = MediaCaptureInitializationSettings()
capture_settings.source_group = source_group
capture_settings.video_device_id = device_id
capture_settings.memory_preference = MediaCaptureMemoryPreference.CPU
capture_settings.streaming_capture_mode = StreamingCaptureMode.VIDEO
capture_settings.sharing_mode = MediaCaptureSharingMode.EXCLUSIVE_CONTROL
media_capture = MediaCapture()
await media_capture.initialize_async(capture_settings)
return media_capture
async def main():
selected_device, selected_source_group = await get_device_supporting_windows_studio()
print(f"Device id selected: {selected_device.id}")
media_control = await get_media_control(selected_device.id, selected_source_group)
print(f"Media control selected: {media_control}")
payload = get_extended_camera_control_payload(media_control.video_device_controller, 40)
x = wf.IPropertyValue._from(payload.value)
o = KsBasicCameraExtendedPropPayload.from_buffer(x.get_uint8_array())
current_eye_gaze_status = "Enabled" if o.header.Flags == 1 else "Disabled"
print(f"Current eye gaze status: {current_eye_gaze_status}")
payload = get_extended_camera_control_payload(media_control.video_device_controller, 41)
x = wf.IPropertyValue._from(payload.value)
o = KsBasicCameraExtendedPropPayload.from_buffer(x.get_uint8_array())
current_background_blur_status = "Enabled" if o.header.Flags == 1 else "Disabled"
print(f"Current background blur status: {current_background_blur_status}")
payload = get_extended_camera_control_payload(media_control.video_device_controller, 43)
x = wf.IPropertyValue._from(payload.value)
o = KsBasicCameraExtendedPropPayload.from_buffer(x.get_uint8_array())
current_automatic_framming_status = "Enabled" if o.header.Flags == 1 else "Disabled"
print(f"Current automatic framing status: {current_automatic_framming_status}")
control_acquired = media_control.video_device_controller.try_acquire_exclusive_control(selected_device.id, MediaCaptureDeviceExclusiveControlReleaseMode.ON_DISPOSE)
print(f"{control_acquired = }")
input("Press enter to enable background blur...")
result = set_extended_camera_control_payload(media_control.video_device_controller, 41, 1)
print(result)
# input("Press enter to disable background blur...")
# result = set_extended_camera_control_payload(media_control.video_device_controller, 41, 0)
# print(result)
if __name__ == "__main__":
asyncio.run(main())
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment