@@ -91,6 +91,58 @@ using namespace ccap;
9191namespace {
9292constexpr FrameOrientation kDefaultFrameOrientation = FrameOrientation::BottomToTop;
9393
94+ #if defined(_MSC_VER) || (defined(__MINGW64__) && defined(__SEH__))
95+ #define CCAP_SEH_SUPPORTED 1
96+ #else
97+ #define CCAP_SEH_SUPPORTED 0
98+ #endif
99+
100+ enum class DeviceBindResult {
101+ Success = 0 ,
102+ Failed = 1 ,
103+ Exception = 2
104+ };
105+
106+ // / Bind moniker to filter with SEH protection for device validation
107+ DeviceBindResult tryBindMonikerToFilter (IMoniker* moniker, std::string_view name) {
108+ #if CCAP_SEH_SUPPORTED
109+ __try {
110+ #endif
111+ IBaseFilter* filter = nullptr ;
112+ HRESULT hr = moniker->BindToObject (0 , 0 , IID_IBaseFilter, (void **)&filter);
113+ if (SUCCEEDED (hr) && filter) {
114+ filter->Release ();
115+ return DeviceBindResult::Success;
116+ } else {
117+ CCAP_LOG_I (" ccap: \" %s\" is not a valid video capture device, removed\n " , name.data ());
118+ return DeviceBindResult::Failed;
119+ }
120+ #if CCAP_SEH_SUPPORTED
121+ } __except (EXCEPTION_EXECUTE_HANDLER ) {
122+ CCAP_LOG_W (" ccap: \" %s\" caused an exception during device binding, skipping\n " , name.data ());
123+ return DeviceBindResult::Exception;
124+ }
125+ #endif
126+ }
127+
128+ // / Bind moniker to device filter with SEH protection for device opening
129+ DeviceBindResult tryBindMonikerForOpen (IMoniker* moniker, IBaseFilter** deviceFilter) {
130+ #if CCAP_SEH_SUPPORTED
131+ __try {
132+ #endif
133+ HRESULT hr = moniker->BindToObject (0 , 0 , IID_IBaseFilter, (void **)deviceFilter);
134+ if (SUCCEEDED (hr)) {
135+ return DeviceBindResult::Success;
136+ } else {
137+ return DeviceBindResult::Failed;
138+ }
139+ #if CCAP_SEH_SUPPORTED
140+ } __except (EXCEPTION_EXECUTE_HANDLER ) {
141+ return DeviceBindResult::Exception;
142+ }
143+ #endif
144+ }
145+
94146// Release the format block for a media type.
95147void freeMediaType (AM_MEDIA_TYPE & mt) {
96148 if (mt.cbFormat != 0 ) {
@@ -400,21 +452,12 @@ std::vector<std::string> ProviderDirectShow::findDeviceNames() {
400452
401453 enumerateDevices ([&](IMoniker* moniker, std::string_view name) {
402454 // Try to bind device, check if available
403- // Use SEH to catch crashes from buggy drivers (e.g., unplugged devices like Oculus Quest 3)
404- __try {
405- IBaseFilter* filter = nullptr ;
406- HRESULT hr = moniker->BindToObject (0 , 0 , IID_IBaseFilter, (void **)&filter);
407- if (SUCCEEDED (hr) && filter) {
408- m_allDeviceNames.emplace_back (name.data (), name.size ());
409- filter->Release ();
410- } else {
411- CCAP_LOG_I (" ccap: \" %s\" is not a valid video capture device, removed\n " , name.data ());
412- }
413- } __except (EXCEPTION_EXECUTE_HANDLER ) {
414- // Catch crashes from buggy camera drivers (e.g., when camera is physically unplugged)
415- CCAP_LOG_W (" ccap: \" %s\" caused an exception during device binding, skipping\n " , name.data ());
455+ // Use helper function with SEH to catch crashes from exception-throwing devices
456+ DeviceBindResult result = tryBindMonikerToFilter (moniker, name);
457+ if (result == DeviceBindResult::Success) {
458+ m_allDeviceNames.emplace_back (name.data (), name.size ());
416459 }
417- // Unavailable devices are not added to the list
460+ // Unavailable devices (Failed or Exception) are not added to the list
418461 return false ; // Continue enumeration
419462 });
420463
@@ -666,28 +709,29 @@ bool ProviderDirectShow::open(std::string_view deviceName) {
666709
667710 enumerateDevices ([&](IMoniker* moniker, std::string_view name) {
668711 if (deviceName.empty () || deviceName == name) {
669- // Use SEH to catch crashes from buggy drivers (e.g., unplugged devices like Oculus Quest 3)
670- __try {
671- auto hr = moniker->BindToObject (0 , 0 , IID_IBaseFilter, (void **)&m_deviceFilter);
672- if (SUCCEEDED (hr)) {
673- CCAP_LOG_V (" ccap: Using video capture device: %s\n " , name.data ());
674- m_deviceName = name;
675- found = true ;
676- return true ; // stop enumeration when returning true
712+ // Use helper function with SEH to catch crashes from exception-throwing devices
713+ DeviceBindResult result = tryBindMonikerForOpen (moniker, &m_deviceFilter);
714+
715+ if (result == DeviceBindResult::Success) {
716+ // Success - save device name and stop enumeration
717+ CCAP_LOG_V (" ccap: Using video capture device: %s\n " , name.data ());
718+ m_deviceName = name;
719+ found = true ;
720+ return true ;
721+ } else if (!deviceName.empty ()) {
722+ // Specific device was requested but failed
723+ if (result == DeviceBindResult::Exception) {
724+ reportError (ErrorCode::InvalidDevice, " \" " + std::string (deviceName) + " \" caused an exception during binding" );
677725 } else {
678- if (!deviceName.empty ()) {
679- reportError (ErrorCode::InvalidDevice, " \" " + std::string (deviceName) + " \" is not a valid video capture device, bind failed" );
680- return true ; // stop enumeration when returning true
681- }
682-
683- CCAP_LOG_I (" ccap: bind \" %s\" failed(result=%x), try next device...\n " , name.data (), hr);
726+ reportError (ErrorCode::InvalidDevice, " \" " + std::string (deviceName) + " \" is not a valid video capture device, bind failed" );
684727 }
685- } __except (EXCEPTION_EXECUTE_HANDLER ) {
686- // Catch crashes from buggy camera drivers (e.g., when camera is physically unplugged)
687- CCAP_LOG_W (" ccap: \" %s\" caused an exception during device binding, skipping\n " , name.data ());
688- if (!deviceName.empty ()) {
689- reportError (ErrorCode::InvalidDevice, " \" " + std::string (deviceName) + " \" caused an exception during binding" );
690- return true ; // stop enumeration when returning true
728+ return true ; // stop enumeration
729+ } else {
730+ // No specific device requested and this one failed - log and continue trying next device
731+ if (result == DeviceBindResult::Exception) {
732+ CCAP_LOG_W (" ccap: \" %s\" caused an exception during device binding, skipping\n " , name.data ());
733+ } else {
734+ CCAP_LOG_I (" ccap: bind \" %s\" failed, try next device...\n " , name.data ());
691735 }
692736 }
693737 }
0 commit comments