duvc-ctl 2.0.0
USB Video Class Camera Control Library
Loading...
Searching...
No Matches
ks_properties.cpp
Go to the documentation of this file.
1
6#ifdef _WIN32
7
8#include <dshow.h>
10#include <duvc-ctl/detail/com_helpers.h>
13#include <ks.h>
14#include <ksproxy.h>
15
16namespace duvc {
17namespace detail {
18 // Forward declaration - function is in duvc::detail
20}
21
22KsPropertySet::KsPropertySet(const Device &device) : device_(device), mfksproxy_dll_(nullptr) {
23 try {
24 // VALIDATION 1: Check device validity before any operations
25 if (!device.is_valid()) {
26 throw std::invalid_argument(
27 "Device must be valid and opened before creating KsPropertySet. "
28 "Call open_camera(device) first.");
29 }
30
31 // VALIDATION 2: Attempt to open device filter with null check
32 auto filter = detail::open_device_filter(device_);
33 if (!filter) {
34 throw std::runtime_error(
35 "Failed to obtain device filter. Device may not be properly opened "
36 "or may have been disconnected.");
37 }
38
39 // Store ONLY the base filter - this keeps the DLL loaded
40 basefilter_ = std::move(filter);
41
42 // Explicitly load the DLL and keep the handle to ensure it stays loaded
43 mfksproxy_dll_ = LoadLibraryW(L"mfksproxy.dll");
44 if (!mfksproxy_dll_) {
45 throw std::runtime_error("Failed to load mfksproxy.dll.");
46 }
47
48 // VALIDATION 3: Verify property set is available (temporary, not stored)
49 detail::com_ptr<IKsPropertySet> temp_props;
50 HRESULT hr = basefilter_->QueryInterface(IID_PPV_ARGS(temp_props.put()));
51
52 if (FAILED(hr)) {
53 basefilter_.reset(); // cleanup on failure
54 FreeLibrary(mfksproxy_dll_); // Cleanup DLL if query fails
55 throw std::runtime_error(
56 "Device does not support KsPropertySet interface. "
57 "This device may not expose vendor-specific properties. "
58 "HRESULT: " + decode_hresult(hr));
59 }
60
61 // VALIDATION 4: Verify pointer is non-null (double-check after SUCCEEDED)
62 if (!temp_props) {
63 basefilter_.reset();
64 FreeLibrary(mfksproxy_dll_); // Cleanup DLL if temp_props is null
65 throw std::runtime_error(
66 "QueryInterface succeeded but returned null pointer. "
67 "This indicates a driver or COM issue.");
68 }
69
70 // temp_props automatically released here (DLL stays loaded because basefilter_ is held)
71 } catch (...) {
72 basefilter_.reset();
73 if (mfksproxy_dll_) {
74 FreeLibrary(mfksproxy_dll_);
75 }
76 throw;
77 }
78}
79
80// Helper to get property set interface on-demand
81detail::com_ptr<IKsPropertySet> KsPropertySet::get_property_set() const {
82 if (!basefilter_) {
83 return {};
84 }
85 detail::com_ptr<IKsPropertySet> props;
86 HRESULT hr = basefilter_->QueryInterface(IID_PPV_ARGS(props.put()));
87 if (FAILED(hr)) {
88 return {};
89 }
90 return props;
91}
92
93// Destructor that ensures DLL is unloaded safely
95
96 // CRITICAL ORDER: Release COM objects BEFORE unloading DLL
97 // COM vtables may point into the DLL
98 basefilter_.reset();
99
100 if (mfksproxy_dll_ != nullptr) {
101 FreeLibrary(mfksproxy_dll_);
102 mfksproxy_dll_ = nullptr; // Defensive: prevent double-free
103 }
104}
105
107 : device_(std::move(other.device_)),
108 basefilter_(std::move(other.basefilter_)),
109 mfksproxy_dll_(other.mfksproxy_dll_) {
110 other.mfksproxy_dll_ = nullptr; // CRITICAL: Prevent double-free
111}
112
114 if (this != &other) {
115 // Clean up current resources FIRST
116 basefilter_.reset();
117 if (mfksproxy_dll_ != nullptr) {
118 FreeLibrary(mfksproxy_dll_);
119 }
120
121 // Transfer ownership from other
122 device_ = std::move(other.device_);
123 basefilter_ = std::move(other.basefilter_);
124 mfksproxy_dll_ = other.mfksproxy_dll_;
125 other.mfksproxy_dll_ = nullptr; // CRITICAL: Prevent double-free
126 }
127 return *this;
128}
129
130
132 return static_cast<bool>(basefilter_); // Check basefilter instead
133}
134
136 uint32_t property_id) {
137 auto props = get_property_set(); // Get temporary
138 if (!props) {
140 "Property set interface not available");
141 }
142
144 HRESULT hr = props->QuerySupported(property_set, property_id, &type_support);
145 // props automatically released here (DLL stays loaded because basefilter_ is held)
146
147 if (FAILED(hr)) {
149 "Property not supported: " + decode_hresult(hr));
150 }
151
152 return Ok(static_cast<uint32_t>(type_support));
153}
154
156KsPropertySet::get_property(const GUID &property_set, uint32_t property_id) {
157 auto props = get_property_set(); // Get temporary
158 if (!props) {
160 "Property set interface not available");
161 }
162
163 // First, get the required buffer size
165 HRESULT hr = props->Get(property_set, property_id, nullptr, 0,
166 nullptr, 0, &bytes_returned);
167 if (FAILED(hr) || bytes_returned == 0) {
169 "Failed to get property size: " +
171 }
172
173 // Allocate buffer and get the actual data
174 std::vector<uint8_t> data(bytes_returned);
175 hr = props->Get(property_set, property_id, nullptr, 0, data.data(),
177 // props automatically released here (DLL stays loaded because basefilter_ is held)
178
179 if (FAILED(hr)) {
181 "Failed to get property data: " +
183 }
184
185 // Resize to actual returned size
186 data.resize(bytes_returned);
187 return Ok(std::move(data));
188}
189
191 uint32_t property_id,
192 const std::vector<uint8_t> &data) {
193 auto props = get_property_set(); // Get temporary
194 if (!props) {
196 "Property set interface not available");
197 }
198
199 HRESULT hr = props->Set(property_set, property_id, nullptr, 0,
200 const_cast<uint8_t *>(data.data()),
201 static_cast<ULONG>(data.size()));
202 // props automatically released here (DLL stays loaded because basefilter_ is held)
203
204 if (FAILED(hr)) {
206 "Failed to set property: " + decode_hresult(hr));
207 }
208
209 return Ok();
210}
211
212template <typename T>
214 uint32_t property_id) {
215 auto result = get_property(property_set, property_id);
216 if (!result.is_ok()) {
217 return Err<T>(result.error());
218 }
219
220 const auto &data = result.value();
221 if (data.size() != sizeof(T)) {
223 "Property data size mismatch: expected " +
224 std::to_string(sizeof(T)) + " bytes, got " +
225 std::to_string(data.size()));
226 }
227
228 T value;
229 std::memcpy(&value, data.data(), sizeof(T));
230 return Ok(value);
231}
232
233template <typename T>
235 uint32_t property_id,
236 const T &value) {
237 std::vector<uint8_t> data(sizeof(T));
238 std::memcpy(data.data(), &value, sizeof(T));
239 return set_property(property_set, property_id, data);
240}
241
242// Explicit template instantiations for common types
243template Result<uint32_t>
244KsPropertySet::get_property_typed<uint32_t>(const GUID &, uint32_t);
245template Result<int32_t>
246KsPropertySet::get_property_typed<int32_t>(const GUID &, uint32_t);
247template Result<bool> KsPropertySet::get_property_typed<bool>(const GUID &,
248 uint32_t);
249
250template Result<void>
251KsPropertySet::set_property_typed<uint32_t>(const GUID &, uint32_t,
252 const uint32_t &);
253template Result<void>
254KsPropertySet::set_property_typed<int32_t>(const GUID &, uint32_t,
255 const int32_t &);
256template Result<void>
257KsPropertySet::set_property_typed<bool>(const GUID &, uint32_t, const bool &);
258
259} // namespace duvc
260
261#endif // _WIN32
RAII wrapper for IKsPropertySet interface.
Result< T > get_property_typed(const GUID &property_set, uint32_t property_id)
Get typed property value.
Result< uint32_t > query_support(const GUID &property_set, uint32_t property_id)
Query property support.
Result< void > set_property_typed(const GUID &property_set, uint32_t property_id, const T &value)
Set typed property value.
Result< void > set_property(const GUID &property_set, uint32_t property_id, const std::vector< uint8_t > &data)
Set property data.
KsPropertySet & operator=(const KsPropertySet &)=delete
bool is_valid() const
Check if property set is valid.
Result< std::vector< uint8_t > > get_property(const GUID &property_set, uint32_t property_id)
Get property data.
KsPropertySet(const Device &device)
Create KsPropertySet from device.
~KsPropertySet()
Destructor.
Result type that can contain either a value or an error.
Definition result.h:75
const T & value() const &
Get the value (assumes success)
Definition result.h:114
bool is_ok() const
Check if result contains a value (success)
Definition result.h:101
const Error & error() const
Get the error (assumes error)
Definition result.h:128
Device enumeration and management functions.
HRESULT decoder and diagnostics utilities.
IKsPropertySet wrapper for vendor properties.
com_ptr< IBaseFilter > open_device_filter(const Device &dev)
Definition core.h:13
@ InvalidValue
Property value out of range.
@ PropertyNotSupported
Property not supported by device.
@ SystemError
System/platform error.
std::string decode_hresult(HRESULT hr)
Decode HRESULT to human-readable string.
Result< void > Ok()
Helper to create successful void Result.
Definition result.h:222
Represents a camera device.
Definition types.h:131
bool is_valid() const
Check if device has valid identifying information.
Definition types.h:186