Skip to main content

duvc-ctl

duvc-ctl is a lightweight Python library for controlling USB Video Class (UVC) camera properties on Windows using the native DirectShow API.
It exposes camera and video properties (PTZ, focus, exposure, brightness, etc.), supports device monitoring with hotplug detection, and provides access to vendor-specific property sets — all without vendor SDKs, extra drivers, or dependencies.

Key Features

  • Simple Pythonic API: Property-based access (cam.brightness = 80) with automatic device management and beginner-friendly error handling
  • Advanced Result-Based API: Explicit error handling with Result types for detailed control in production code
  • Camera & Video Properties: Get/set PTZ controls, exposure, focus, white balance, gain, and other IAMCameraControl/IAMVideoProcAmp properties
  • Device Monitoring: List devices, check connectivity, and handle hotplug events
  • Vendor Extensions: Access custom property sets via GUID
  • Multiple Error Handling: Simple exceptions, Result types, or safe tuple returns
  • Thread-Safe: Safe for use in multi-threaded applications

Works on Windows 7+ with Python 3.8+. Suitable for computer vision, robotics, video streaming, and automation projects requiring precise USB camera control.

Installation

pip install duvc-ctl

Two APIs, One Library

duvc-ctl provides two complementary APIs for different use cases:

Use when: You want simple, readable code with automatic device selection and beginner-friendly errors.

Simple property-based control with context manager support:

import duvc_ctl as duvc

# Connect to first available camera
with duvc.CameraController() as cam:
    cam.brightness = 80        # Simple assignment
    cam.pan += 10              # Relative adjustment
    print(f"Zoom: {cam.zoom}")  # Simple property read
    
    # Reset to defaults
    cam.reset_to_defaults()

Or find a specific camera:

cam = duvc.find_camera("Logitech")
cam.brightness = 80
cam.close()

2. Result-Based API (Advanced)

Use when: You need explicit error handling, detailed diagnostics, or building production systems.

Result types provide detailed error information without exceptions:

import duvc_ctl as duvc

devices = duvc.list_devices()
if not devices:
    print("No cameras found")
else:
    device = devices[0]
    camera_result = duvc.open_camera(device)
    
    if camera_result.is_ok():
        camera = camera_result.value()
        
        # Set property with explicit error checking
        setting = duvc.PropSetting(100, duvc.CamMode.Manual)
        result = camera.set(duvc.CamProp.Pan, setting)
        
        if result.is_ok():
            print("Pan set successfully")
        else:
            error = result.error()
            print(f"Error: {error.description()}")
    else:
        print(f"Failed to open camera: {camera_result.error().description()}")

API Comparison

Feature Pythonic Result-Based
Device selection Automatic Manual
Error handling Exceptions Result types
Code verbosity Minimal Explicit
Learning curve Easy Moderate
Production ready Yes Yes
Diagnostics Basic Detailed

Quick Start — Pythonic API

import duvc_ctl as duvc

# List cameras
cameras = duvc.list_cameras()
print(f"Found {len(cameras)} camera(s): {cameras}")

# Connect and control
with duvc.CameraController() as cam:
    print(f"Connected to: {cam.device_name}")
    
    # Check supported properties
    supported = cam.get_supported_properties()
    print(f"Camera properties: {supported['camera']}")
    print(f"Video properties: {supported['video']}")
    
    # Video properties
    if 'brightness' in supported['video']:
        cam.brightness = 75
        cam.contrast = 60
        print(f"Brightness: {cam.brightness}")
    
    # PTZ properties
    if 'pan' in supported['camera']:
        cam.pan = 0      # Center
        cam.zoom = 100   # 1x zoom

Quick Start — Result-Based API

import duvc_ctl as duvc

