From d91042fe2a489a92dcafd84e4bfc14824d787ed2 Mon Sep 17 00:00:00 2001 From: Michael Fabian 'Xaymar' Dirks Date: Fri, 14 Feb 2020 07:55:18 +0100 Subject: [PATCH] source-mirror: Fix OBS_SOURCE_CUSTOM_DRAW support & more Fixes support for OBS_SOURCE_CUSTOM_DRAW sources and refactors the class onto better isolated and wrapped classes to deal with specific tasks. This drastically improves stability without causing code complexity to increase, and makes the code vastly easier to read too. Related: #99 --- source/sources/source-mirror.cpp | 227 ++++++++++++++----------------- source/sources/source-mirror.hpp | 36 +++-- 2 files changed, 121 insertions(+), 142 deletions(-) diff --git a/source/sources/source-mirror.cpp b/source/sources/source-mirror.cpp index 6dd4c39..2c64096 100644 --- a/source/sources/source-mirror.cpp +++ b/source/sources/source-mirror.cpp @@ -50,55 +50,10 @@ using namespace source; -void mirror::mirror_instance::release() -{ - _source_item.reset(); - if (_source) { - _source->events.rename.clear(); - _source->events.audio_data.clear(); - } - _source.reset(); - _source_name.clear(); -} - -void mirror::mirror_instance::acquire(std::string source_name) -{ - using namespace std::placeholders; - - // Try and get source by name. - std::shared_ptr source = std::shared_ptr( - obs_get_source_by_name(source_name.c_str()), [](obs_source_t* ref) { obs_source_release(ref); }); - if (!source) { // If we failed, just exit early. - return; - } else if (source.get() == _self) { // Otherwise, if we somehow found self, also early exit. - return; - } - - // We seem to have a true link to a source, let's add it to our rendering. - obs_sceneitem_t* item = obs_scene_add(obs_scene_from_source(_scene.get()), source.get()); - if (!item) { // Can't add this source to our scene, probably due to one or more issues with it. - return; - } - - // It seems everything has worked out, so let's update our state. - _source = std::make_shared(source.get(), true, true); - _source_name = obs_source_get_name(source.get()); - _source_item = std::shared_ptr(item, [](obs_sceneitem_t* ref) { obs_sceneitem_remove(ref); }); - - // And let's hook up all our events too. - _source->events.rename.add(std::bind(&mirror::mirror_instance::on_source_rename, this, _1, _2, _3)); - if ((obs_source_get_output_flags(this->_source->get()) & OBS_SOURCE_AUDIO) != 0) - _source->events.audio_data.add(std::bind(&mirror::mirror_instance::on_audio_data, this, _1, _2, _3)); -} - mirror::mirror_instance::mirror_instance(obs_data_t* settings, obs_source_t* self) - : obs::source_instance(settings, self), _source(), _source_name(), _audio_enabled(), _audio_layout(), - _audio_kill_thread(), _audio_have_output() + : obs::source_instance(settings, self), _source(), _audio_enabled(), _audio_layout(), _audio_kill_thread(), + _audio_have_output() { - // Create Internal Scene - _scene = std::shared_ptr(obs_scene_get_source(obs_scene_create_private("")), - [](obs_source_t* ref) { obs_source_release(ref); }); - // Spawn Audio Thread _audio_thread = std::thread(std::bind(&mirror::mirror_instance::audio_output_cb, this)); @@ -115,19 +70,16 @@ mirror::mirror_instance::~mirror_instance() if (_audio_thread.joinable()) { _audio_thread.join(); } - - // Delete Internal Scene - _scene.reset(); } uint32_t mirror::mirror_instance::get_width() { - return _source_size.first; + return obs_source_get_width(_source.get()); } uint32_t mirror::mirror_instance::get_height() { - return _source_size.second; + return obs_source_get_height(_source.get()); } static void convert_config(obs_data_t* data) @@ -150,97 +102,87 @@ void mirror::mirror_instance::update(obs_data_t* data) { convert_config(data); - if (this->_source_name != obs_data_get_string(data, ST_SOURCE)) { - // Mirrored source was changed, release and reacquire. - release(); - - // Acquire the new source. - acquire(obs_data_get_string(data, ST_SOURCE)); - } + // Acquire new source. + acquire(obs_data_get_string(data, ST_SOURCE)); // Audio - this->_audio_enabled = obs_data_get_bool(data, ST_SOURCE_AUDIO); - this->_audio_layout = static_cast(obs_data_get_int(data, ST_SOURCE_AUDIO_LAYOUT)); + _audio_enabled = obs_data_get_bool(data, ST_SOURCE_AUDIO); + _audio_layout = static_cast(obs_data_get_int(data, ST_SOURCE_AUDIO_LAYOUT)); } void mirror::mirror_instance::load(obs_data_t* data) { - this->update(data); + update(data); } void mirror::mirror_instance::save(obs_data_t* data) { if (_source) { - obs_data_set_string(data, ST_SOURCE, obs_source_get_name(_source->get())); - } -} - -void mirror::mirror_instance::video_tick(float time) -{ - if (_source && ((obs_source_get_output_flags(_source->get()) & OBS_SOURCE_VIDEO) != 0)) { - _source_size.first = _source->width(); - _source_size.second = _source->height(); + obs_data_set_string(data, ST_SOURCE, obs_source_get_name(_source.get())); } else { - _source_size.first = 0; - _source_size.second = 0; - } - - if (_source_item && ((obs_source_get_output_flags(_source->get()) & OBS_SOURCE_VIDEO) != 0)) { - obs_transform_info info; - - /// Position, Rotation, Scale, Alignment, Bounding Box - vec2_set(&info.pos, 0, 0); - info.rot = 0; - vec2_set(&info.scale, 1., 1.); - info.alignment = OBS_ALIGN_LEFT | OBS_ALIGN_TOP; - vec2_set(&info.bounds, static_cast(_source_size.first), static_cast(_source_size.second)); - - info.bounds_alignment = 0; - info.bounds_type = OBS_BOUNDS_STRETCH; - - obs_sceneitem_set_info(_source_item.get(), &info); - obs_sceneitem_force_update_transform(_source_item.get()); - obs_sceneitem_set_scale_filter(_source_item.get(), OBS_SCALE_DISABLE); + obs_data_unset_user_value(data, ST_SOURCE); } } +void source::mirror::mirror_instance::show() +{ + _visible = obs_source_showing(_self); +} + +void source::mirror::mirror_instance::hide() +{ + _visible = obs_source_showing(_self); +} + +void source::mirror::mirror_instance::activate() +{ + _active = obs_source_active(_self); +} + +void source::mirror::mirror_instance::deactivate() +{ + _active = obs_source_active(_self); +} + +void mirror::mirror_instance::video_tick(float time) {} + void mirror::mirror_instance::video_render(gs_effect_t* effect) { - if (!_source || !_source_item) + if (!_source) return; - if ((obs_source_get_output_flags(_source->get()) & OBS_SOURCE_VIDEO) == 0) + if ((obs_source_get_output_flags(_source.get()) & OBS_SOURCE_VIDEO) == 0) return; - obs_source_video_render(_scene.get()); + obs_source_video_render(_source.get()); } void mirror::mirror_instance::audio_output_cb() noexcept try { - std::unique_lock ulock(this->_audio_lock_outputter); + std::unique_lock ulock(_audio_lock_outputter); - while (!this->_audio_kill_thread) { - this->_audio_notify.wait(ulock, [this]() { return this->_audio_have_output || this->_audio_kill_thread; }); + while (!_audio_kill_thread) { + _audio_notify.wait(ulock, [this]() { return _audio_have_output || _audio_kill_thread; }); - if (this->_audio_have_output) { // Get used audio element + if (_audio_have_output) { // Get used audio element std::shared_ptr mad; { - std::lock_guard capture_lock(this->_audio_lock_capturer); + std::lock_guard capture_lock(_audio_lock_capturer); if (_audio_data_queue.size() > 0) { mad = _audio_data_queue.front(); _audio_data_queue.pop(); } if (_audio_data_queue.size() == 0) { - this->_audio_have_output = false; + _audio_have_output = false; } } if (mad) { ulock.unlock(); - obs_source_output_audio(this->_self, &mad->audio); + obs_source_output_audio(_self, &mad->audio); ulock.lock(); { - std::lock_guard capture_lock(this->_audio_lock_capturer); + std::lock_guard capture_lock(_audio_lock_capturer); _audio_data_free_queue.push(mad); } } @@ -252,34 +194,63 @@ try { LOG_ERROR("Unexpected exception in function '%s'.", __FUNCTION_NAME__); } -void mirror::mirror_instance::enum_active_sources(obs_source_enum_proc_t enum_callback, void* param) +void mirror::mirror_instance::enum_active_sources(obs_source_enum_proc_t cb, void* ptr) { - /* if (_scene) { - enum_callback(_self, _scene.get(), param); - }*/ - if (_source) { - enum_callback(_self, _source->get(), param); - } + if (!_source || !_active) + return; + cb(_self, _source.get(), ptr); } -void mirror::mirror_instance::enum_all_sources(obs_source_enum_proc_t enum_callback, void* param) +void source::mirror::mirror_instance::enum_all_sources(obs_source_enum_proc_t cb, void* ptr) { - /* if (_scene) { - enum_callback(_self, _scene.get(), param); - }*/ - if (_source) { - enum_callback(_self, _source->get(), param); - } + if (!_source) + return; + + cb(_self, _source.get(), ptr); } -void mirror::mirror_instance::on_source_rename(obs::deprecated_source* source, std::string, std::string) +void mirror::mirror_instance::acquire(std::string source_name) +try { + // Find source by name if possible. + std::shared_ptr source = + std::shared_ptr{obs_get_source_by_name(source_name.c_str()), obs::obs_source_deleter}; + if ((!source) || (source.get() == _self)) { // If we failed, just exit early. + return; + } + + // Everything went well, store. + _source_child = std::make_shared(_self, source); + _source = source; + + // Listen to the rename event to update our own settings. + _signal_rename = std::make_shared("rename", _source); + _signal_rename->event.add( + std::bind(&source::mirror::mirror_instance::on_rename, this, std::placeholders::_1, std::placeholders::_2)); + + // Listen to any audio the source spews out. + _signal_audio = std::make_shared(_source); + _signal_audio->event.add(std::bind(&source::mirror::mirror_instance::on_audio, this, std::placeholders::_1, + std::placeholders::_2, std::placeholders::_3)); +} catch (...) { + release(); +} + +void mirror::mirror_instance::release() +{ + _signal_audio.reset(); + _signal_rename.reset(); + _source_child.reset(); + _source.reset(); +} + +void source::mirror::mirror_instance::on_rename(std::shared_ptr, calldata*) { obs_source_save(_self); } -void mirror::mirror_instance::on_audio_data(obs::deprecated_source*, const audio_data* audio, bool) +void source::mirror::mirror_instance::on_audio(std::shared_ptr, const audio_data* audio, bool) { - if (!this->_audio_enabled) { + if (!_audio_enabled) { return; } @@ -294,7 +265,7 @@ void mirror::mirror_instance::on_audio_data(obs::deprecated_source*, const audio std::shared_ptr mad; { // Get free audio data element. - std::lock_guard capture_lock(this->_audio_lock_capturer); + std::lock_guard capture_lock(_audio_lock_capturer); if (_audio_data_free_queue.size() > 0) { mad = _audio_data_free_queue.front(); _audio_data_free_queue.pop(); @@ -324,23 +295,23 @@ void mirror::mirror_instance::on_audio_data(obs::deprecated_source*, const audio mad->audio.frames = audio->frames; mad->audio.timestamp = audio->timestamp; mad->audio.samples_per_sec = aoi->samples_per_sec; - if (this->_audio_layout != SPEAKERS_UNKNOWN) { - mad->audio.speakers = this->_audio_layout; + if (_audio_layout != SPEAKERS_UNKNOWN) { + mad->audio.speakers = _audio_layout; } else { mad->audio.speakers = aoi->speakers; } } { // Push used audio data element. - std::lock_guard capture_lock(this->_audio_lock_capturer); + std::lock_guard capture_lock(_audio_lock_capturer); _audio_data_queue.push(mad); } { // Signal other side. - std::lock_guard output_lock(this->_audio_lock_outputter); - this->_audio_have_output = true; + std::lock_guard output_lock(_audio_lock_outputter); + _audio_have_output = true; } - this->_audio_notify.notify_all(); + _audio_notify.notify_all(); } std::shared_ptr mirror::mirror_factory::factory_instance; @@ -349,9 +320,11 @@ mirror::mirror_factory::mirror_factory() { _info.id = "obs-stream-effects-source-mirror"; _info.type = OBS_SOURCE_TYPE_INPUT; - _info.output_flags = OBS_SOURCE_VIDEO | OBS_SOURCE_AUDIO; + _info.output_flags = OBS_SOURCE_VIDEO | OBS_SOURCE_CUSTOM_DRAW | OBS_SOURCE_AUDIO; set_have_active_child_sources(true); + set_have_child_sources(true); + set_visibility_tracking_enabled(true); finish_setup(); } diff --git a/source/sources/source-mirror.hpp b/source/sources/source-mirror.hpp index 45000fb..b7fdc59 100644 --- a/source/sources/source-mirror.hpp +++ b/source/sources/source-mirror.hpp @@ -27,8 +27,10 @@ #include "gfx/gfx-source-texture.hpp" #include "obs/gs/gs-rendertarget.hpp" #include "obs/gs/gs-sampler.hpp" +#include "obs/obs-signal-handler.hpp" #include "obs/obs-source-factory.hpp" #include "obs/obs-source.hpp" +#include "obs/obs-tools.hpp" #include "plugin.hpp" // OBS @@ -48,12 +50,14 @@ namespace source::mirror { }; class mirror_instance : public obs::source_instance { - // Source - std::shared_ptr _source; - std::string _source_name; + bool _visible; + bool _active; - // Cached Data - std::pair _source_size; + // Source + std::shared_ptr _source; + std::shared_ptr _source_child; + std::shared_ptr _signal_rename; + std::shared_ptr _signal_audio; // Audio bool _audio_enabled; @@ -67,14 +71,6 @@ namespace source::mirror { std::queue> _audio_data_queue; std::queue> _audio_data_free_queue; - // Scene - std::shared_ptr _scene; - std::shared_ptr _source_item; - - private: - void release(); - void acquire(std::string source_name); - public: mirror_instance(obs_data_t* settings, obs_source_t* self); virtual ~mirror_instance(); @@ -86,16 +82,26 @@ namespace source::mirror { virtual void load(obs_data_t*) override; virtual void save(obs_data_t*) override; + virtual void show() override; + virtual void hide() override; + + virtual void activate() override; + virtual void deactivate() override; + virtual void video_tick(float) override; virtual void video_render(gs_effect_t*) override; virtual void enum_active_sources(obs_source_enum_proc_t, void*) override; virtual void enum_all_sources(obs_source_enum_proc_t, void*) override; + private: + void acquire(std::string source_name); + void release(); + void audio_output_cb() noexcept; - void on_source_rename(obs::deprecated_source* source, std::string new_name, std::string old_name); - void on_audio_data(obs::deprecated_source* source, const audio_data* audio, bool muted); + void on_rename(std::shared_ptr, calldata*); + void on_audio(std::shared_ptr, const struct audio_data*, bool); }; class mirror_factory : public obs::source_factory {