10. Platform-Specific Information¶
10.1 Windows-Specific Details¶
Windows API Dependencies:
duvc-ctl is deeply integrated with Windows platform APIs. Understanding these dependencies is essential for proper usage and troubleshooting.
Required Windows Headers¶
// Core Windows headers
#include <windows.h> // Base Windows API
#include <objbase.h> // COM initialization
#include <dshow.h> // DirectShow interfaces
#include <ks.h> // Kernel streaming
#include <ksproxy.h> // KS proxy interfaces
#include <vidcap.h> // Video capture structures
Linked libraries:
# Required system libraries
ole32.lib # COM runtime
oleaut32.lib # COM automation
strmiids.lib # DirectShow GUIDs
uuid.lib # Interface UUIDs
COM Initialization¶
COM must be initialized per-thread:
// Automatic RAII wrapper
class ComInitializer {
public:
ComInitializer() {
HRESULT hr = CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED);
if (FAILED(hr) && hr != RPC_E_CHANGED_MODE) {
throw std::runtime_error("COM initialization failed");
}
initialized_ = SUCCEEDED(hr);
}
~ComInitializer() {
if (initialized_) {
CoUninitialize();
}
}
private:
bool initialized_;
};
Library handles this automatically:
// duvc-ctl handles COM initialization internally
Camera camera(0); // COM initialized if needed
Manual control:
// If you need explicit control
HRESULT hr = CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED);
// ... use duvc-ctl ...
CoUninitialize();
Threading Model¶
Single-Threaded Apartment (STA):
DirectShow uses STA threading. COM objects are bound to the creating thread and cannot be marshaled to other threads without explicit marshaling.
// CORRECT: Each thread initializes COM
std::thread t1([]() {
ComInitializer com; // COM for this thread
Camera camera(0);
camera.set(CamProp::Exposure, {-5, CamMode::Manual});
});
// WRONG: Passing camera between threads
Camera camera(0);
std::thread t2([&camera]() {
camera.set(CamProp::Exposure, {-5, CamMode::Manual}); // CRASH!
});
Windows Error Codes¶
HRESULT values:
HRESULT |
Value |
Meaning |
|---|---|---|
|
|
Success |
|
|
Unspecified failure |
|
|
Invalid argument |
|
|
Out of memory |
|
|
NULL pointer |
|
|
Not implemented |
|
|
Filter not connected |
|
|
Cannot connect pins |
Decoding errors:
// Use library utilities
std::string error_msg = duvc::decode_hresult(hr);
std::cout << "Error: " << error_msg << std::endl;
// Check error categories
if (duvc::is_device_error(hr)) {
// Device-specific error
}
if (duvc::is_permission_error(hr)) {
// Access denied
}
Device Paths¶
DirectShow device moniker paths:
\\?\usb#vid_046d&pid_0825&mi_00#7&2a2f12e8&0&0000#{65e8773d-8f56-11d0-a3b9-00a0c9223196}\global
Format breakdown:
\\?\- Win32 device namespace prefixusb#vid_046d&pid_0825- USB Vendor ID and Product IDmi_00- Multiple interface index{guid}- Device class GUID (video capture)\global- System-wide device
Parsing device info:
// Extract vendor/product ID
std::wstring path = device.path;
size_t vid_pos = path.find(L"vid_");
size_t pid_pos = path.find(L"pid_");
if (vid_pos != std::wstring::npos && pid_pos != std::wstring::npos) {
std::wstring vid = path.substr(vid_pos + 4, 4); // e.g., "046d"
std::wstring pid = path.substr(pid_pos + 4, 4); // e.g., "0825"
}
System Requirements¶
Minimum Windows version:
Windows 10 recommended
Windows 8.1 compatible (not tested)
Windows 7 not supported (DirectShow API changes)
Required components:
Visual C++ Redistributable 2019/2022
DirectX 9.0c or later
Windows Media Feature Pack (if disabled)
Checking availability:
// Verify DirectShow availability
HRESULT hr = CoInitialize(nullptr);
if (SUCCEEDED(hr)) {
ICreateDevEnum* pDevEnum = nullptr;
hr = CoCreateInstance(CLSID_SystemDeviceEnum, nullptr,
CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pDevEnum));
if (SUCCEEDED(hr)) {
// DirectShow available
pDevEnum->Release();
}
CoUninitialize();
}
Permissions and Access¶
Administrator rights: Not required for UVC camera access. Standard user permissions suffice.
Exclusive access:
DirectShow grants exclusive access to camera properties. If another application is controlling the camera, property changes may fail with E_FAIL or VFW_E_IN_USE.
Antivirus interference: Some antivirus software blocks camera access. Check:
Windows Security → Camera privacy settings
Third-party antivirus camera protection
Wide Character Strings¶
Windows uses UTF-16:
// Device names are std::wstring
Device device = devices[0];
std::wstring name = device.name; // UTF-16
// Convert to UTF-8 for display
std::string utf8_name = duvc::to_utf8(name);
std::cout << "Device: " << utf8_name << std::endl;
Conversion utilities:
namespace duvc {
std::string to_utf8(const std::wstring& wstr);
std::wstring from_utf8(const std::string& str);
}
10.2 DirectShow Background¶
DirectShow is Microsoft’s multimedia framework for capture, playback, and processing.
Architecture Overview¶
Filter graph model:
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ Capture │ │ Transform │ │ Renderer │
│ Filter │─────▶│ Filter │─────▶│ Filter │
│ (Camera) │ │ (Optional) │ │ (Display) │
└─────────────┘ └─────────────┘ └─────────────┘
Components:
Filters - Processing units (capture, transform, render)
Pins - Connection points between filters
Filter Graph Manager - Coordinates filter operation
Media Types - Describes data format (video format, resolution)
Key Interfaces Used¶
Device enumeration:
// ICreateDevEnum - Enumerate device categories
CLSID_SystemDeviceEnum
→ ICreateDevEnum::CreateClassEnumerator(CLSID_VideoInputDeviceCategory)
→ IEnumMoniker (iterate devices)
→ IMoniker::BindToObject(IBaseFilter)
Camera control:
// IAMCameraControl - PTZ and camera-specific properties
IBaseFilter* pFilter;
IAMCameraControl* pCameraControl;
pFilter->QueryInterface(IID_IAMCameraControl, (void**)&pCameraControl);
// Set property
pCameraControl->Set(CameraControl_Pan, value, CameraControl_Flags_Manual);
pCameraControl->Get(CameraControl_Pan, &value, &flags);
Video processing:
// IAMVideoProcAmp - Image properties
IAMVideoProcAmp* pVideoProcAmp;
pFilter->QueryInterface(IID_IAMVideoProcAmp, (void**)&pVideoProcAmp);
// Set brightness
pVideoProcAmp->Set(VideoProcAmp_Brightness, value, VideoProcAmp_Flags_Manual);
Vendor extensions:
// IKsPropertySet - Kernel streaming properties
IKsPropertySet* pKsPropertySet;
pFilter->QueryInterface(IID_IKsPropertySet, (void**)&pKsPropertySet);
// Get/Set vendor-specific properties
GUID propSet = {/* vendor GUID */};
pKsPropertySet->Get(propSet, propId, nullptr, 0, data, dataSize, &returned);
Property Control Flow¶
Setting a camera property:
Enumerate devices →
ICreateDevEnumBind to device filter →
IMoniker::BindToObjectQuery property interface →
QueryInterface(IAMCameraControl)Check property support →
GetRange()Set property value →
Set(prop, value, flags)Verify change →
Get(prop, &value, &flags)
Code path:
// duvc-ctl wraps this flow
Camera camera(device); // Steps 1-3
auto range = camera.get_range(CamProp::Pan); // Step 4
camera.set(CamProp::Pan, {0, CamMode::Manual}); // Steps 5-6
DirectShow vs Media Foundation¶
DirectShow (used by duvc-ctl):
Legacy API (Windows XP+)
Widely supported by hardware
Filter graph model
Synchronous property access
Well-documented UVC support
Media Foundation (newer):
Modern API (Windows Vista+)
Async design
Better performance for streaming
Limited camera control compared to DirectShow
Less hardware compatibility
Why DirectShow?
Universal UVC support - Works with all compliant cameras
Direct property access - No additional drivers needed
Mature ecosystem - 20+ years of hardware compatibility
Synchronous operations - Simpler control flow for settings
Graph Building (Not Used)¶
duvc-ctl does not build filter graphs for streaming. It only uses device filters for property control:
// duvc-ctl does NOT do this:
IGraphBuilder* pGraph;
pGraph->AddFilter(pCaptureFilter, L"Capture");
pGraph->Render(pCapturePin); // Build capture graph
// duvc-ctl ONLY does this:
IAMCameraControl* pControl;
pCaptureFilter->QueryInterface(IID_IAMCameraControl, (void**)&pControl);
pControl->Set(CameraControl_Pan, 0, CameraControl_Flags_Manual);
Streaming is handled by:
Windows Camera app
OpenCV (VideoCapture)
FFmpeg (dshow input)
Third-party capture applications
10.3 UVC Camera Standards¶
USB Video Class (UVC) defines standard camera controls.
UVC Specification¶
USB.org UVC 1.5:
Standard published by USB Implementers Forum
Defines camera control terminals and units
Provides class-specific USB descriptors
Maps to DirectShow
IAMCameraControlandIAMVideoProcAmp
Key concepts:
Control terminals - Camera-specific controls (pan, tilt, zoom)
Processing units - Image processing (brightness, contrast)
Extension units - Vendor-specific controls
Standard UVC Controls¶
Camera Terminal Controls (IAMCameraControl):
UVC Control |
DirectShow Property |
Units |
Typical Range |
|---|---|---|---|
Scanning Mode |
|
- |
0 (Interlaced), 1 (Progressive) |
Auto Exposure Mode |
|
- |
1 (Manual), 2-8 (Auto modes) |
Auto Exposure Priority |
- |
- |
Camera-dependent |
Exposure Time |
|
100µs |
-13 to -1 (log scale) |
Focus |
|
mm |
0 to max (device-specific) |
Iris |
|
f-stop × 10 |
Device-specific |
Zoom |
|
mm |
Device-specific |
Pan |
|
degrees |
-180° to +180° |
Tilt |
|
degrees |
-180° to +180° |
Roll |
|
degrees |
-180° to +180° |
Privacy Shutter |
|
- |
0 (Open), 1 (Closed) |
Processing Unit Controls (IAMVideoProcAmp):
UVC Control |
DirectShow Property |
Typical Range |
|---|---|---|
Brightness |
|
0 to 255 |
Contrast |
|
0 to 255 |
Hue |
|
-180° to +180° |
Saturation |
|
0 to 255 |
Sharpness |
|
0 to 255 |
Gamma |
|
100 to 500 (100 = 1.0) |
White Balance |
|
2800K to 6500K |
Backlight Compensation |
|
0 (Off), 1 (On) |
Gain |
|
0 to 255 |
Color Enable |
|
0 (B&W), 1 (Color) |
Auto vs Manual Modes¶
UVC defines automatic control:
// Auto mode - camera adjusts automatically
camera.set(CamProp::Exposure, {0, CamMode::Auto});
// Manual mode - explicit value
camera.set(CamProp::Exposure, {-5, CamMode::Manual});
DirectShow flags:
CameraControl_Flags_Auto = 0x0001 // Automatic adjustment
CameraControl_Flags_Manual = 0x0002 // Manual control
UVC behavior:
Auto mode - Value parameter ignored, camera firmware controls
Manual mode - Value parameter used, firmware uses specified value
Auto → Manual - Locks current auto value
Manual → Auto - Resumes automatic adjustment
Vendor-Specific Extensions¶
Beyond UVC standard:
Vendors implement proprietary controls via Extension Units (XU) using IKsPropertySet.
Logitech example:
// Standard UVC
camera.set(CamProp::Exposure, {-5, CamMode::Manual});
// Vendor extension (Logitech)
logitech::set_logitech_property(device, LogitechProperty::RightLight, true);
Common vendor extensions:
Logitech - RightLight, LED control, face tracking
Microsoft LifeCam - TrueColor, noise reduction
Razer Kiyo - Ring light control
Elgato Facecam - Advanced color grading
UVC Compliance Levels¶
Not all cameras are equal:
Full UVC 1.5:
All standard controls
Auto/manual modes
Proper range reporting
Spec-compliant behavior
Partial UVC:
Subset of controls (e.g., no PTZ)
Missing auto modes
Incorrect range values
Quirky implementations
Testing compliance:
// Check which properties are supported
for (auto prop : {CamProp::Pan, CamProp::Tilt, CamProp::Zoom}) {
auto range_result = camera.get_range(prop);
if (range_result.is_ok()) {
std::cout << "Supports: " << to_string(prop) << std::endl;
}
}
USB Bandwidth¶
UVC streaming consumes USB bandwidth:
USB 2.0 limits:
High-speed: 480 Mbps theoretical
Practical: ~35 MB/s for video
1080p30 uncompressed: ~177 MB/s (requires compression)
USB 3.0/3.1:
SuperSpeed: 5 Gbps (USB 3.0), 10 Gbps (USB 3.1)
Supports uncompressed high-resolution video
Control bandwidth: Property control uses USB control transfers (separate from video data). Control operations are lightweight and don’t impact streaming performance.
Descriptor Parsing¶
UVC cameras expose USB descriptors:
DirectShow automatically parses:
Video Control Interface
Video Streaming Interface
Camera Terminal Descriptor
Processing Unit Descriptor
Extension Unit Descriptor
duvc-ctl abstracts this:
// No need to parse descriptors
Camera camera(0);
auto range = camera.get_range(CamProp::Pan); // Descriptor info extracted