devices = duvc.list_devices()
if devices:
    device = devices[0]
    
    # Open camera with explicit error handling
    result = duvc.open_camera(device)
    if result.is_ok():
        camera = result.value()
        
        # Get property with range
        range_result = camera.get_range(duvc.CamProp.Pan)
        if range_result.is_ok():
            pan_range = range_result.value()
            print(f"Pan range: {pan_range.min} to {pan_range.max}")
            
            # Set to center
            center = duvc.PropSetting(0, duvc.CamMode.Manual)
            result = camera.set(duvc.CamProp.Pan, center)

API Reference

Pythonic API — Classes

CameraController

High-level camera control with property-based access.

# Connect to first available camera
cam = duvc.CameraController()

# Connect to specific camera
cam = duvc.CameraController(device_index=1)
cam = duvc.CameraController(device_name="Logitech")

# Use as context manager (recommended)
with duvc.CameraController() as cam:
    cam.brightness = 80
    # Auto-closes camera

# Property access
cam.brightness = 75       # Set
value = cam.brightness    # Get
range_info = cam.get_property_range('brightness')

# Device info
print(cam.device_name)
print(cam.is_connected)

# Cleanup
cam.close()

Pythonic API — Device Discovery

# List camera names
cameras = duvc.list_cameras()

# Find camera by name pattern
cam = duvc.find_camera("Logitech")

# Get camera info
info = duvc.get_camera_info(0)

Result-Based API — Classes

Device

Represents a connected USB camera.

device.name   # Friendly name
device.path   # System path
device.is_valid()

Camera

Opened camera connection with explicit error handling.

result = duvc.open_camera(device)
if result.is_ok():
    camera = result.value()
    
    # All operations return Result types
    get_result = camera.get(duvc.CamProp.Pan)
    set_result = camera.set(duvc.CamProp.Pan, setting)
    range_result = camera.get_range(duvc.CamProp.Pan)

PropSetting

Property value and control mode.

setting = duvc.PropSetting(100, duvc.CamMode.Manual)
auto_setting = duvc.PropSetting(0, duvc.CamMode.Auto)
print(setting.value, setting.mode)

PropRange

Property constraints and defaults.

range_result = camera.get_range(duvc.CamProp.Pan)
if range_result.is_ok():
    prop_range = range_result.value()
    print(prop_range.min, prop_range.max, prop_range.step)

Result Types

# All Result types have the same pattern
result = camera.get(duvc.CamProp.Pan)

if result.is_ok():
    value = result.value()      # Get the value
else:
    error = result.error()      # Get the error
    print(error.description())

Property Enums

CamProp (Camera Properties)

Pan, Tilt, Roll, Zoom, Exposure, Iris, Focus, Privacy, 
ScanMode, Lamp, BacklightCompensation, DigitalZoom

VidProp (Video Properties)

Brightness, Contrast, Hue, Saturation, Sharpness, Gamma,
ColorEnable, WhiteBalance, BacklightCompensation, Gain,
DigitalMultiplier, DigitalMultiplierLimit, WhiteBalanceComponent, PowerLineFrequency

Control Modes

CamMode

  • Auto — Automatic adjustment
  • Manual — Manual control with specific values

Error Types

Pythonic API

Standard Python exceptions:

try:
    cam = duvc.CameraController()
except duvc.DeviceNotFoundError:
    print("No cameras found")
except duvc.PropertyNotSupportedError:
    print("Property not supported")

Result-Based API

Result type checking with no exceptions:

result = duvc.open_camera(device)
if result.is_ok():
    camera = result.value()
else:
    error = result.error()
    code = error.code()           # Get error code
    description = error.description()  # Get description

Exception Types (Both APIs)

  • DuvcError — Base exception for all errors
  • DeviceNotFoundError — Camera disconnected or not found
  • DeviceBusyError — Camera in use by another application
  • PropertyNotSupportedError — Property not supported by camera
  • InvalidValueError — Property value out of range
  • PermissionDeniedError — Insufficient permissions
  • SystemError — Windows/DirectShow system error

Examples

Property Enumeration

