5. Vendor-Specific Extensions¶
5.1 Logitech Extensions¶
Header: <duvc-ctl/vendor/logitech.h>
Namespace: duvc::logitech
Platform: Windows only (#ifdef _WIN32)
Provides access to Logitech-specific UVC extension unit properties beyond standard DirectShow controls. These properties control vendor-specific features like RightLight, face tracking, and LED indicators.
This serves more of like an example for similar vendor specific extensions that can be made available
LogitechProperty enum¶
enum class LogitechProperty : uint32_t {
RightLight = 1, // Auto-exposure and brightness optimization
RightSound = 2, // Audio processing and noise cancellation
FaceTracking = 3, // Face tracking enable/disable for auto-framing
LedIndicator = 4, // LED indicator control (on/off/blink modes)
ProcessorUsage = 5, // Processor usage optimization hints
RawDataBits = 6, // Raw data bit depth configuration
FocusAssist = 7, // Focus assist beam control
VideoStandard = 8, // Video standard selection (NTSC/PAL/etc)
DigitalZoomROI = 9, // Digital zoom region of interest coordinates
TiltPan = 10 // Combined tilt/pan control (absolute positioning)
};
10 vendor property values corresponding to Logitech’s UVC extension unit property IDs. Values map to the property set GUID {82066163-7BD0-43EF-8A6F-5B8905C9A64C}.
Property descriptions:
Property |
Description |
Typical Data Type |
|---|---|---|
|
Adaptive lighting optimization |
|
|
Audio noise reduction and enhancement |
|
|
Auto-framing based on detected faces |
|
|
Camera status LED control |
|
|
CPU/GPU usage hints for firmware |
|
|
Sensor bit depth configuration |
|
|
IR focus assist beam enable |
|
|
Broadcast standard selection |
|
|
Digital zoom target region |
Struct (x, y, width, height) |
|
Combined PTZ positioning |
Struct (pan, tilt values) |
Raw byte property access¶
Result<std::vector<uint8_t>> get_logitech_property(const Device& device, LogitechProperty prop);
Result<void> set_logitech_property(const Device& device, LogitechProperty prop, const std::vector<uint8_t>& data);
Low-level functions for reading/writing properties as raw byte vectors. Caller responsible for interpreting binary format.
Example:
auto devices = duvc::list_devices();
auto data_result = duvc::logitech::get_logitech_property(devices, duvc::logitech::LogitechProperty::RightLight);
if (data_result.is_ok()) {
const auto& bytes = data_result.value();
// Interpret bytes based on property format
}
Setting property:
std::vector<uint8_t> led_data = {0x02}; // LED mode 2
auto result = duvc::logitech::set_logitech_property(device, duvc::logitech::LogitechProperty::LedIndicator, led_data);
Error returns:
ErrorCode::PropertyNotSupported: Device lacks Logitech extension unitErrorCode::SystemError: KsProperty query failedErrorCode::DeviceNotFound: Device disconnected
Typed property access¶
template <typename T>
Result<T> get_logitech_property_typed(const Device& device, LogitechProperty prop);
template <typename T>
Result<void> set_logitech_property_typed(const Device& device, LogitechProperty prop, const T& value);
Type-safe wrappers that reinterpret property bytes as specified type. Type T must be trivially copyable and match the property’s binary layout.
Supported types (explicit instantiations):
uint32_tint32_tbool
Reading typed property:
auto result = duvc::logitech::get_logitech_property_typed<uint32_t>(device, duvc::logitech::LogitechProperty::RightLight);
if (result.is_ok()) {
uint32_t rightlight_mode = result.value();
std::cout << "RightLight: " << (rightlight_mode ? "enabled" : "disabled") << "\n";
}
Setting typed property:
// Enable face tracking
auto result = duvc::logitech::set_logitech_property_typed<bool>(device, duvc::logitech::LogitechProperty::FaceTracking, true);
if (result.is_error()) {
std::cerr << "Failed: " << result.error().description() << "\n";
}
Type safety: The typed functions validate that received data size matches sizeof(T). Mismatched sizes return ErrorCode::InvalidValue.
Custom types: For complex properties like DigitalZoomROI, define a matching struct:
struct ZoomROI {
int32_t x;
int32_t y;
uint32_t width;
uint32_t height;
};
static_assert(std::is_trivially_copyable_v<ZoomROI>);
auto roi = duvc::logitech::get_logitech_property_typed<ZoomROI>(device, duvc::logitech::LogitechProperty::DigitalZoomROI);
Device support detection¶
Result<bool> supports_logitech_properties(const Device& device);
Checks if a device supports Logitech vendor extensions by querying the extension unit’s presence and capabilities.
auto support = duvc::logitech::supports_logitech_properties(device);
if (support.is_ok() && support.value()) {
// Device supports Logitech extensions
auto rightlight = duvc::logitech::get_logitech_property_typed<uint32_t>(device, duvc::logitech::LogitechProperty::RightLight);
} else {
std::cout << "Logitech extensions not available\n";
}
Implementation: Internally queries support flags for the RightLight property. Returns false on any error or if KSPROPERTY_SUPPORT_GET/KSPROPERTY_SUPPORT_SET flags are not set.
Non-Logitech cameras: Always returns false. Safe to call on any device.
Implementation details¶
Property set GUID:
inline constexpr GUID LOGITECH_PROPERTY_SET = {
0x82066163, 0x7BD0, 0x43EF,
{0x8A, 0x6F, 0x5B, 0x89, 0x05, 0xC9, 0xA6, 0x4C}
};
All Logitech properties belong to this extension unit GUID. Defined as inline constexpr for C++17 ODR-safety.
Backend: Uses KsPropertySet class (from ks_properties.h) to interact with Windows Kernel Streaming property APIs. This bypasses DirectShow and directly communicates with the USB Video Class driver.
Error handling:
Exceptions from
KsPropertySetcaught and converted toResult<T>errorsLogged at
ERRORlevel for get/set,DEBUGlevel for support queriesDevice disconnection during operation returns
SystemError
Usage patterns¶
Toggle feature:
if (duvc::logitech::supports_logitech_properties(device).value_or(false)) {
// Enable RightLight
duvc::logitech::set_logitech_property_typed<uint32_t>(device, duvc::logitech::LogitechProperty::RightLight, 1);
// Toggle face tracking
auto tracking = duvc::logitech::get_logitech_property_typed<bool>(device, duvc::logitech::LogitechProperty::FaceTracking);
if (tracking.is_ok()) {
duvc::logitech::set_logitech_property_typed<bool>(device, duvc::logitech::LogitechProperty::FaceTracking, !tracking.value());
}
}
Conditional features:
auto devices = duvc::list_devices();
for (const auto& dev : devices) {
auto support = duvc::logitech::supports_logitech_properties(dev);
if (support.value_or(false)) {
std::wcout << dev.name << L" [Logitech extensions available]\n";
}
}
Platform availability: All functions are Windows-only and wrapped in #ifdef _WIN32. Non-Windows builds will have no duvc::logitech namespace.
Camera compatibility: Only Logitech cameras with UVC extension units support these properties. Other manufacturers may use different GUIDs and property IDs.
5.2 Kernel Streaming Properties¶
Header: <duvc-ctl/platform/ks_properties.h>
Platform: Windows only (#ifdef _WIN32)
Low-level wrapper for Windows Kernel Streaming property access via the IKsPropertySet COM interface. Used internally by vendor extensions to query properties beyond DirectShow’s standard interfaces.
KsPropertySet class¶
class KsPropertySet {
public:
explicit KsPropertySet(const Device& device);
~KsPropertySet();
// Non-copyable but movable
KsPropertySet(KsPropertySet&&) noexcept;
KsPropertySet& operator=(KsPropertySet&&) noexcept;
bool is_valid() const;
Result<uint32_t> query_support(const GUID& property_set, uint32_t property_id);
Result<std::vector<uint8_t>> get_property(const GUID& property_set, uint32_t property_id);
Result<void> set_property(const GUID& property_set, uint32_t property_id, const std::vector<uint8_t>& data);
template <typename T>
Result<T> get_property_typed(const GUID& property_set, uint32_t property_id);
template <typename T>
Result<void> set_property_typed(const GUID& property_set, uint32_t property_id, const T& value);
};
RAII wrapper managing lifetime of the IKsPropertySet COM interface. Automatically queries interface from device filter on construction.
Constructor and validity¶
explicit KsPropertySet(const Device& device);
bool is_valid() const;
Constructor opens the device’s DirectShow filter and queries for IID_IKsPropertySet interface. If the query fails (device doesn’t support Kernel Streaming properties), the instance is invalid.
KsPropertySet prop_set(device);
if (!prop_set.is_valid()) {
std::cerr << "Device does not support KS properties\n";
return;
}
Non-throwing: Constructor catches all exceptions and sets the instance to invalid state. Always check is_valid() before use.
Move semantics: The class is move-only (non-copyable). Moving transfers ownership of the COM interface pointer.
Query support¶
Result<uint32_t> query_support(const GUID& property_set, uint32_t property_id);
Queries whether a property is supported and what operations (get/set) are available. Returns support flags as a bitmask.
auto support = prop_set.query_support(LOGITECH_PROPERTY_SET, 1);
if (support.is_ok()) {
uint32_t flags = support.value();
bool can_get = (flags & KSPROPERTY_SUPPORT_GET) != 0;
bool can_set = (flags & KSPROPERTY_SUPPORT_SET) != 0;
}
Support flags:
KSPROPERTY_SUPPORT_GET(0x1): Property can be readKSPROPERTY_SUPPORT_SET(0x2): Property can be written
Error returns:
ErrorCode::SystemError: Interface not validErrorCode::PropertyNotSupported: Property not supported by device
Raw property access¶
Result<std::vector<uint8_t>> get_property(const GUID& property_set, uint32_t property_id);
Result<void> set_property(const GUID& property_set, uint32_t property_id, const std::vector<uint8_t>& data);
Low-level byte-oriented property access.
get_property: Queries property size, allocates buffer, retrieves data. Returns byte vector containing property value in its native binary format.
auto data = prop_set.get_property(some_guid, 5);
if (data.is_ok()) {
const auto& bytes = data.value();
// Interpret bytes based on property definition
}
set_property: Writes raw byte data to property.
std::vector<uint8_t> value_bytes = {0x01, 0x00, 0x00, 0x00}; // uint32_t = 1
prop_set.set_property(some_guid, 5, value_bytes);
Implementation details:
get_propertyfirst callsIKsPropertySet::Get()with null buffer to query size, then allocates and retrieves actual dataset_propertycallsIKsPropertySet::Set()directly with provided bufferBoth decode HRESULT failures using
decode_hresult()utility
Typed property access¶
template <typename T>
Result<T> get_property_typed(const GUID& property_set, uint32_t property_id);
template <typename T>
Result<void> set_property_typed(const GUID& property_set, uint32_t property_id, const T& value);
Type-safe wrappers that reinterpret property bytes as specified type T. Type must be trivially copyable.
Reading typed value:
auto value = prop_set.get_property_typed<uint32_t>(guid, 2);
if (value.is_ok()) {
uint32_t mode = value.value();
}
Writing typed value:
bool enable = true;
prop_set.set_property_typed<bool>(guid, 3, enable);
Implementation:
get_property_typedcallsget_property(), validates size matchessizeof(T), then usesstd::memcpyto reinterpret bytesset_property_typedusesstd::memcpyto convert value to bytes, then callsset_property()Size mismatch returns
ErrorCode::InvalidValuewith descriptive message
Explicit instantiations for common types:
uint32_tint32_tbool
Custom types can be used if they are trivially copyable and match the property’s binary layout.
IKsPropertySet interface¶
struct IKsPropertySet; // Forward declaration
COM interface defined in Windows Kernel Streaming headers. Provides low-level access to device properties via property set GUIDs and IDs.
Key methods (internal to implementation):
QuerySupported(GUID, DWORD, ULONG*): Query support flagsGet(GUID, DWORD, void*, ULONG, void*, ULONG, ULONG*): Read propertySet(GUID, DWORD, void*, ULONG, void*, ULONG): Write property
GUID handling: Property sets are identified by 128-bit GUIDs. Each property within a set has a numeric ID (typically 1-based).
Example GUIDs:
Logitech vendor properties:
{82066163-7BD0-43EF-8A6F-5B8905C9A64C}DirectShow standard sets use different GUIDs
5.3 Vendor Property Structure¶
Note: The codebase does not define a VendorProperty struct or generic get_vendor_property/set_vendor_property functions. Vendor properties are accessed through:
Logitech-specific functions in
duvc::logitechnamespace (Section 5.1)Generic
KsPropertySetclass (Section 5.2) for direct property access
Architecture: The library provides vendor-specific namespaces (currently only duvc::logitech) that wrap KsPropertySet with typed enums and documented property IDs. This approach allows adding new vendors without changing the core API.
Implementing custom vendor properties¶
To add support for another vendor (e.g., Microsoft Surface cameras), follow this pattern:
1. Define property enum and GUID:
namespace duvc::microsoft {
inline constexpr GUID SURFACE_PROPERTY_SET = {/* ... */};
enum class SurfaceProperty : uint32_t {
IRFloodLight = 1,
RGBExposure = 2,
// ...
};
}
2. Implement get/set wrappers:
namespace duvc::microsoft {
Result<std::vector<uint8_t>> get_surface_property(const Device& device, SurfaceProperty prop) {
KsPropertySet prop_set(device);
if (!prop_set.is_valid()) {
return Err<std::vector<uint8_t>>(ErrorCode::PropertyNotSupported, "...");
}
return prop_set.get_property(SURFACE_PROPERTY_SET, static_cast<uint32_t>(prop));
}
// Similar for set_surface_property, typed variants, supports check
}
3. Document property types and typical values in header comments (as done in logitech.h).
Direct KsPropertySet usage¶
For one-off vendor property access without creating a vendor namespace:
#include <duvc-ctl/platform/ks_properties.h>
GUID custom_guid = {/* ... */};
duvc::KsPropertySet props(device);
if (props.is_valid()) {
auto data = props.get_property(custom_guid, 7);
if (data.is_ok()) {
// Process raw bytes
}
}
Use cases:
Prototyping new vendor property support
Accessing undocumented properties for debugging
One-off custom integrations
Production recommendation: Wrap raw access in typed functions for maintainability and documentation, as demonstrated by the Logitech extension implementation.