12. Troubleshooting & Debugging¶
12.1 Common Issues¶
Quick reference for frequent problems and solutions.
Issue: No Cameras Detected¶
Symptom:
auto devices = duvc::list_devices();
std::cout << "Found " << devices.size() << " cameras\n"; // Prints: Found 0 cameras
Possible causes:
Camera not UVC-compliant:
// Check Device Manager → Imaging devices
// If camera appears under "Other devices" or requires vendor driver, it's not UVC
DirectShow not available:
Windows N/KN editions lack Media Feature Pack
Install: Settings → Apps → Optional features → Media Feature Pack
Antivirus blocking:
Check Windows Security → Camera privacy settings
Ensure application has camera permission
USB connection issues:
Try different USB port
Check USB selective suspend settings
Verify camera works in Windows Camera app
Issue: Property Operations Fail¶
Symptom:
auto result = camera.set(CamProp::Exposure, {-5, CamMode::Manual});
if (result.is_error()) {
std::cout << result.error().description() << std::endl;
// "Property not supported" or "Operation failed"
}
Check property support:
auto range_result = camera.get_range(CamProp::Exposure);
if (range_result.is_error()) {
std::cout << "Exposure not supported by this camera\n";
}
Device in use by another application:
// Close: Windows Camera, Skype, Teams, OBS, Zoom, etc.
// Then retry operation
Camera needs reinitialization:
// Unplug and replug camera
// Or use device monitor to detect reconnection
Issue: COM Initialization Failed¶
Symptom:
Runtime error: COM initialization failed
Cause: COM already initialized with incompatible threading model.
Solution:
// Let duvc-ctl manage COM automatically
Camera camera(0); // Works
// If you must initialize COM yourself:
HRESULT hr = CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED);
// duvc-ctl will detect and use existing COM initialization
Issue: Crashes or Access Violations¶
Common causes:
Using camera object after device disconnect:
Camera camera(0);
// ... camera unplugged ...
camera.set(CamProp::Pan, {0, CamMode::Manual}); // CRASH
// Fix: Always check validity
if (!camera.is_valid()) {
std::cerr << "Camera disconnected\n";
return;
}
Thread safety violation:
// WRONG: Passing camera between threads
Camera camera(0);
std::thread t([&camera]() {
camera.set(CamProp::Exposure, {-5, CamMode::Manual}); // CRASH
});
// CORRECT: Create camera in worker thread
std::thread t([]() {
Camera camera(0); // COM initialized in this thread
camera.set(CamProp::Exposure, {-5, CamMode::Manual});
});
Issue: Slow Performance¶
Symptom: Operations take several hundred milliseconds.
Expected timing: Property queries can take 50-200ms on some cameras.
Optimization:
// Cache property ranges
std::map<CamProp, PropRange> ranges;
for (auto prop : {CamProp::Pan, CamProp::Tilt}) {
auto range = camera.get_range(prop);
if (range.is_ok()) {
ranges[prop] = range.value();
}
}
// Use cached values for validation
if (ranges[CamProp::Pan].is_valid(value)) {
camera.set(CamProp::Pan, {value, CamMode::Manual});
}
Issue: Property Values Incorrect¶
Symptom: get() returns unexpected value after set().
Cameras may clamp to nearest valid value:
camera.set(CamProp::Brightness, {127, CamMode::Manual});
auto result = camera.get(CamProp::Brightness);
std::cout << result.value().value << std::endl; // May print 128 (step size)
Check property range:
auto range = camera.get_range(CamProp::Brightness);
if (range.is_ok()) {
std::cout << "Step: " << range.value().step << std::endl;
// Use step-aligned value
int aligned = (value / range.value().step) * range.value().step;
}
Issue: DLL Load Failures (Python/C API)¶
Symptom:
ImportError: DLL load failed: The specified module could not be found.
Solutions:
Install Visual C++ Redistributable:
Install and restart
Check DLL dependencies:
# Use Dependencies.exe or dumpbin
dumpbin /dependents duvc-core.dll
Python-specific:
# Ensure correct Python version
python --version # Should be 3.8+
# Reinstall package
pip uninstall duvc-ctl
pip install duvc-ctl --force-reinstall
12.2 Debugging Techniques¶
Enable Verbose Logging¶
Enable full debug output:
#include <duvc-ctl/utils/logging.h>
// Set log level before any operations
duvc::set_log_level(duvc::LogLevel::Debug);
// Set custom log handler
duvc::set_log_callback([](duvc::LogLevel level, const std::string& msg) {
std::cout << "[" << duvc::to_string(level) << "] " << msg << std::endl;
});
// Now all operations log detailed information
auto devices = duvc::list_devices();
Camera camera(devices);
auto result = camera.set(CamProp::Exposure, {-5, CamMode::Manual});
Output:
[Debug] Initializing COM
[Debug] Enumerating video input devices
[Info] Found 2 camera(s)
[Debug] Creating connection to device: Logitech C920
[Debug] Querying IAMCameraControl interface
[Debug] Setting CamProp::Exposure to -5 (Manual)
[Debug] DirectShow Set() returned 0x00000000 (S_OK)
Inspect HRESULT Error Codes¶
Decode Windows error codes:
#include <duvc-ctl/utils/error_decoder.h>
auto result = camera.set(CamProp::Pan, {0, CamMode::Manual});
if (result.is_error()) {
std::cout << "Error: " << result.error().description() << std::endl;
// Get detailed DirectShow error
#ifdef _WIN32
HRESULT hr = /* extract from error */;
std::string details = duvc::decode_hresult(hr);
std::cout << "HRESULT: " << details << std::endl;
// Check error category
if (duvc::is_device_error(hr)) {
std::cout << "Device-specific error\n";
}
if (duvc::is_permission_error(hr)) {
std::cout << "Permission denied\n";
}
#endif
}
Common HRESULT values:
HRESULT |
Code |
Meaning |
|---|---|---|
|
|
Success |
|
|
Unspecified failure |
|
|
Invalid argument |
|
|
Property not supported |
|
|
Filter not connected |
Device Capability Inspection¶
Complete capability dump:
void dump_camera_capabilities(const Device& device) {
std::cout << "=== Camera Capabilities ===" << std::endl;
std::cout << "Name: " << duvc::to_utf8(device.name) << std::endl;
std::cout << "Path: " << duvc::to_utf8(device.path) << std::endl << std::endl;
Camera camera(device);
// Camera properties
std::cout << "Camera Properties (IAMCameraControl):" << std::endl;
std::vector<CamProp> cam_props = {
CamProp::Pan, CamProp::Tilt, CamProp::Roll, CamProp::Zoom,
CamProp::Exposure, CamProp::Iris, CamProp::Focus,
CamProp::Privacy, CamProp::BacklightCompensation
};
for (auto prop : cam_props) {
auto range = camera.get_range(prop);
if (range.is_ok()) {
const auto& r = range.value();
std::cout << " " << to_string(prop) << ":" << std::endl;
std::cout << " Range: [" << r.min << ", " << r.max << "]" << std::endl;
std::cout << " Step: " << r.step << std::endl;
std::cout << " Default: " << r.default_val
<< " (" << to_string(r.default_mode) << ")" << std::endl;
// Current value
auto current = camera.get(prop);
if (current.is_ok()) {
std::cout << " Current: " << current.value().value
<< " (" << to_string(current.value().mode) << ")" << std::endl;
}
}
}
// Video properties
std::cout << "\nVideo Properties (IAMVideoProcAmp):" << std::endl;
std::vector<VidProp> vid_props = {
VidProp::Brightness, VidProp::Contrast, VidProp::Hue,
VidProp::Saturation, VidProp::Sharpness, VidProp::Gamma,
VidProp::WhiteBalance, VidProp::BacklightCompensation, VidProp::Gain
};
for (auto prop : vid_props) {
auto range = camera.get_range(prop);
if (range.is_ok()) {
const auto& r = range.value();
std::cout << " " << to_string(prop) << ": ["
<< r.min << ", " << r.max << "], step=" << r.step << std::endl;
}
}
}
Monitor Device Events¶
Track connect/disconnect:
#include <duvc-ctl/platform/device_monitor.h>
void monitor_devices() {
auto callback = [](bool added, const std::wstring& path) {
if (added) {
std::wcout << L"Camera connected: " << path << std::endl;
// Reapply settings
auto devices = duvc::list_devices();
for (const auto& dev : devices) {
if (dev.path == path) {
Camera camera(dev);
// Apply saved settings
break;
}
}
} else {
std::wcout << L"Camera disconnected: " << path << std::endl;
}
};
duvc::register_device_change_callback(callback);
// Keep monitoring (blocking or in separate thread)
std::cout << "Monitoring for device changes... Press Enter to stop" << std::endl;
std::cin.get();
}
Step-by-Step Operation Trace¶
Trace each DirectShow call:
// Enable maximum verbosity
duvc::set_log_level(duvc::LogLevel::Debug);
std::cout << "Step 1: Initialize platform" << std::endl;
auto platform = duvc::create_platform_interface();
std::cout << "Step 2: List devices" << std::endl;
auto devices_result = platform->list_devices();
if (!devices_result.is_ok()) {
std::cout << "Failed: " << devices_result.error().description() << std::endl;
return;
}
std::cout << "Step 3: Create connection" << std::endl;
auto connection_result = platform->create_connection(devices_result.value());
if (!connection_result.is_ok()) {
std::cout << "Failed: " << connection_result.error().description() << std::endl;
return;
}
auto connection = std::move(connection_result.value());
std::cout << "Step 4: Query property range" << std::endl;
auto range_result = connection->get_camera_property_range(CamProp::Pan);
if (!range_result.is_ok()) {
std::cout << "Failed: " << range_result.error().description() << std::endl;
return;
}
std::cout << "Step 5: Set property" << std::endl;
PropSetting setting{0, CamMode::Manual};
auto set_result = connection->set_camera_property(CamProp::Pan, setting);
if (!set_result.is_ok()) {
std::cout << "Failed: " << set_result.error().description() << std::endl;
return;
}
std::cout << "Success!" << std::endl;
12.3 Diagnostic Tools¶
Built-in Diagnostic Script¶
Create comprehensive diagnostic report:
#include <duvc-ctl/duvc.hpp>
#include <duvc-ctl/utils/logging.h>
#include <duvc-ctl/utils/error_decoder.h>
#include <fstream>
void generate_diagnostic_report(const std::string& filename) {
std::ofstream report(filename);
// System information
report << "=== System Diagnostics ===" << std::endl;
report << "Operating System: Windows" << std::endl;
report << "duvc-ctl Version: " << DUVC_VERSION << std::endl;
report << std::endl;
// COM initialization test
report << "=== COM Initialization ===" << std::endl;
HRESULT hr = CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED);
if (SUCCEEDED(hr)) {
report << "Status: OK" << std::endl;
CoUninitialize();
} else {
report << "Status: FAILED (0x" << std::hex << hr << ")" << std::endl;
report << "Details: " << duvc::decode_hresult(hr) << std::endl;
}
report << std::endl;
// Device enumeration
report << "=== Device Enumeration ===" << std::endl;
auto devices = duvc::list_devices();
report << "Found " << devices.size() << " camera(s)" << std::endl;
for (size_t i = 0; i < devices.size(); ++i) {
report << std::endl << "Camera " << i << ":" << std::endl;
report << " Name: " << duvc::to_utf8(devices[i].name) << std::endl;
report << " Path: " << duvc::to_utf8(devices[i].path) << std::endl;
// Test connection
try {
Camera camera(devices[i]);
report << " Connection: OK" << std::endl;
// Test basic property
auto range = camera.get_range(VidProp::Brightness);
if (range.is_ok()) {
report << " Basic property access: OK" << std::endl;
} else {
report << " Basic property access: FAILED" << std::endl;
report << " " << range.error().description() << std::endl;
}
} catch (const std::exception& e) {
report << " Connection: FAILED" << std::endl;
report << " " << e.what() << std::endl;
}
}
report << std::endl << "=== Diagnostic Complete ===" << std::endl;
report.close();
std::cout << "Diagnostic report saved to: " << filename << std::endl;
}
GraphEdit (DirectShow Debugging)¶
Use Microsoft GraphEdit to inspect DirectShow:
Download: Windows SDK Tools
Usage:
Open GraphEdit.exe
Graph → Insert Filters → Video Input Device
Select your camera
Right-click filter → Properties
Inspect IAMCameraControl and IAMVideoProcAmp tabs
Benefits:
See raw DirectShow capabilities
Test property changes directly
Identify if issue is camera or library
Windows Device Manager¶
Check camera registration:
Open Device Manager (devmgmt.msc)
Expand “Imaging devices” or “Cameras”
Right-click camera → Properties
Check:
Driver status (should be “Working properly”)
Device class GUID:
{ca3e7ab9-b4c3-4ae6-8251-579ef933890f}(video capture)Hardware IDs contain
USB\VID_andPID_
If camera appears under “Other devices:”
Not recognized as UVC device
Requires vendor driver
duvc-ctl won’t work
Process Monitor (Sysinternals)¶
Track library interactions:
Download: https://learn.microsoft.com/en-us/sysinternals/downloads/procmon
Usage:
Run procmon.exe as Administrator
Add filter: Process Name → your_app.exe
Add filter: Path → contains → “duvc” or “camera”
Run your application
Observe file, registry, and DLL access
Look for:
Missing DLL dependencies
Registry access denied
Device path lookups
WinDbg (Advanced Crash Analysis)¶
Debug crashes in DirectShow:
Download: Windows SDK
Usage:
windbg.exe your_app.exe
> .sympath+ C:\path\to\duvc-ctl\symbols
> g (run)
> (crash occurs)
> !analyze -v
> k (stack trace)
Common crash causes:
COM interface called from wrong thread
Use-after-free of device filter
NULL pointer from failed QueryInterface
Custom Test Harness¶
Minimal reproduction case:
// test_camera.cpp
#include <duvc-ctl/duvc.hpp>
#include <duvc-ctl/utils/logging.h>
#include <iostream>
int main() {
try {
// Enable logging
duvc::set_log_level(duvc::LogLevel::Debug);
duvc::set_log_callback([](duvc::LogLevel level, const std::string& msg) {
std::cout << "[" << duvc::to_string(level) << "] " << msg << std::endl;
});
std::cout << "Test 1: List devices" << std::endl;
auto devices = duvc::list_devices();
std::cout << "Result: " << devices.size() << " device(s)" << std::endl;
if (devices.empty()) {
std::cout << "No cameras found. Test complete." << std::endl;
return 0;
}
std::cout << "\nTest 2: Create camera" << std::endl;
Camera camera(devices);
std::cout << "Result: Success" << std::endl;
std::cout << "\nTest 3: Get brightness range" << std::endl;
auto range = camera.get_range(VidProp::Brightness);
if (range.is_ok()) {
std::cout << "Result: [" << range.value().min
<< ", " << range.value().max << "]" << std::endl;
} else {
std::cout << "Result: " << range.error().description() << std::endl;
}
std::cout << "\nTest 4: Set brightness" << std::endl;
auto result = camera.set(VidProp::Brightness, {128, CamMode::Manual});
if (result.is_ok()) {
std::cout << "Result: Success" << std::endl;
} else {
std::cout << "Result: " << result.error().description() << std::endl;
}
std::cout << "\nAll tests complete." << std::endl;
} catch (const std::exception& e) {
std::cerr << "Exception: " << e.what() << std::endl;
return 1;
}
return 0;
}