with duvc.CameraController() as cam:
    # Get supported properties
    props = cam.get_supported_properties()
    
    print("Camera properties:")
    for prop in props['camera']:
        print(f"  - {prop}")
    
    print("Video properties:")
    for prop in props['video']:
        print(f"  - {prop}")

PTZ Control

with duvc.CameraController() as cam:
    # Absolute positioning
    cam.pan = 0      # Center
    cam.tilt = 0
    
    # Relative movement
    cam.pan_relative(15)   # Move 15 degrees right
    cam.zoom_relative(2)   # Zoom in 2 steps
    
    # Get current values
    print(f"Pan: {cam.pan}°, Tilt: {cam.tilt}°, Zoom: {cam.zoom}")

Preset Configuration

with duvc.CameraController() as cam:
    # Apply built-in preset
    cam.apply_preset('daylight')
    
    # Create custom preset
    config = {
        'brightness': 75,
        'contrast': 60,
        'saturation': 80
    }
    cam.create_custom_preset('my_preset', config)
    
    # Apply custom preset
    cam.apply_preset('my_preset')

Batch Operations

with duvc.CameraController() as cam:
    # Set multiple properties at once
    settings = {
        'brightness': 80,
        'contrast': 65,
        'saturation': 75
    }
    results = cam.set_multiple(settings)
    
    # Get multiple properties at once
    values = cam.get_multiple(['brightness', 'contrast', 'saturation'])

Vendor Properties

import uuid
import duvc_ctl as duvc

device = duvc.list_devices()[0]

# Read vendor property
vendor_guid = uuid.UUID('12345678-1234-5678-9abc-123456789abc')
success, data = duvc.read_vendor_property(device, vendor_guid, 1)
if success:
    print(f"Property data: {data.hex()}")

# Write vendor property
success = duvc.write_vendor_property(device, vendor_guid, 1, b'\\x01\\x02\\x03')

Error Handling Patterns

# Pythonic API - with exceptions
try:
    with duvc.CameraController() as cam:
        cam.brightness = 999  # Out of range
except duvc.InvalidValueError as e:
    print(f"Invalid value: {e}")

# Result-Based API - explicit checking
result = duvc.open_camera(device)
if result.is_ok():
    camera = result.value()
    set_result = camera.set(duvc.CamProp.Pan, setting)
    if not set_result.is_ok():
        print(f"Failed: {set_result.error().description()}")

Requirements

  • Windows 10+ (x64)
  • Python 3.8+
  • No additional drivers or SDKs required

Performance

  • Native C++ backend with DirectShow for optimal performance
  • Thread-safe for concurrent access
  • Minimal overhead — thin Python wrapper over C++ core

Other Interfaces

  • C++ Library — Native API for C++ applications
  • CLI Tool — duvc-cli.exe for scripting and automation

Metadata

Release files for duvc-ctl 2.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for duvc-ctl 2.1.0
File Size Uploaded Provenance Actions
duvc_ctl-2.1.0.tar.gz 55.6 kB With provenance Details for duvc_ctl-2.1.0.tar.gz

Built distributions (wheels)

Table of built distributions (wheels) for duvc-ctl 2.1.0
File Actions
duvc_ctl-2.1.0-cp312-cp312-win_amd64.whl CPython 3.12 CPython 3.12 Windows x86-64 Details for duvc_ctl-2.1.0-cp312-cp312-win_amd64.whl
duvc_ctl-2.1.0-cp311-cp311-win_amd64.whl CPython 3.11 CPython 3.11 Windows x86-64 Details for duvc_ctl-2.1.0-cp311-cp311-win_amd64.whl
duvc_ctl-2.1.0-cp310-cp310-win_amd64.whl CPython 3.10 CPython 3.10 Windows x86-64 Details for duvc_ctl-2.1.0-cp310-cp310-win_amd64.whl
duvc_ctl-2.1.0-cp39-cp39-win_amd64.whl CPython 3.9 CPython 3.9 Windows x86-64 Details for duvc_ctl-2.1.0-cp39-cp39-win_amd64.whl
duvc_ctl-2.1.0-cp38-cp38-win_amd64.whl CPython 3.8 CPython 3.8 Windows x86-64 Details for duvc_ctl-2.1.0-cp38-cp38-win_amd64.whl

