11. Device Support & Compatibility¶
11.1 Tested Devices¶
duvc-ctl works with any UVC-compliant camera on Windows.
The library relies on DirectShow’s UVC support rather than device-specific drivers, providing broad compatibility.
Verified Compatible Brands¶
Logitech (Extended support via vendor extensions):
C920/C922 HD Pro Webcam
C930e Business Webcam
BRIO 4K Ultra HD
StreamCam
PTZ Pro series
Microsoft:
LifeCam HD-3000
LifeCam Studio
Modern Webcam
Generic UVC:
Most USB webcams without proprietary drivers
Conference room PTZ cameras
Document cameras
Industrial machine vision cameras
Professional:
PTZOptics cameras
AVer CAM series
Elgato Facecam
Razer Kiyo series
Property Support Varies¶
Not all cameras implement all UVC properties:
Common capabilities:
Brightness, Contrast, Saturation
Auto/Manual Exposure
Auto/Manual White Balance
Auto Focus (if supported by hardware)
Less common:
Pan/Tilt/Zoom (PTZ cameras only)
Privacy Shutter
Roll
Iris control
Check support:
auto devices = duvc::list_devices();
Camera camera(devices);
// Query which properties are available
std::cout << "Camera: " << duvc::to_utf8(devices.name) << "\n";
for (auto prop : {CamProp::Pan, CamProp::Tilt, CamProp::Zoom,
CamProp::Focus, CamProp::Exposure}) {
auto range = camera.get_range(prop);
if (range.is_ok()) {
std::cout << " " << to_string(prop) << ": "
<< range.value().min << " to " << range.value().max << "\n";
}
}
11.2 Device Capability Matrix¶
Typical property support by camera type:
Property |
Basic Webcam |
PTZ Camera |
High-End Webcam |
Industrial Camera |
|---|---|---|---|---|
Brightness |
✓ |
✓ |
✓ |
✓ |
Contrast |
✓ |
✓ |
✓ |
✓ |
Saturation |
✓ |
✓ |
✓ |
✓ |
Hue |
✓ |
✓ |
✓ |
~ |
Sharpness |
✓ |
✓ |
✓ |
✓ |
Gamma |
~ |
✓ |
✓ |
✓ |
White Balance |
✓ |
✓ |
✓ |
✓ |
Backlight Comp |
~ |
✓ |
✓ |
✓ |
Gain |
~ |
✓ |
✓ |
✓ |
Exposure |
✓ |
✓ |
✓ |
✓ |
Focus |
Auto only |
✓ |
✓ |
✓ |
Zoom |
✗ |
✓ |
~ |
~ |
Pan |
✗ |
✓ |
✗ |
~ |
Tilt |
✗ |
✓ |
✗ |
~ |
Roll |
✗ |
~ |
✗ |
✗ |
Iris |
✗ |
~ |
✗ |
✓ |
Privacy |
✗ |
~ |
~ |
~ |
Legend:
✓ = Commonly supported
~ = Sometimes supported
✗ = Rarely supported
Logitech-Specific Features¶
Vendor extensions via IKsPropertySet:
Feature |
C920 |
C922 |
C930e |
BRIO |
StreamCam |
|---|---|---|---|---|---|
RightLight |
✓ |
✓ |
✓ |
✓ |
✓ |
RightSound |
✗ |
✗ |
✓ |
✓ |
✗ |
Face Tracking |
~ |
~ |
✓ |
✓ |
~ |
LED Control |
✓ |
✓ |
✓ |
✓ |
✓ |
HDR |
✗ |
✗ |
✗ |
✓ |
✗ |
Digital Pan/Tilt |
✗ |
✗ |
✗ |
✓ |
~ |
Usage:
#include <duvc-ctl/vendor/logitech.h>
auto devices = duvc::list_devices();
if (duvc::logitech::supports_logitech_properties(devices).value_or(false)) {
// Enable RightLight auto-exposure
duvc::logitech::set_logitech_property_typed<bool>(
devices, duvc::logitech::LogitechProperty::RightLight, true);
}
Resolution Impact¶
Property availability can vary by resolution:
Some cameras disable certain controls at high resolutions (e.g., 4K) due to bandwidth or processing limits.
Example:
1080p: Full property support
4K: Auto focus only, manual focus disabled
720p: All properties available
This is firmware-dependent and varies by manufacturer.
11.3 Known Issues & Workarounds¶
Issue 1: Property Values Don’t Persist¶
Problem: Some cameras reset properties to defaults when:
Camera is unplugged/replugged
System reboots
Camera app is closed
Cause: Properties stored in volatile firmware memory, not flash.
Workaround:
// Save desired settings
struct CameraSettings {
int exposure;
int brightness;
int white_balance;
};
// Apply on startup
void apply_settings(Camera& camera, const CameraSettings& settings) {
camera.set(CamProp::Exposure, {settings.exposure, CamMode::Manual});
camera.set(VidProp::Brightness, {settings.brightness, CamMode::Manual});
camera.set(VidProp::WhiteBalance, {settings.white_balance, CamMode::Manual});
}
// Call on device connection
duvc::register_device_change_callback([](bool added, const std::string& path) {
if (added) {
// Device connected - reapply settings
auto devices = duvc::list_devices();
Camera camera(devices);
apply_settings(camera, saved_settings);
}
});
Issue 2: Auto Mode Doesn’t Revert to Default¶
Problem: Setting manual value, then switching back to auto may not restore original auto behavior.
Example:
// Original state: Auto exposure
camera.set(CamProp::Exposure, {-5, CamMode::Manual}); // Set manual
camera.set(CamProp::Exposure, {0, CamMode::Auto}); // Back to auto
// Auto behavior may differ from original
Cause: Some cameras remember manual value as “auto starting point.”
Workaround: Query and store default value before modification:
auto original = camera.get(CamProp::Exposure).value();
auto range = camera.get_range(CamProp::Exposure).value();
// Modify
camera.set(CamProp::Exposure, {-5, CamMode::Manual});
// Restore to true default
camera.set(CamProp::Exposure, {range.default_val, range.default_mode});
Issue 3: Property Changes During Streaming¶
Problem: Some cameras ignore property changes while actively streaming video.
Symptom:
Camera camera(0);
camera.set(CamProp::Exposure, {-5, CamMode::Manual}); // Returns success
// But value doesn't change if camera is streaming
auto result = camera.get(CamProp::Exposure); // Still old value
Workaround: Stop streaming, apply properties, restart streaming:
// OpenCV example
cv::VideoCapture cap(0);
cap.release(); // Stop streaming
Camera camera(0);
camera.set(CamProp::Exposure, {-5, CamMode::Manual});
cap.open(0); // Restart with new settings
Issue 4: Incorrect Range Values¶
Problem:
Some cameras report invalid min, max, or step values.
Examples:
Step of 0 (continuous values)
Min > Max (reversed range)
Range includes impossible values
Workaround: Validate and clamp:
auto range_result = camera.get_range(CamProp::Zoom);
if (range_result.is_ok()) {
auto range = range_result.value();
// Validate
if (range.min > range.max) {
std::swap(range.min, range.max);
}
if (range.step <= 0) {
range.step = 1; // Assume step of 1
}
// Safe clamping
int value = range.clamp(user_value);
camera.set(CamProp::Zoom, {value, CamMode::Manual});
}
Issue 5: Slow Property Queries¶
Problem:
Some cameras take a long time to respond to get() or get_range() calls.
Typical duration: 50-200ms per query
Workaround: Cache property ranges:
// Cache on startup
std::map<CamProp, PropRange> cached_ranges;
for (auto prop : {CamProp::Pan, CamProp::Tilt, CamProp::Zoom}) {
auto range = camera.get_range(prop);
if (range.is_ok()) {
cached_ranges[prop] = range.value();
}
}
// Use cached values for validation
if (cached_ranges[CamProp::Pan].is_valid(value)) {
camera.set(CamProp::Pan, {value, CamMode::Manual});
}
Issue 6: DirectShow Exclusive Access¶
Problem: If another application is using DirectShow to control the camera, property changes may fail.
Error: E_FAIL or VFW_E_IN_USE
Conflicting applications:
Windows Camera app
Skype/Teams (if camera preview active)
OBS Studio (with DirectShow source)
Third-party camera control software
Workaround:
auto result = camera.set(CamProp::Exposure, {-5, CamMode::Manual});
if (result.is_error()) {
if (result.error().code() == ErrorCode::DeviceInUse) {
std::cerr << "Camera in use by another application\n";
// Prompt user to close other apps
}
}
Issue 7: USB Power Management¶
Problem: Windows USB selective suspend can reset camera properties.
Symptom: Properties revert to defaults after period of inactivity.
Workaround: Disable USB selective suspend for camera devices:
PowerShell:
# Disable for all USB devices
powercfg /change usb-selective-suspend-setting 0
# Or via Device Manager → USB Hub → Power Management →
# Uncheck "Allow computer to turn off this device"
Programmatic detection:
duvc::register_device_change_callback([](bool added, const std::string& path) {
if (added) {
// Device reconnected (possibly due to power management)
// Reapply settings
}
});
Issue 8: Firmware Bugs¶
Problem: Some cameras have firmware bugs causing:
Invalid property values
Crashes when setting certain combinations
Properties affecting unrelated settings
Example: Setting Zoom on some cameras resets Exposure.
Workaround: Test and document camera-specific quirks:
// Logitech C920 quirk: Setting zoom resets exposure
auto exposure = camera.get(CamProp::Exposure).value();
camera.set(CamProp::Zoom, {100, CamMode::Manual});
camera.set(CamProp::Exposure, exposure); // Restore exposure
Issue 9: Multiple Cameras Same Model¶
Problem: Cameras of same model have identical names, making identification difficult.
Solution: Use device path for unique identification:
auto devices = duvc::list_devices();
for (const auto& device : devices) {
std::wcout << L"Name: " << device.name << L"\n";
std::wcout << L"Path: " << device.path << L"\n\n";
// Path contains unique USB bus/port info
}
// Store path to identify specific camera
std::wstring my_camera_path = devices.path;
// Later, find same camera
for (const auto& device : duvc::list_devices()) {
if (device.path == my_camera_path) {
Camera camera(device);
// This is the correct camera
}
}
Issue 10: No UVC Compliance¶
Problem: Some “webcams” don’t implement UVC properly and require proprietary drivers.
Examples:
Very old webcams (pre-2008)
Some security cameras
Proprietary industrial cameras
Detection:
auto devices = duvc::list_devices();
if (devices.empty()) {
std::cerr << "No UVC cameras found\n";
std::cerr << "Camera may require vendor drivers\n";
}
// Or camera appears but has no properties
Camera camera(0);
auto range = camera.get_range(CamProp::Brightness);
if (range.is_error()) {
std::cerr << "Camera doesn't support standard UVC properties\n";
}
Solution: Use vendor SDK if available, or duvc-ctl won’t work.
Testing New Devices¶
Capability discovery script:
void test_camera_capabilities(const Device& device) {
std::cout << "Testing: " << duvc::to_utf8(device.name) << "\n\n";
Camera camera(device);
// Test camera properties
std::cout << "Camera Properties:\n";
for (auto prop : {CamProp::Pan, CamProp::Tilt, CamProp::Zoom,
CamProp::Exposure, CamProp::Focus, CamProp::Iris}) {
auto range = camera.get_range(prop);
if (range.is_ok()) {
std::cout << " " << to_string(prop) << ": "
<< range.value().min << " to " << range.value().max
<< " (default: " << range.value().default_val << ")\n";
}
}
// Test video properties
std::cout << "\nVideo Properties:\n";
for (auto prop : {VidProp::Brightness, VidProp::Contrast,
VidProp::Saturation, VidProp::WhiteBalance}) {
auto range = camera.get_range(prop);
if (range.is_ok()) {
std::cout << " " << to_string(prop) << ": "
<< range.value().min << " to " << range.value().max << "\n";
}
}
}