duvc-ctl 2.1.0
USB Video Class Camera Control Library
Loading...
Searching...
No Matches
core.cpp
Go to the documentation of this file.
1#include "duvc-ctl/core.h"
2#include "duvc-ctl/defs.h"
3
4#ifdef _WIN32
5
6#ifndef NOMINMAX
7#define NOMINMAX
8#endif
9
10#include <windows.h>
11#include <dshow.h>
12#include <iostream>
13#include <vector>
14#include <string>
15#include <sstream>
16#include <stdexcept>
17#include <comdef.h>
18#include <strmif.h>
19#include <control.h>
20#include <uuids.h>
21#include <dbt.h>
22#include <ks.h>
23#include <ksproxy.h>
24#include <unordered_map>
25#include <memory>
26#include <mutex>
27
28// Fallback IAMCameraControl (rarely needed but safe)
29#ifndef __AMCAMERACONTROL__
30#define CameraControl_Pan 0L
31#define CameraControl_Tilt 1L
32#define CameraControl_Roll 2L
33#define CameraControl_Zoom 3L
34#define CameraControl_Exposure 4L
35#define CameraControl_Iris 5L
36#define CameraControl_Focus 6L
37#define CameraControl_ScanMode 7L
38#define CameraControl_Privacy 8L
39#define CameraControl_PanRelative 9L
40#define CameraControl_TiltRelative 10L
41#define CameraControl_RollRelative 11L
42#define CameraControl_ZoomRelative 12L
43#define CameraControl_ExposureRelative 13L
44#define CameraControl_IrisRelative 14L
45#define CameraControl_FocusRelative 15L
46#define CameraControl_PanTilt 16L
47#define CameraControl_PanTiltRelative 17L
48#define CameraControl_FocusSimple 18L
49#define CameraControl_DigitalZoom 19L
50#define CameraControl_DigitalZoomRelative 20L
51#define CameraControl_BacklightCompensation 21L
52#define CameraControl_Lamp 22L
53#define CameraControl_Flags_Auto 0x0001
54#define CameraControl_Flags_Manual 0x0002
55#endif
56
57// Fallback IAMVideoProcAmp
58#ifndef __AMVIDEOPROCAMP__
59#define VideoProcAmp_Brightness 0
60#define VideoProcAmp_Contrast 1
61#define VideoProcAmp_Hue 2
62#define VideoProcAmp_Saturation 3
63#define VideoProcAmp_Sharpness 4
64#define VideoProcAmp_Gamma 5
65#define VideoProcAmp_ColorEnable 6
66#define VideoProcAmp_WhiteBalance 7
67#define VideoProcAmp_BacklightCompensation 8
68#define VideoProcAmp_Gain 9
69#define VideoProcAmp_DigitalMultiplier 10
70#define VideoProcAmp_DigitalMultiplierLimit 11
71#define VideoProcAmp_WhiteBalanceComponent 12
72#define VideoProcAmp_PowerLineFrequency 13
73#define VideoProcAmp_Flags_Auto 0x0001
74#define VideoProcAmp_Flags_Manual 0x0002
75#endif
76
77#ifdef _MSC_VER
78#pragma comment(lib, "ole32.lib")
79#pragma comment(lib, "oleaut32.lib")
80#pragma comment(lib, "strmiids.lib")
81#endif
82
83namespace duvc {
84
85template<typename T>
86class com_ptr {
87public:
90 ~com_ptr() { reset(); }
91
92 com_ptr(const com_ptr&) = delete;
93 com_ptr& operator=(const com_ptr&) = delete;
94
95 com_ptr(com_ptr&& o) noexcept : p_(o.p_) { o.p_ = nullptr; }
96 com_ptr& operator=(com_ptr&& o) noexcept {
97 if (this != &o) { reset(); p_ = o.p_; o.p_ = nullptr; }
98 return *this;
99 }
100
101 T* get() const noexcept { return p_; }
102 T** put() noexcept { reset(); return &p_; }
103 T* operator->() const noexcept { return p_; }
104 explicit operator bool() const noexcept { return p_ != nullptr; }
105 void reset() noexcept { if (p_) { p_->Release(); p_ = nullptr; } }
106
107private:
108 T* p_ = nullptr;
109};
110
111[[maybe_unused]] static std::string wide_to_utf8(const wchar_t* ws) {
112 if (!ws) return {};
113 int sz = WideCharToMultiByte(CP_UTF8, 0, ws, -1, nullptr, 0, nullptr, nullptr);
114 std::string out(sz > 0 ? sz - 1 : 0, '\0');
115 if (sz > 0) WideCharToMultiByte(CP_UTF8, 0, ws, -1, out.data(), sz, nullptr, nullptr);
116 return out;
117}
118
119static void throw_hr(HRESULT hr, const char* where) {
121 std::ostringstream oss;
122 oss << where << " failed (hr=0x" << std::hex << hr << ")";
123 if (err.ErrorMessage()) {
124 #ifdef UNICODE
125 oss << " - " << wide_to_utf8(err.ErrorMessage());
126 #else
127 oss << " - " << err.ErrorMessage();
128 #endif
129 }
130 throw std::runtime_error(oss.str());
131}
132
134public:
137 if (FAILED(hr_) && hr_ != RPC_E_CHANGED_MODE) {
138 throw_hr(hr_, "CoInitializeEx");
139 }
140 }
141
143 if (SUCCEEDED(hr_)) CoUninitialize();
144 }
145
146private:
147 HRESULT hr_{ S_OK };
148};
149
150// Device monitoring globals
154
155// Connection pool globals
156static std::mutex g_cache_mutex;
157static std::unordered_map<std::wstring, std::unique_ptr<DeviceConnection>> g_connection_cache;
158
159// Window procedure for device notifications
174
175
176
180 IID_ICreateDevEnum, reinterpret_cast<void**>(dev.put()));
181 if (FAILED(hr)) throw_hr(hr, "CoCreateInstance(SystemDeviceEnum)");
182 return dev;
183}
184
187 HRESULT hr = dev->CreateClassEnumerator(CLSID_VideoInputDeviceCategory, e.put(), 0);
188 if (hr == S_FALSE) return {}; // none
189 if (FAILED(hr)) throw_hr(hr, "CreateClassEnumerator(VideoInputDeviceCategory)");
190 return e;
191}
192
193static std::wstring read_prop_bstr(IPropertyBag* bag, const wchar_t* key) {
195 std::wstring res;
196 if (SUCCEEDED(bag->Read(key, &v, nullptr)) && v.vt == VT_BSTR && v.bstrVal) {
197 res.assign(v.bstrVal, SysStringLen(v.bstrVal));
198 }
199 VariantClear(&v);
200 return res;
201}
202
203static std::wstring read_friendly_name(IMoniker* mon) {
205 HRESULT hr = mon->BindToStorage(nullptr, nullptr, IID_IPropertyBag, reinterpret_cast<void**>(bag.put()));
206 if (FAILED(hr)) return L"";
207 auto name = read_prop_bstr(bag.get(), L"FriendlyName");
208 return name.empty() ? L"" : name;
209}
210
211static std::wstring read_device_path(IMoniker* mon) {
213 if (SUCCEEDED(mon->BindToStorage(nullptr, nullptr, IID_IPropertyBag, reinterpret_cast<void**>(bag.put())))) {
214 auto dp = read_prop_bstr(bag.get(), L"DevicePath");
215 if (!dp.empty()) return dp;
216 }
217
218 LPOLESTR disp = nullptr;
219 std::wstring res;
220 if (SUCCEEDED(mon->GetDisplayName(nullptr, nullptr, &disp)) && disp) {
221 res.assign(disp);
223 }
224 return res;
225}
226
229 HRESULT hr = mon->BindToObject(nullptr, nullptr, IID_IBaseFilter, reinterpret_cast<void**>(f.put()));
230 if (FAILED(hr)) throw_hr(hr, "BindToObject(IBaseFilter)");
231 return f;
232}
233
236 if (FAILED(f->QueryInterface(IID_IAMCameraControl, reinterpret_cast<void**>(cam.put())))) return {};
237 return cam;
238}
239
242 if (FAILED(f->QueryInterface(IID_IAMVideoProcAmp, reinterpret_cast<void**>(vp.put())))) return {};
243 return vp;
244}
245
248 if (FAILED(f->QueryInterface(IID_IKsPropertySet, reinterpret_cast<void**>(props.put())))) {
249 return {};
250 }
251 return props;
252}
253
254static bool is_same_device(const Device& d, const std::wstring& name, const std::wstring& path) {
255 if (!d.path.empty() && !path.empty()) {
256 if (_wcsicmp(d.path.c_str(), path.c_str()) == 0) return true;
257 }
258 if (!d.name.empty() && !name.empty()) {
259 if (_wcsicmp(d.name.c_str(), name.c_str()) == 0) return true;
260 }
261 return false;
262}
263
265 auto de = create_dev_enum();
266 auto en = enum_video_devices(de.get());
267 if (!en) throw std::runtime_error("No video devices available");
268
269 ULONG fetched = 0;
271 while (en->Next(1, mon.put(), &fetched) == S_OK && fetched) {
272 auto fname = read_friendly_name(mon.get());
273 auto dpath = read_device_path(mon.get());
274 if (is_same_device(dev, fname, dpath)) {
275 return bind_to_filter(mon.get());
276 }
277 mon.reset();
278 }
279 throw std::runtime_error("Device not found");
280}
281
282// Mapping helpers
284 switch (p) {
285 case CamProp::Pan: return CameraControl_Pan;
308 default: return -1;
309 }
310}
311
331
339
347
348// DeviceConnection implementation
350public:
353 if (FAILED(hr_) && hr_ != RPC_E_CHANGED_MODE) {
354 throw_hr(hr_, "CoInitializeEx");
355 }
356 }
357
359 if (SUCCEEDED(hr_)) CoUninitialize();
360 }
361
362private:
363 HRESULT hr_{ S_OK };
364};
365
367 com_(std::make_unique<com_apartment>()),
368 filter_(nullptr),
369 cam_ctrl_(nullptr),
370 vid_proc_(nullptr) {
371 try {
373 if (filter) {
374 auto cam_ctrl = get_cam_ctrl(filter.get());
375 auto vid_proc = get_vproc(filter.get());
376
377 // Store as raw pointers but keep references
378 filter_ = new com_ptr<IBaseFilter>(std::move(filter));
379 cam_ctrl_ = new com_ptr<IAMCameraControl>(std::move(cam_ctrl));
380 vid_proc_ = new com_ptr<IAMVideoProcAmp>(std::move(vid_proc));
381 }
382 } catch (...) {
383 filter_ = nullptr;
384 }
385}
386
388 delete static_cast<com_ptr<IBaseFilter>*>(filter_);
389 delete static_cast<com_ptr<IAMCameraControl>*>(cam_ctrl_);
390 delete static_cast<com_ptr<IAMVideoProcAmp>*>(vid_proc_);
391}
392
394 auto* cam_ctrl = static_cast<com_ptr<IAMCameraControl>*>(cam_ctrl_);
395 if (!cam_ctrl || !*cam_ctrl) return false;
396
397 long pid = camprop_to_dshow(prop);
398 if (pid < 0) return false;
399
400 long value = 0, flags = 0;
401 HRESULT hr = (*cam_ctrl)->Get(pid, &value, &flags);
402 if (FAILED(hr)) return false;
403
404 val.value = static_cast<int>(value);
405 val.mode = from_flag(flags, true);
406 return true;
407}
408
410 auto* cam_ctrl = static_cast<com_ptr<IAMCameraControl>*>(cam_ctrl_);
411 if (!cam_ctrl || !*cam_ctrl) return false;
412
413 long pid = camprop_to_dshow(prop);
414 if (pid < 0) return false;
415
416 long flags = to_flag(val.mode, true);
417 HRESULT hr = (*cam_ctrl)->Set(pid, static_cast<long>(val.value), flags);
418 return SUCCEEDED(hr);
419}
420
422 auto* vid_proc = static_cast<com_ptr<IAMVideoProcAmp>*>(vid_proc_);
423 if (!vid_proc || !*vid_proc) return false;
424
425 long pid = vidprop_to_dshow(prop);
426 if (pid < 0) return false;
427
428 long value = 0, flags = 0;
429 HRESULT hr = (*vid_proc)->Get(pid, &value, &flags);
430 if (FAILED(hr)) return false;
431
432 val.value = static_cast<int>(value);
433 val.mode = from_flag(flags, false);
434 return true;
435}
436
438 auto* vid_proc = static_cast<com_ptr<IAMVideoProcAmp>*>(vid_proc_);
439 if (!vid_proc || !*vid_proc) return false;
440
441 long pid = vidprop_to_dshow(prop);
442 if (pid < 0) return false;
443
444 long flags = to_flag(val.mode, false);
445 HRESULT hr = (*vid_proc)->Set(pid, static_cast<long>(val.value), flags);
446 return SUCCEEDED(hr);
447}
448
450 auto* cam_ctrl = static_cast<com_ptr<IAMCameraControl>*>(cam_ctrl_);
451 if (!cam_ctrl || !*cam_ctrl) return false;
452
453 long pid = camprop_to_dshow(prop);
454 if (pid < 0) return false;
455
456 long min = 0, max = 0, step = 0, def = 0, flags = 0;
457 HRESULT hr = (*cam_ctrl)->GetRange(pid, &min, &max, &step, &def, &flags);
458 if (FAILED(hr)) return false;
459
460 range.min = static_cast<int>(min);
461 range.max = static_cast<int>(max);
462 range.step = static_cast<int>(step);
463 range.default_val = static_cast<int>(def);
464 range.default_mode = from_flag(flags, true);
465 return true;
466}
467
469 auto* vid_proc = static_cast<com_ptr<IAMVideoProcAmp>*>(vid_proc_);
470 if (!vid_proc || !*vid_proc) return false;
471
472 long pid = vidprop_to_dshow(prop);
473 if (pid < 0) return false;
474
475 long min = 0, max = 0, step = 0, def = 0, flags = 0;
476 HRESULT hr = (*vid_proc)->GetRange(pid, &min, &max, &step, &def, &flags);
477 if (FAILED(hr)) return false;
478
479 range.min = static_cast<int>(min);
480 range.max = static_cast<int>(max);
481 range.step = static_cast<int>(step);
482 range.default_val = static_cast<int>(def);
483 range.default_mode = from_flag(flags, false);
484 return true;
485}
486
487// Connection pool management
489 std::lock_guard<std::mutex> lock(g_cache_mutex);
490
491 std::wstring key = dev.path.empty() ? dev.name : dev.path;
492 auto it = g_connection_cache.find(key);
493
494 if (it != g_connection_cache.end() && it->second->is_valid()) {
495 return it->second.get();
496 }
497
498 // Create new connection
499 auto conn = std::make_unique<DeviceConnection>(dev);
500 if (!conn->is_valid()) return nullptr;
501
503 g_connection_cache[key] = std::move(conn);
504 return result;
505}
506
508 std::lock_guard<std::mutex> lock(g_cache_mutex);
509 std::wstring key = dev.path.empty() ? dev.name : dev.path;
510 g_connection_cache.erase(key);
511}
512
514 std::lock_guard<std::mutex> lock(g_cache_mutex);
515 g_connection_cache.clear();
516}
517
518// Device monitoring implementation
520 if (g_notification_window) return; // Already registered
521
523
524 // Create invisible window for notifications
525 WNDCLASSW wc = {}; // Use WNDCLASSW for Unicode
526 wc.lpfnWndProc = device_wndproc;
527 wc.hInstance = GetModuleHandle(nullptr);
528 wc.lpszClassName = L"DuvcDeviceNotification";
529 RegisterClassW(&wc); // Use RegisterClassW
530
531 g_notification_window = CreateWindowW(L"DuvcDeviceNotification", L"", 0, 0, 0, 0, 0, // Use CreateWindowW
532 HWND_MESSAGE, nullptr, GetModuleHandle(nullptr), nullptr);
533
534 // Register for device interface notifications
536 filter.dbcc_size = sizeof(filter);
537 filter.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
538 filter.dbcc_classguid = CLSID_VideoInputDeviceCategory;
539
542}
543
544
556
558 try {
559 // First try: Check if device still exists in enumeration
561 auto de = create_dev_enum();
562 auto en = enum_video_devices(de.get());
563 if (!en) return false;
564
565 ULONG fetched = 0;
567 while (en->Next(1, mon.put(), &fetched) == S_OK && fetched) {
568 auto fname = read_friendly_name(mon.get());
569 auto dpath = read_device_path(mon.get());
570 if (is_same_device(dev, fname, dpath)) {
571 // Found in enumeration - now try lightweight access test
572 try {
573 // Try to create a cached connection (uses existing connection if available)
575 return conn != nullptr && conn->is_valid();
576 } catch (...) {
577 // If cached connection fails, device exists but might be busy
578 // Since it's enumerated, consider it "connected" but potentially busy
579 return true;
580 }
581 }
582 mon.reset();
583 }
584 return false; // Not found in enumeration
585 } catch (...) {
586 return false;
587 }
588}
589
590
591// Vendor property implementation
592bool get_vendor_property(const Device& dev, const GUID& property_set, ULONG property_id,
593 std::vector<uint8_t>& data) {
596 auto props = get_property_set(filter.get());
597 if (!props) return false;
598
600 HRESULT hr = props->Get(property_set, property_id, nullptr, 0,
601 nullptr, 0, &bytes_returned);
602 if (FAILED(hr) || bytes_returned == 0) return false;
603
604 data.resize(bytes_returned);
605 hr = props->Get(property_set, property_id, nullptr, 0,
606 data.data(), bytes_returned, &bytes_returned);
607 return SUCCEEDED(hr);
608}
609
610bool set_vendor_property(const Device& dev, const GUID& property_set, ULONG property_id,
611 const std::vector<uint8_t>& data) {
614 auto props = get_property_set(filter.get());
615 if (!props) return false;
616
617 HRESULT hr = props->Set(property_set, property_id, nullptr, 0,
618 const_cast<uint8_t*>(data.data()), data.size());
619 return SUCCEEDED(hr);
620}
621
622bool query_vendor_property_support(const Device& dev, const GUID& property_set, ULONG property_id) {
625 auto props = get_property_set(filter.get());
626 if (!props) return false;
627
629 HRESULT hr = props->QuerySupported(property_set, property_id, &type_support);
631}
632
633// Public API: enumeration
634std::vector<Device> list_devices() {
636 std::vector<Device> out;
637
638 auto de = create_dev_enum();
639 auto en = enum_video_devices(de.get());
640 if (!en) return out;
641
642 ULONG fetched = 0;
644 while (en->Next(1, mon.put(), &fetched) == S_OK && fetched) {
645 Device d;
646 d.name = read_friendly_name(mon.get());
647 d.path = read_device_path(mon.get());
648 out.emplace_back(std::move(d));
649 mon.reset();
650 }
651 return out;
652}
653
654// Updated public API functions to use cached connections
655bool get_range(const Device& dev, CamProp prop, PropRange& range) {
656 auto* conn = get_cached_connection(dev);
657 return conn ? conn->get_range(prop, range) : false;
658}
659
660bool get(const Device& dev, CamProp prop, PropSetting& val) {
661 auto* conn = get_cached_connection(dev);
662 return conn ? conn->get(prop, val) : false;
663}
664
665bool set(const Device& dev, CamProp prop, const PropSetting& val) {
666 auto* conn = get_cached_connection(dev);
667 return conn ? conn->set(prop, val) : false;
668}
669
670bool get_range(const Device& dev, VidProp prop, PropRange& range) {
671 auto* conn = get_cached_connection(dev);
672 return conn ? conn->get_range(prop, range) : false;
673}
674
675bool get(const Device& dev, VidProp prop, PropSetting& val) {
676 auto* conn = get_cached_connection(dev);
677 return conn ? conn->get(prop, val) : false;
678}
679
680bool set(const Device& dev, VidProp prop, const PropSetting& val) {
681 auto* conn = get_cached_connection(dev);
682 return conn ? conn->set(prop, val) : false;
683}
684
685// String helpers
686const char* to_string(CamProp p) {
687 switch (p) {
688 case CamProp::Pan: return "Pan";
689 case CamProp::Tilt: return "Tilt";
690 case CamProp::Roll: return "Roll";
691 case CamProp::Zoom: return "Zoom";
692 case CamProp::Exposure: return "Exposure";
693 case CamProp::Iris: return "Iris";
694 case CamProp::Focus: return "Focus";
695 case CamProp::ScanMode: return "ScanMode";
696 case CamProp::Privacy: return "Privacy";
697 case CamProp::PanRelative: return "PanRelative";
698 case CamProp::TiltRelative: return "TiltRelative";
699 case CamProp::RollRelative: return "RollRelative";
700 case CamProp::ZoomRelative: return "ZoomRelative";
701 case CamProp::ExposureRelative: return "ExposureRelative";
702 case CamProp::IrisRelative: return "IrisRelative";
703 case CamProp::FocusRelative: return "FocusRelative";
704 case CamProp::PanTilt: return "PanTilt";
705 case CamProp::PanTiltRelative: return "PanTiltRelative";
706 case CamProp::FocusSimple: return "FocusSimple";
707 case CamProp::DigitalZoom: return "DigitalZoom";
708 case CamProp::DigitalZoomRelative: return "DigitalZoomRelative";
709 case CamProp::BacklightCompensation: return "BacklightCompensation";
710 case CamProp::Lamp: return "Lamp";
711 default: return "Unknown";
712 }
713}
714
715const wchar_t* to_wstring(CamProp p) {
716 switch (p) {
717 case CamProp::Pan: return L"Pan";
718 case CamProp::Tilt: return L"Tilt";
719 case CamProp::Roll: return L"Roll";
720 case CamProp::Zoom: return L"Zoom";
721 case CamProp::Exposure: return L"Exposure";
722 case CamProp::Iris: return L"Iris";
723 case CamProp::Focus: return L"Focus";
724 case CamProp::ScanMode: return L"ScanMode";
725 case CamProp::Privacy: return L"Privacy";
726 case CamProp::PanRelative: return L"PanRelative";
727 case CamProp::TiltRelative: return L"TiltRelative";
728 case CamProp::RollRelative: return L"RollRelative";
729 case CamProp::ZoomRelative: return L"ZoomRelative";
730 case CamProp::ExposureRelative: return L"ExposureRelative";
731 case CamProp::IrisRelative: return L"IrisRelative";
732 case CamProp::FocusRelative: return L"FocusRelative";
733 case CamProp::PanTilt: return L"PanTilt";
734 case CamProp::PanTiltRelative: return L"PanTiltRelative";
735 case CamProp::FocusSimple: return L"FocusSimple";
736 case CamProp::DigitalZoom: return L"DigitalZoom";
737 case CamProp::DigitalZoomRelative: return L"DigitalZoomRelative";
738 case CamProp::BacklightCompensation: return L"BacklightCompensation";
739 case CamProp::Lamp: return L"Lamp";
740 default: return L"Unknown";
741 }
742}
743
744const char* to_string(VidProp p) {
745 switch (p) {
746 case VidProp::Brightness: return "Brightness";
747 case VidProp::Contrast: return "Contrast";
748 case VidProp::Hue: return "Hue";
749 case VidProp::Saturation: return "Saturation";
750 case VidProp::Sharpness: return "Sharpness";
751 case VidProp::Gamma: return "Gamma";
752 case VidProp::ColorEnable: return "ColorEnable";
753 case VidProp::WhiteBalance: return "WhiteBalance";
754 case VidProp::BacklightCompensation: return "BacklightCompensation";
755 case VidProp::Gain: return "Gain";
756 case VidProp::DigitalMultiplier: return "DigitalMultiplier";
757 case VidProp::DigitalMultiplierLimit: return "DigitalMultiplierLimit";
758 case VidProp::WhiteBalanceComponent: return "WhiteBalanceComponent";
759 case VidProp::PowerLineFrequency: return "PowerLineFrequency";
760 default: return "Unknown";
761 }
762}
763
764const wchar_t* to_wstring(VidProp p) {
765 switch (p) {
766 case VidProp::Brightness: return L"Brightness";
767 case VidProp::Contrast: return L"Contrast";
768 case VidProp::Hue: return L"Hue";
769 case VidProp::Saturation: return L"Saturation";
770 case VidProp::Sharpness: return L"Sharpness";
771 case VidProp::Gamma: return L"Gamma";
772 case VidProp::ColorEnable: return L"ColorEnable";
773 case VidProp::WhiteBalance: return L"WhiteBalance";
774 case VidProp::BacklightCompensation: return L"BacklightCompensation";
775 case VidProp::Gain: return L"Gain";
776 case VidProp::DigitalMultiplier: return L"DigitalMultiplier";
777 case VidProp::DigitalMultiplierLimit: return L"DigitalMultiplierLimit";
778 case VidProp::WhiteBalanceComponent: return L"WhiteBalanceComponent";
779 case VidProp::PowerLineFrequency: return L"PowerLineFrequency";
780 default: return L"Unknown";
781 }
782}
783
784const char* to_string(CamMode m) {
785 return (m == CamMode::Auto) ? "AUTO" : "MANUAL";
786}
787
788const wchar_t* to_wstring(CamMode m) {
789 return (m == CamMode::Auto) ? L"AUTO" : L"MANUAL";
790}
791
792} // namespace duvc
793
794#else // _WIN32
795
796// Non-Windows stubs to allow inclusion without linking errors
797namespace duvc {
798
799std::vector<Device> list_devices() { return {}; }
800bool get_range(const Device&, CamProp, PropRange&) { return false; }
801bool get(const Device&, CamProp, PropSetting&) { return false; }
802bool set(const Device&, CamProp, const PropSetting&) { return false; }
803bool get_range(const Device&, VidProp, PropRange&) { return false; }
804bool get(const Device&, VidProp, PropSetting&) { return false; }
805bool set(const Device&, VidProp, const PropSetting&) { return false; }
806
807const char* to_string(CamProp) { return "Unknown"; }
808const char* to_string(VidProp) { return "Unknown"; }
809const char* to_string(CamMode) { return "MANUAL"; }
810
811const wchar_t* to_wstring(CamProp) { return L"Unknown"; }
812const wchar_t* to_wstring(VidProp) { return L"Unknown"; }
813const wchar_t* to_wstring(CamMode) { return L"MANUAL"; }
814
815// Stubs for new functionality
818bool is_device_connected(const Device&) { return false; }
819
820bool get_vendor_property(const Device&, const GUID&, ULONG, std::vector<uint8_t>&) { return false; }
821bool set_vendor_property(const Device&, const GUID&, ULONG, const std::vector<uint8_t>&) { return false; }
822bool query_vendor_property_support(const Device&, const GUID&, ULONG) { return false; }
823
824DeviceConnection* get_cached_connection(const Device&) { return nullptr; }
825void release_cached_connection(const Device&) {}
827
828} // namespace duvc
829
830#endif // _WIN32
RAII wrapper for DirectShow device connections.
Definition core.h:55
DeviceConnection(const Device &dev)
Definition core.cpp:366
bool get(CamProp prop, PropSetting &val)
Definition core.cpp:393
bool set(CamProp prop, const PropSetting &val)
Definition core.cpp:409
bool get_range(CamProp prop, PropRange &range)
Definition core.cpp:449
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
com_ptr() noexcept=default
T * operator->() const noexcept
Definition core.cpp:103
T ** put() noexcept
Definition core.cpp:102
com_ptr(com_ptr &&o) noexcept
Definition core.cpp:95
T * get() const noexcept
Definition core.cpp:101
com_ptr & operator=(const com_ptr &)=delete
com_ptr(const com_ptr &)=delete
com_ptr & operator=(com_ptr &&o) noexcept
Definition core.cpp:96
void reset() noexcept
Definition core.cpp:105
#define VideoProcAmp_Contrast
Definition core.cpp:60
#define VideoProcAmp_Hue
Definition core.cpp:61
#define CameraControl_PanRelative
Definition core.cpp:39
#define CameraControl_Roll
Definition core.cpp:32
#define VideoProcAmp_ColorEnable
Definition core.cpp:65
#define CameraControl_DigitalZoom
Definition core.cpp:49
#define CameraControl_Tilt
Definition core.cpp:31
#define CameraControl_Zoom
Definition core.cpp:33
#define VideoProcAmp_Sharpness
Definition core.cpp:63
#define VideoProcAmp_WhiteBalanceComponent
Definition core.cpp:71
#define CameraControl_Focus
Definition core.cpp:36
#define CameraControl_IrisRelative
Definition core.cpp:44
#define CameraControl_Lamp
Definition core.cpp:52
#define CameraControl_FocusSimple
Definition core.cpp:48
#define CameraControl_TiltRelative
Definition core.cpp:40
#define CameraControl_ExposureRelative
Definition core.cpp:43
#define CameraControl_PanTiltRelative
Definition core.cpp:47
#define VideoProcAmp_WhiteBalance
Definition core.cpp:66
#define CameraControl_ZoomRelative
Definition core.cpp:42
#define VideoProcAmp_Gamma
Definition core.cpp:64
#define CameraControl_ScanMode
Definition core.cpp:37
#define VideoProcAmp_PowerLineFrequency
Definition core.cpp:72
#define CameraControl_Pan
Definition core.cpp:30
#define CameraControl_Privacy
Definition core.cpp:38
#define CameraControl_Exposure
Definition core.cpp:34
#define CameraControl_Flags_Manual
Definition core.cpp:54
#define CameraControl_DigitalZoomRelative
Definition core.cpp:50
#define CameraControl_Iris
Definition core.cpp:35
#define VideoProcAmp_BacklightCompensation
Definition core.cpp:67
#define CameraControl_PanTilt
Definition core.cpp:46
#define VideoProcAmp_DigitalMultiplierLimit
Definition core.cpp:70
#define CameraControl_RollRelative
Definition core.cpp:41
#define VideoProcAmp_DigitalMultiplier
Definition core.cpp:69
#define CameraControl_BacklightCompensation
Definition core.cpp:51
#define CameraControl_Flags_Auto
Definition core.cpp:53
#define CameraControl_FocusRelative
Definition core.cpp:45
#define VideoProcAmp_Flags_Manual
Definition core.cpp:74
#define VideoProcAmp_Brightness
Definition core.cpp:59
#define VideoProcAmp_Flags_Auto
Definition core.cpp:73
#define VideoProcAmp_Saturation
Definition core.cpp:62
#define VideoProcAmp_Gain
Definition core.cpp:68
EXTERN_C const IID IID_IPropertyBag
Definition device.cpp:20
EXTERN_C const CLSID CLSID_SystemDeviceEnum
Definition device.cpp:17
EXTERN_C const CLSID CLSID_VideoInputDeviceCategory
Definition device.cpp:18
EXTERN_C const IID IID_ICreateDevEnum
Definition device.cpp:19
bool get(const Device &, CamProp, PropSetting &)
Get a camera control property value.
Definition duvc.hpp:63
bool set(const Device &, CamProp, const PropSetting &)
Set a camera control property value.
Definition duvc.hpp:81
bool get_range(const Device &, CamProp, PropRange &)
Get the valid range for a camera control property.
Definition duvc.hpp:95
#define KSPROPERTY_SUPPORT_SET
Definition logitech.cpp:18
#define KSPROPERTY_SUPPORT_GET
Definition logitech.cpp:17
Definition core.h:13
std::wstring read_friendly_name(IMoniker *mon)
Read friendly name from device moniker.
Definition core.cpp:203
static std::mutex g_cache_mutex
Definition core.cpp:156
static std::string wide_to_utf8(const wchar_t *ws)
Definition core.cpp:111
static com_ptr< IAMVideoProcAmp > get_vproc(IBaseFilter *f)
Definition core.cpp:240
static std::wstring read_prop_bstr(IPropertyBag *bag, const wchar_t *key)
Definition core.cpp:193
VidProp
Video processing properties (IAMVideoProcAmp interface)
Definition types.h:50
@ PowerLineFrequency
Power line frequency (anti-flicker setting)
@ DigitalMultiplier
Digital multiplier level.
@ Saturation
Color saturation level.
@ Gain
Sensor gain level.
@ WhiteBalanceComponent
White balance component adjustment.
@ WhiteBalance
White balance adjustment.
@ DigitalMultiplierLimit
Maximum digital multiplier level.
@ Brightness
Image brightness level.
@ ColorEnable
Color vs. monochrome mode.
@ Sharpness
Image sharpness level.
@ Contrast
Image contrast level.
@ Gamma
Gamma correction value.
@ BacklightCompensation
Backlight compensation level.
@ Hue
Color hue adjustment.
void clear_connection_cache()
Definition core.cpp:513
com_ptr< IBaseFilter > open_device_filter(const Device &dev)
Create DirectShow filter from device.
Definition core.cpp:264
bool is_same_device(const Device &d, const std::wstring &name, const std::wstring &path)
Check if two device identifiers refer to same device.
Definition core.cpp:254
static DeviceChangeCallback g_device_callback
Definition core.cpp:151
com_ptr< IEnumMoniker > enum_video_devices(ICreateDevEnum *dev)
Enumerate video input devices.
Definition core.cpp:185
static void throw_hr(HRESULT hr, const char *where)
Definition core.cpp:119
std::vector< Device > list_devices()
Enumerate all available video input devices.
Definition core.cpp:634
std::wstring read_device_path(IMoniker *mon)
Read device path from moniker.
Definition core.cpp:211
void unregister_device_change_callback()
Unregister device change callback.
Definition core.cpp:545
void release_cached_connection(const Device &dev)
Definition core.cpp:507
CamMode
Property control mode.
Definition types.h:70
@ Auto
Automatic control by camera.
@ Manual
Manual control by application.
static com_ptr< IAMCameraControl > get_cam_ctrl(IBaseFilter *f)
Definition core.cpp:234
bool query_vendor_property_support(const Device &dev, const GUID &property_set, ULONG property_id)
Query whether device supports a vendor-specific property.
Definition core.cpp:622
static LRESULT CALLBACK device_wndproc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
Definition core.cpp:160
static std::unordered_map< std::wstring, std::unique_ptr< DeviceConnection > > g_connection_cache
Definition core.cpp:157
const wchar_t * to_wstring(CamProp)
Convert camera property enum to wide string.
Definition core.cpp:715
static com_ptr< IBaseFilter > bind_to_filter(IMoniker *mon)
Definition core.cpp:227
static com_ptr< IKsPropertySet > get_property_set(IBaseFilter *f)
Definition core.cpp:246
const char * to_string(CamProp)
Convert camera property enum to string.
Definition core.cpp:686
CamProp
Camera control properties (IAMCameraControl interface)
Definition types.h:18
@ ExposureRelative
Relative exposure adjustment.
@ Roll
Camera roll rotation.
@ FocusSimple
Simple focus control.
@ PanRelative
Relative pan movement.
@ RollRelative
Relative roll movement.
@ Zoom
Optical zoom level.
@ Lamp
Camera lamp/flash control.
@ PanTiltRelative
Relative pan/tilt movement.
@ Tilt
Vertical camera rotation.
@ TiltRelative
Relative tilt movement.
@ ScanMode
Scan mode (progressive/interlaced)
@ ZoomRelative
Relative zoom movement.
@ Privacy
Privacy mode on/off.
@ IrisRelative
Relative iris adjustment.
@ Iris
Aperture/iris setting.
@ Exposure
Exposure time.
@ Focus
Focus position.
@ FocusRelative
Relative focus adjustment.
@ PanTilt
Combined pan/tilt control.
@ DigitalZoomRelative
Relative digital zoom.
@ Pan
Horizontal camera rotation.
@ BacklightCompensation
Backlight compensation.
@ DigitalZoom
Digital zoom level.
static HDEVNOTIFY g_device_notify
Definition core.cpp:153
bool get_vendor_property(const Device &dev, const GUID &property_set, ULONG property_id, std::vector< uint8_t > &data)
Get vendor-specific property data from device.
Definition core.cpp:592
static long to_flag(CamMode m, bool is_camera_control)
Definition core.cpp:332
DeviceConnection * get_cached_connection(const Device &dev)
Definition core.cpp:488
bool set_vendor_property(const Device &dev, const GUID &property_set, ULONG property_id, const std::vector< uint8_t > &data)
Set vendor-specific property data on device.
Definition core.cpp:610
bool is_device_connected(const Device &dev)
Check if a device is currently connected and accessible.
Definition core.cpp:557
com_ptr< ICreateDevEnum > create_dev_enum()
Create DirectShow device enumerator.
Definition core.cpp:177
void register_device_change_callback(DeviceChangeCallback callback)
Register callback for device hotplug events.
Definition core.cpp:519
std::function< void(bool device_added, const std::wstring &device_path)> DeviceChangeCallback
Device change callback function type.
Definition core.h:34
static long camprop_to_dshow(CamProp p)
Definition core.cpp:283
static HWND g_notification_window
Definition core.cpp:152
static CamMode from_flag(long flag, bool is_camera_control)
Definition core.cpp:340
static long vidprop_to_dshow(VidProp p)
Definition core.cpp:312
Represents a camera device.
Definition types.h:135
Property range and default information.
Definition types.h:96
int default_val
Default value.
Definition types.h:100
int min
Minimum supported value.
Definition types.h:97
int step
Step size between valid values.
Definition types.h:99
int max
Maximum supported value.
Definition types.h:98
CamMode default_mode
Default control mode.
Definition types.h:101
Property setting with value and control mode.
Definition types.h:78