Total release size: 6.3 MB

Release files / duvc_ctl-2.1.0.tar.gz

Download URL duvc_ctl-2.1.0.tar.gz
Size 55.6 kB
Tags Source
SHA-256 checksum
How to use checksums
a29405dae537718a927953537ad89efb35addfb625695d80c8362dac46fc4891
BLAKE2b-256 checksum
How to use checksums
d39a1ee83c72ba0954e329c4a93a79c2be8cd4fecf56102d12fa2c6daa848369
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jul 23, 2026.

Transparency log

Release files / duvc_ctl-2.1.0-cp312-cp312-win_amd64.whl

Download URL duvc_ctl-2.1.0-cp312-cp312-win_amd64.whl
Size 1.2 MB
Tags CPython 3.12 Windows x86-64
SHA-256 checksum
How to use checksums
e34a6f81f885f06106edea1886b1c7bc50ce4760df17073f534de53fe1011345
BLAKE2b-256 checksum
How to use checksums
cf439ec11093c0cf2d9bb1e0643d34c4a56c69289cded916537bea4625a86f77
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jul 23, 2026.

Transparency log

Release files / duvc_ctl-2.1.0-cp311-cp311-win_amd64.whl

Download URL duvc_ctl-2.1.0-cp311-cp311-win_amd64.whl
Size 1.2 MB
Tags CPython 3.11 Windows x86-64
SHA-256 checksum
How to use checksums
745b4bc7f3776081132d6dc402cdc0c3afa47fd150c58fcfedf4119a930e4f54
BLAKE2b-256 checksum
How to use checksums
949c1d15402824d0b9ecbe18a32ea1053a08baab7058af22fd20b4ed0c3c3b43
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jul 23, 2026.

Transparency log

Release files / duvc_ctl-2.1.0-cp310-cp310-win_amd64.whl

Download URL duvc_ctl-2.1.0-cp310-cp310-win_amd64.whl
Size 1.2 MB
Tags CPython 3.10 Windows x86-64
SHA-256 checksum
How to use checksums
233875132dce195f10e06a16361cb2599d6bd03734409a3662e46f29edb7a9d2
BLAKE2b-256 checksum
How to use checksums
000e338a7bfc7fc9144a06b45e7033b7a3ebc8807cd58e1d54089eeaec471db1
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jul 23, 2026.

Transparency log

Release files / duvc_ctl-2.1.0-cp39-cp39-win_amd64.whl

Download URL duvc_ctl-2.1.0-cp39-cp39-win_amd64.whl
Size 1.3 MB
Tags CPython 3.9 Windows x86-64
SHA-256 checksum
How to use checksums
9607ff9c86104ced4e79100f3cf3be93fb9662365bbc0297b92574b51d3184e8
BLAKE2b-256 checksum
How to use checksums
da1b9d4310ca788d30474a956c8dffef276c79ce2dca708459e302bd30ec871c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jul 23, 2026.

Transparency log

Release files / duvc_ctl-2.1.0-cp38-cp38-win_amd64.whl

Download URL duvc_ctl-2.1.0-cp38-cp38-win_amd64.whl
Size 1.3 MB
Tags CPython 3.8 Windows x86-64
SHA-256 checksum
How to use checksums
c1c24a71faff158a399b7a6f534865468ab8aec5fa3415a5d2686d88a9616142
BLAKE2b-256 checksum
How to use checksums
74d0b203629dfa72b7535864879b150a3866149b608e302910c484cc9a350c02
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jul 23, 2026.

Transparency log

Security for duvc-ctl 2.1.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 6 files in this release have been uploaded with provenance metadata.

Select from 6 files

Attestation for duvc_ctl-2.1.0.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jul 23, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page