obs/tools: Replace scene_contains_source

As the recursion checking code is somewhat broken in libOBS, we need something to prevent accidental recursion from occurring. While the alternative fix is to simply make all of libOBS support recursion, unfortunately that endeavor would be too large for a single person to take on.
This commit is contained in:
Michael Fabian 'Xaymar' Dirks 2022-05-10 20:51:59 +02:00
parent e3c7b13d6f
commit 39548e760d
2 changed files with 83 additions and 53 deletions

View file

@ -19,67 +19,96 @@
#include "obs-tools.hpp"
#include <map>
#include <set>
#include <stdexcept>
#include "obs-source.hpp"
#include "obs-weak-source.hpp"
#include "plugin.hpp"
struct scs_searchdata {
obs_source_t* source;
bool found = false;
std::map<obs_source_t*, bool> visited;
struct __sfs_data {
std::set<::streamfx::obs::weak_source> sources;
};
static bool scs_contains(scs_searchdata& sd, obs_source_t* source);
static void scs_enum_active_cb(obs_source_t*, obs_source_t* child, void* searchdata) noexcept
try {
scs_searchdata& sd = reinterpret_cast<scs_searchdata&>(*reinterpret_cast<scs_searchdata*>(searchdata));
scs_contains(sd, child);
} catch (...) {
DLOG_ERROR("Unexpected exception in function '%s'.", __FUNCTION_NAME__);
}
static bool scs_enum_items_cb(obs_scene_t*, obs_sceneitem_t* item, void* searchdata) noexcept
try {
scs_searchdata& sd = reinterpret_cast<scs_searchdata&>(*reinterpret_cast<scs_searchdata*>(searchdata));
obs_source_t* source = obs_sceneitem_get_source(item);
return scs_contains(sd, source);
} catch (...) {
DLOG_ERROR("Unexpected exception in function '%s'.", __FUNCTION_NAME__);
return false;
}
static bool scs_contains(scs_searchdata& sd, obs_source_t* source)
void __source_find_source_enumerate(obs_source_t* haystack, __sfs_data* cbd)
{
if (sd.visited.find(source) != sd.visited.end()) {
return false;
} else {
sd.visited.insert({source, true});
auto tp = obs_source_get_type(haystack);
// Check if this source is already present in the set.
::streamfx::obs::weak_source weak_child{haystack};
if (!weak_child || (cbd->sources.find(weak_child) != cbd->sources.end())) {
return;
}
if (source == sd.source) {
sd.found = true;
return true;
} else {
if (strcmp(obs_source_get_id(source), "scene")) {
obs_scene_t* nscene = obs_scene_from_source(source);
obs_scene_enum_items(nscene, scs_enum_items_cb, &sd);
} else {
obs_source_enum_active_sources(source, scs_enum_active_cb, &sd);
}
}
// If it was not in the list, add it now.
cbd->sources.insert(weak_child);
if (sd.found) {
return false;
// Enumerate direct reference tree.
obs_source_enum_full_tree(
haystack,
[](obs_source_t* parent, obs_source_t* child, void* param) {
try {
__source_find_source_enumerate(child, reinterpret_cast<__sfs_data*>(param));
} catch (...) {
}
},
cbd);
switch (tp) {
case OBS_SOURCE_TYPE_SCENE: {
obs_scene_enum_items(
obs_scene_from_source(haystack),
[](obs_scene_t* scene, obs_sceneitem_t* item, void* param) {
try {
__sfs_data* cbd = reinterpret_cast<__sfs_data*>(param);
__source_find_source_enumerate(obs_sceneitem_get_source(item), cbd);
return true;
} catch (...) {
return true;
}
},
cbd);
}
#if __cplusplus >= 201700L
[[fallthrough]];
#endif
case OBS_SOURCE_TYPE_INPUT: {
// Enumerate filter tree.
obs_source_enum_filters(
haystack,
[](obs_source_t* parent, obs_source_t* child, void* param) {
try {
__sfs_data* cbd = reinterpret_cast<__sfs_data*>(param);
__source_find_source_enumerate(child, cbd);
} catch (...) {
}
},
cbd);
}
#if __cplusplus >= 201700L
[[fallthrough]];
#endif
default:
break;
}
}
bool streamfx::obs::tools::scene_contains_source(obs_scene_t* scene, obs_source_t* source)
bool streamfx::obs::tools::source_find_source(::streamfx::obs::source haystack, ::streamfx::obs::source needle)
{
scs_searchdata sd;
sd.source = source;
obs_scene_enum_items(scene, scs_enum_items_cb, &sd);
return sd.found;
__sfs_data cbd = {};
try {
__source_find_source_enumerate(haystack.get(), &cbd);
} catch (...) {
}
for (auto weak_source : cbd.sources) {
if (!weak_source)
continue;
if (weak_source == needle)
return true;
}
return false;
}
streamfx::obs::tools::child_source::child_source(obs_source_t* parent, std::shared_ptr<obs_source_t> child)

View file

@ -19,10 +19,11 @@
#pragma once
#include "common.hpp"
#include "obs-source.hpp"
namespace streamfx::obs {
namespace tools {
bool scene_contains_source(obs_scene_t* scene, obs_source_t* source);
bool source_find_source(::streamfx::obs::source haystack, ::streamfx::obs::source needle);
class child_source {
obs_source_t* _parent;