diff --git a/src/FilterData.hpp b/src/FilterData.hpp index 0cd585ac..6cdab689 100644 --- a/src/FilterData.hpp +++ b/src/FilterData.hpp @@ -31,6 +31,7 @@ struct filter_data : public ORTModelData, public std::enable_shared_from_this newFrameAvailable{false}; std::atomic isDisabled{false}; diff --git a/src/background-filter.cpp b/src/background-filter.cpp index f1652b0a..b9c7373b 100644 --- a/src/background-filter.cpp +++ b/src/background-filter.cpp @@ -536,11 +536,11 @@ void background_filter_video_tick(void *data, float seconds) // No data to process return; } - if (tf->inputBGRA.empty()) { - // No data to process + if (!tf->newFrameAvailable) { return; } - imageBGRA = tf->inputBGRA.clone(); + cv::swap(imageBGRA, tf->inputBGRA); + tf->newFrameAvailable = false; } if (tf->enableImageSimilarity) { diff --git a/src/enhance-filter.cpp b/src/enhance-filter.cpp index 7bb39e07..70f7a808 100644 --- a/src/enhance-filter.cpp +++ b/src/enhance-filter.cpp @@ -247,10 +247,6 @@ void enhance_filter_video_tick(void *data, float seconds) return; } - if (tf->inputBGRA.empty()) { - return; - } - // Get input image from source rendering pipeline cv::Mat imageBGRA; { @@ -258,7 +254,11 @@ void enhance_filter_video_tick(void *data, float seconds) if (!lock.owns_lock()) { return; } - imageBGRA = tf->inputBGRA.clone(); + if (!tf->newFrameAvailable) { + return; + } + cv::swap(imageBGRA, tf->inputBGRA); + tf->newFrameAvailable = false; } cv::Mat outputImage; diff --git a/src/obs-utils/obs-utils.cpp b/src/obs-utils/obs-utils.cpp index 0ebd21a9..6bd0df97 100644 --- a/src/obs-utils/obs-utils.cpp +++ b/src/obs-utils/obs-utils.cpp @@ -67,9 +67,10 @@ bool getRGBAFromStageSurface(filter_data *tf, uint32_t &width, uint32_t &height) std::lock_guard lock(tf->inputBGRALock); // Create a temporary Mat that wraps the video_data pointer cv::Mat temp(height, width, CV_8UC4, video_data, linesize); - // Clone the data to ensure tf->inputBGRA has its own copy - // This prevents use-after-unmap race condition - tf->inputBGRA = temp.clone(); + // Copy frame data into tf->inputBGRA, reusing its allocation + // when dimensions match. Ensures we own the pixels before unmap. + temp.copyTo(tf->inputBGRA); + tf->newFrameAvailable = true; } gs_stagesurface_unmap(tf->stagesurface); return true;