From bacf52f9b641353298383183abe695463bc59dd1 Mon Sep 17 00:00:00 2001 From: Michael Fabian 'Xaymar' Dirks Date: Wed, 16 Oct 2019 04:24:18 +0200 Subject: [PATCH] source-mirror: Refactor to improve performance Caching the output of a source is only necessary for really expensive to render sources, so it is disabled by default now. Thanks to that, most Source Mirrors are now "free" instead of requiring two context switches and a texture, while those really expensive can be manually set to cache. The scaling mode is also set to disabled instead of point when rescaling is off to further improve performance. The previous method would incorrectly cause an extra texture to be used. Additionally we now have support for debug markers for graphics debugging, allowing us to exactly tell apart improvements in rendering cost for this source. --- data/locale/en-US.ini | 19 +- source/sources/source-mirror.cpp | 604 ++++++++++++++++--------------- source/sources/source-mirror.hpp | 62 ++-- source/strings.hpp | 3 +- 4 files changed, 355 insertions(+), 333 deletions(-) diff --git a/data/locale/en-US.ini b/data/locale/en-US.ini index 8e255099..9940cae4 100644 --- a/data/locale/en-US.ini +++ b/data/locale/en-US.ini @@ -285,15 +285,16 @@ Source.Mirror.Source.Size="Source Size" Source.Mirror.Source.Size.Description="The size of the source being mirrored. (Automatically updated)" Source.Mirror.Source.Audio="Enable Audio" Source.Mirror.Source.Audio.Description="Enables audio mirroring from this source." -Source.Mirror.Audio.Layout="Audio Layout" -Source.Mirror.Audio.Layout.Unknown="Unknown" -Source.Mirror.Audio.Layout.Mono="Mono" -Source.Mirror.Audio.Layout.Stereo="Stereo" -Source.Mirror.Audio.Layout.StereoLFE="Stereo with LFE" -Source.Mirror.Audio.Layout.Quadraphonic="Quadraphonic" -Source.Mirror.Audio.Layout.QuadraphonicLFE="Quadraphonic With LFE" -Source.Mirror.Audio.Layout.Surround="Surround" -Source.Mirror.Audio.Layout.FullSurround="Full Surround" +Source.Mirror.Source.Audio.Layout="Audio Layout" +Source.Mirror.Source.Audio.Layout.Description="Override the audio layout if the automatically detected one doesn't match the source.\nThis does not perform down or upmixing of any kind." +Source.Mirror.Source.Audio.Layout.Unknown="Unknown" +Source.Mirror.Source.Audio.Layout.Mono="Mono" +Source.Mirror.Source.Audio.Layout.Stereo="Stereo" +Source.Mirror.Source.Audio.Layout.StereoLFE="Stereo with LFE" +Source.Mirror.Source.Audio.Layout.Quadraphonic="Quadraphonic" +Source.Mirror.Source.Audio.Layout.QuadraphonicLFE="Quadraphonic With LFE" +Source.Mirror.Source.Audio.Layout.Surround="Surround" +Source.Mirror.Source.Audio.Layout.FullSurround="Full Surround" Source.Mirror.Scaling="Rescale Source" Source.Mirror.Scaling.Description="Should the source be rescaled?" Source.Mirror.Scaling.Method="Filter" diff --git a/source/sources/source-mirror.cpp b/source/sources/source-mirror.cpp index 8ba1a91b..d5f900b5 100644 --- a/source/sources/source-mirror.cpp +++ b/source/sources/source-mirror.cpp @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include "obs/obs-source-tracker.hpp" @@ -33,6 +34,7 @@ #pragma warning(push) #pragma warning(disable : 4201) #endif +#include #include #include #ifdef _MSC_VER @@ -40,157 +42,153 @@ #endif #define ST "Source.Mirror" -#define ST_SOURCE "Source.Mirror.Source" -#define ST_SOURCE_SIZE "Source.Mirror.Source.Size" -#define ST_AUDIO "Source.Mirror.Source.Audio" -#define ST_AUDIO_LAYOUT ST ".Audio.Layout" -#define ST_AUDIO_LAYOUT_(x) ST_AUDIO_LAYOUT "." D_VSTR(x) -#define ST_SCALING "Source.Mirror.Scaling" -#define ST_SCALING_METHOD "Source.Mirror.Scaling.Method" -#define ST_SCALING_METHOD_POINT "Source.Mirror.Scaling.Method.Point" -#define ST_SCALING_METHOD_BILINEAR "Source.Mirror.Scaling.Method.Bilinear" -#define ST_SCALING_METHOD_BICUBIC "Source.Mirror.Scaling.Method.Bicubic" -#define ST_SCALING_METHOD_LANCZOS "Source.Mirror.Scaling.Method.Lanczos" -#define ST_SCALING_SIZE "Source.Mirror.Scaling.Size" -#define ST_SCALING_TRANSFORMKEEPORIGINAL "Source.Mirror.Scaling.TransformKeepOriginal" -#define ST_SCALING_BOUNDS "Source.Mirror.Scaling.Bounds" -#define ST_SCALING_BOUNDS_STRETCH "Source.Mirror.Scaling.Bounds.Stretch" -#define ST_SCALING_BOUNDS_FIT "Source.Mirror.Scaling.Bounds.Fit" -#define ST_SCALING_BOUNDS_FILL "Source.Mirror.Scaling.Bounds.Fill" -#define ST_SCALING_BOUNDS_FILLWIDTH "Source.Mirror.Scaling.Bounds.FillWidth" -#define ST_SCALING_BOUNDS_FILLHEIGHT "Source.Mirror.Scaling.Bounds.FillHeight" -#define ST_SCALING_ALIGNMENT "Source.Mirror.Scaling.Alignment" +#define ST_SOURCE ST ".Source" +#define ST_SOURCE_SIZE ST_SOURCE ".Size" +#define ST_SOURCE_AUDIO ST_SOURCE ".Audio" +#define ST_SOURCE_AUDIO_LAYOUT ST_SOURCE_AUDIO ".Layout" +#define ST_SOURCE_AUDIO_LAYOUT_(x) ST_SOURCE_AUDIO_LAYOUT "." D_VSTR(x) +#define ST_SCALING ST ".Scaling" +#define ST_SCALING_METHOD ST_SCALING ".Method" +#define ST_SCALING_METHOD_POINT ST_SCALING ".Method.Point" +#define ST_SCALING_METHOD_BILINEAR ST_SCALING ".Method.Bilinear" +#define ST_SCALING_METHOD_BICUBIC ST_SCALING ".Method.Bicubic" +#define ST_SCALING_METHOD_LANCZOS ST_SCALING ".Method.Lanczos" +#define ST_SCALING_SIZE ST_SCALING ".Size" +#define ST_SCALING_TRANSFORMKEEPORIGINAL ST_SCALING ".TransformKeepOriginal" +#define ST_SCALING_BOUNDS ST_SCALING ".Bounds" +#define ST_SCALING_BOUNDS_STRETCH ST_SCALING ".Bounds.Stretch" +#define ST_SCALING_BOUNDS_FIT ST_SCALING ".Bounds.Fit" +#define ST_SCALING_BOUNDS_FILL ST_SCALING ".Bounds.Fill" +#define ST_SCALING_BOUNDS_FILLWIDTH ST_SCALING ".Bounds.FillWidth" +#define ST_SCALING_BOUNDS_FILLHEIGHT ST_SCALING ".Bounds.FillHeight" +#define ST_SCALING_ALIGNMENT ST_SCALING ".Alignment" -void source::mirror::mirror_instance::release_input() +void source::mirror::mirror_instance::release() { - // Clear any references to the previous source. - if (this->_source_item) { - obs_sceneitem_remove(this->_source_item); - this->_source_item = nullptr; + _source_item.reset(); + if (_source) { + _source->events.rename.clear(); + _source->events.audio_data.clear(); } - this->_source.reset(); + _source.reset(); + _source_name.clear(); } -void source::mirror::mirror_instance::acquire_input(std::string source_name) +void source::mirror::mirror_instance::acquire(std::string source_name) { - // Acquire new reference to current source. - obs_source_t* ref_source = obs_get_source_by_name(source_name.c_str()); - if (!ref_source) { - // Early-Exit: Unable to find a source with this name, likely has been released. -#ifdef _DEBUG - P_LOG_DEBUG(" Unable to find target source '%s'.", obs_source_get_name(this->_self), - source_name.c_str()); -#endif + 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 (ref_source == this->_self) { - // Early-Exit: Attempted self-mirror (recursion). -#ifdef _DEBUG - P_LOG_DEBUG(" Attempted to mirror _self.", obs_source_get_name(this->_self)); -#endif - obs_source_release(ref_source); + } else if (source.get() == _self) { // Otherwise, if we somehow found self, also early exit. return; } - std::shared_ptr new_source = std::make_shared(ref_source, true, false); - - // It looks like everything is in order, so continue now. - this->_source_item = obs_scene_add(obs_scene_from_source(this->_scene->get()), new_source->get()); - if (!this->_source_item) { - // Late-Exit: OBS detected something bad, so no further action will be taken. -#ifdef _DEBUG - P_LOG_DEBUG(" Attempted recursion with scene '%s'.", obs_source_get_name(this->_self), - source_name.c_str()); -#endif + // 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; } - // If everything worked fine, we now set everything up. - this->_source = new_source; - this->_source->events.rename += std::bind(&source::mirror::mirror_instance::on_source_rename, this, - std::placeholders::_1, std::placeholders::_2, std::placeholders::_3); - if ((obs_source_get_output_flags(this->_source->get()) & OBS_SOURCE_AUDIO) != 0) { - this->_source->events.audio_data += - std::bind(&source::mirror::mirror_instance::on_audio_data, this, std::placeholders::_1, - std::placeholders::_2, std::placeholders::_3); - } + // 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(&source::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(&source::mirror::mirror_instance::on_audio_data, this, _1, _2, _3)); } source::mirror::mirror_instance::mirror_instance(obs_data_t* settings, obs_source_t* self) - : obs::source_instance(settings, self), _active(true), _tick(0), _scene_rendered(false), _rescale_alignment(0), - _rescale_enabled(false), _rescale_width(1), _rescale_height(1), _rescale_keep_orig_size(false), - _rescale_type(obs_scale_type::OBS_SCALE_BICUBIC), _rescale_bounds(obs_bounds_type::OBS_BOUNDS_STRETCH), - _audio_enabled(false), _audio_kill_thread(false), _audio_have_output(false), _audio_layout(SPEAKERS_UNKNOWN), - _source_item(nullptr) + : obs::source_instance(settings, self), _source(), _source_name(), _audio_enabled(), _audio_layout(), + _audio_kill_thread(), _audio_have_output(), _rescale_enabled(), _rescale_width(), _rescale_height(), + _rescale_keep_orig_size(), _rescale_type(), _rescale_bounds(), _rescale_alignment(), _cache_enabled(), + _cache_rendered() { - // Initialize Video Rendering - this->_scene = - std::make_shared(obs_scene_get_source(obs_scene_create_private("Source Mirror Internal Scene"))); - this->_scene_texture_renderer = - std::make_shared(this->_scene, std::make_shared(this->_self, false, false)); + // Create Internal Scene + _scene = std::shared_ptr(obs_scene_get_source(obs_scene_create_private("")), + [](obs_source_t* ref) { obs_source_release(ref); }); - // Initialize Audio Rendering - this->_audio_thread = std::thread(std::bind(&source::mirror::mirror_instance::audio_output_cb, this)); + // Create Cache Renderer + _cache_renderer = std::make_shared(_scene.get(), _self); + + // Spawn Audio Thread + /// ToDo: Use ThreadPool for this? + _audio_thread = std::thread(std::bind(&source::mirror::mirror_instance::audio_output_cb, this)); } source::mirror::mirror_instance::~mirror_instance() { - release_input(); + release(); - // Finalize Audio Rendering - this->_audio_kill_thread = true; - this->_audio_notify.notify_all(); - if (this->_audio_thread.joinable()) { - this->_audio_thread.join(); + // Kill Audio Thread + _audio_kill_thread = true; + _audio_notify.notify_all(); + if (_audio_thread.joinable()) { + _audio_thread.join(); } - // Finalize Video Rendering - this->_scene_texture_renderer.reset(); - this->_scene.reset(); + // Delete Cache Renderer + _cache_renderer.reset(); + + // Delete Internal Scene + _scene.reset(); } uint32_t source::mirror::mirror_instance::get_width() { - if (_source) { - if ((obs_source_get_output_flags(this->_source->get()) & OBS_SOURCE_VIDEO) == 0) { - return 0; - } - if (this->_rescale_enabled && this->_rescale_width > 0 && !this->_rescale_keep_orig_size) { - return this->_rescale_width; - } else { - return _source->width(); - } - } - return 0; + if (!_source || !_source_item || !(obs_source_get_output_flags(_source->get()) & OBS_SOURCE_VIDEO)) + return 0; + if (_rescale_enabled && _rescale_width > 0 && !_rescale_keep_orig_size) + return _rescale_width; + return _source->width(); } uint32_t source::mirror::mirror_instance::get_height() { - if (_source) { - if ((obs_source_get_output_flags(this->_source->get()) & OBS_SOURCE_VIDEO) == 0) { - return 0; - } - if (this->_rescale_enabled && this->_rescale_height > 0 && !this->_rescale_keep_orig_size) { - return this->_rescale_height; - } else { - return _source->height(); - } + if (!_source || !_source_item || !(obs_source_get_output_flags(_source->get()) & OBS_SOURCE_VIDEO)) + return 0; + if (_rescale_enabled && _rescale_height > 0 && !_rescale_keep_orig_size) + return _rescale_height; + return _source->height(); +} + +static void convert_config(obs_data_t* data) +{ + uint64_t version = static_cast(obs_data_get_int(data, S_VERSION)); + + switch (version) { + case 0: + obs_data_set_int(data, ST_SOURCE_AUDIO_LAYOUT, obs_data_get_int(data, "Source.Mirror.Audio.Layout")); + obs_data_unset_user_value(data, "Source.Mirror.Audio.Layout"); + case STREAMEFFECTS_VERSION: + break; } - return 0; + + obs_data_set_int(data, S_VERSION, STREAMEFFECTS_VERSION); + obs_data_set_string(data, S_COMMIT, STREAMEFFECTS_COMMIT); } void source::mirror::mirror_instance::update(obs_data_t* data) { - { // User changed the source we are tracking. - release_input(); - this->_source_name = obs_data_get_string(data, ST_SOURCE); - if (this->_source_name.length() > 0) { - acquire_input(this->_source_name); - } + 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)); } // Audio - this->_audio_enabled = obs_data_get_bool(data, ST_AUDIO); - this->_audio_layout = static_cast(obs_data_get_int(data, ST_AUDIO_LAYOUT)); + 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)); // Rescaling this->_rescale_enabled = obs_data_get_bool(data, ST_SCALING); @@ -230,115 +228,96 @@ void source::mirror::mirror_instance::update(obs_data_t* data) } } -void source::mirror::mirror_instance::activate() +void source::mirror::mirror_instance::load(obs_data_t* data) { - this->_active = true; - - // No source, delayed acquire. - if (!this->_source_item && this->_source_name.length() > 0) { - this->acquire_input(this->_source_name.c_str()); - } + this->update(data); } -void source::mirror::mirror_instance::deactivate() +void source::mirror::mirror_instance::save(obs_data_t* data) { - this->_active = false; + if (_source) { + obs_data_set_string(data, ST_SOURCE, obs_source_get_name(_source->get())); + } } void source::mirror::mirror_instance::video_tick(float time) { - this->_tick += time; - if (this->_tick > 0.1f) { - this->_tick -= 0.1f; - - // No source, delayed acquire. - if (!this->_source_item && this->_source_name.length() > 0) { - this->acquire_input(this->_source_name.c_str()); - } - } - - // Update Scene Item Boundaries - if ((this->_source_item && this->_source) - && ((obs_source_get_output_flags(this->_source->get()) & OBS_SOURCE_VIDEO) != 0)) { + if (_source_item && ((obs_source_get_output_flags(_source->get()) & OBS_SOURCE_VIDEO) != 0)) { obs_transform_info info; - info.pos.x = 0; - info.pos.y = 0; - info.rot = 0; - info.scale.x = 1.f; - info.scale.y = 1.f; - info.alignment = OBS_ALIGN_LEFT | OBS_ALIGN_TOP; - info.bounds.x = float_t(this->get_width()); - info.bounds.y = float_t(this->get_height()); - info.bounds_alignment = _rescale_alignment; - info.bounds_type = obs_bounds_type::OBS_BOUNDS_STRETCH; - if (this->_rescale_enabled) { - info.bounds_type = this->_rescale_bounds; + + /// Position, Rotation, Scale, Alignment + vec2_set(&info.pos, 0, 0); + info.rot = 0; + vec2_set(&info.scale, 1., 1.); + info.alignment = OBS_ALIGN_LEFT | OBS_ALIGN_TOP; + + /// Bounding Box + if (_rescale_enabled && _rescale_keep_orig_size) { + vec2_set(&info.bounds, static_cast(_rescale_width), static_cast(_rescale_height)); + } else { + vec2_set(&info.bounds, static_cast(get_width()), static_cast(get_height())); } - obs_sceneitem_set_info(this->_source_item, &info); - obs_sceneitem_force_update_transform(this->_source_item); - obs_sceneitem_set_scale_filter(this->_source_item, - this->_rescale_enabled ? this->_rescale_type : obs_scale_type::OBS_SCALE_POINT); + + info.bounds_alignment = _rescale_alignment; + info.bounds_type = OBS_BOUNDS_STRETCH; + if (_rescale_enabled) + info.bounds_type = _rescale_bounds; + + obs_sceneitem_set_info(_source_item.get(), &info); + obs_sceneitem_force_update_transform(_source_item.get()); + obs_sceneitem_set_scale_filter(_source_item.get(), _rescale_enabled ? _rescale_type : OBS_SCALE_DISABLE); } - _scene_rendered = false; + _cache_rendered = false; } void source::mirror::mirror_instance::video_render(gs_effect_t* effect) { - if ((this->_rescale_width == 0) || (this->_rescale_height == 0) || !this->_source_item - || !this->_scene_texture_renderer || !this->_source) { + if (!_source || !_source_item) return; - } - // Don't bother rendering sources that aren't video. - if ((obs_source_get_output_flags(this->_source->get()) & OBS_SOURCE_VIDEO) == 0) { + if ((obs_source_get_output_flags(_source->get()) & OBS_SOURCE_VIDEO) == 0) return; - } GS_DEBUG_MARKER_BEGIN_FORMAT(GS_DEBUG_COLOR_SOURCE, "Source Mirror: %s", obs_source_get_name(_source->get())); - // Only re-render the scene if there was a video_tick, saves GPU cycles. - if (!_scene_rendered) { - GS_DEBUG_MARKER_BEGIN_FORMAT(GS_DEBUG_COLOR_RENDER_VIDEO, "Cache: %s", - obs_source_get_name(_source->get())); - // Override render size if rescaling is enabled. - uint32_t render_width = this->_source->width(); - uint32_t render_height = this->_source->height(); - if (_rescale_enabled) { - render_width = _rescale_width; - render_height = _rescale_height; + // Rendering depends on cached or uncached. + if (_cache_enabled || _rescale_enabled) { + if (!_cache_rendered) { + uint32_t width = get_width(); + uint32_t height = get_height(); + if (_rescale_enabled) { + width = _rescale_width; + height = _rescale_height; + } + + if (!width || !height) + return; + + try { + _cache_texture = this->_cache_renderer->render(width, height); + _cache_rendered = true; + } catch (...) { + } } - if (!render_width || !render_height) { - // Don't render if width or height are 0. + if (!_cache_texture) return; - } - try { - _scene_texture = this->_scene_texture_renderer->render(render_width, render_height); - _scene_rendered = true; - } catch (...) { - // If we fail to render the source, just render nothing. - return; + if (!effect) + effect = obs_get_base_effect(OBS_EFFECT_DEFAULT); + + GS_DEBUG_MARKER_BEGIN(GS_DEBUG_COLOR_ITEM_TEXTURE, "render_cache"); + gs_effect_set_texture(gs_effect_get_param_by_name(effect, "image"), _cache_texture->get_object()); + while (gs_effect_loop(effect, "Draw")) { + gs_draw_sprite(nullptr, 0, get_width(), get_height()); } GS_DEBUG_MARKER_END(); - } - - if (_scene_texture) { - // Use default effect unless we are provided a different effect. - if (!effect) { - effect = obs_get_base_effect(OBS_EFFECT_DEFAULT); - } - - // Render the cached scene texture. - gs_effect_set_texture(gs_effect_get_param_by_name(effect, "image"), _scene_texture->get_object()); - while (gs_effect_loop(effect, "Draw")) { - gs_draw_sprite(_scene_texture->get_object(), 0, this->get_width(), this->get_height()); - } + } else { + obs_source_video_render(_scene.get()); } GS_DEBUG_MARKER_END(); - } void source::mirror::mirror_instance::audio_output_cb() noexcept try { @@ -380,37 +359,27 @@ void source::mirror::mirror_instance::audio_output_cb() noexcept try { void source::mirror::mirror_instance::enum_active_sources(obs_source_enum_proc_t enum_callback, void* param) { - if (this->_scene) { - enum_callback(this->_self, this->_scene->get(), param); + if (_scene) { + enum_callback(_self, _scene.get(), param); + } + if (_source) { + enum_callback(_self, _source->get(), param); } } void source::mirror::mirror_instance::enum_all_sources(obs_source_enum_proc_t enum_callback, void* param) { - if (this->_scene) { - enum_callback(this->_self, this->_scene->get(), param); + if (_scene) { + enum_callback(_self, _scene.get(), param); } -} - -void source::mirror::mirror_instance::load(obs_data_t* data) -{ - this->update(data); -} - -void source::mirror::mirror_instance::save(obs_data_t* data) -{ - if (!this->_source_item || !this->_source) { - return; + if (_source) { + enum_callback(_self, _source->get(), param); } - obs_data_set_string(data, ST_SOURCE, obs_source_get_name(_source->get())); } void source::mirror::mirror_instance::on_source_rename(obs::source* source, std::string, std::string) { - obs_data_t* ref = obs_source_get_settings(this->_self); - obs_data_set_string(ref, ST_SOURCE, obs_source_get_name(source->get())); - obs_source_update(this->_self, ref); - obs_data_release(ref); + obs_source_save(_self); } void source::mirror::mirror_instance::on_audio_data(obs::source*, const audio_data* audio, bool) @@ -500,8 +469,8 @@ const char* source::mirror::mirror_factory::get_name() void source::mirror::mirror_factory::get_defaults2(obs_data_t* data) { obs_data_set_default_string(data, ST_SOURCE, ""); - obs_data_set_default_bool(data, ST_AUDIO, false); - obs_data_set_default_int(data, ST_AUDIO_LAYOUT, static_cast(SPEAKERS_UNKNOWN)); + obs_data_set_default_bool(data, ST_SOURCE_AUDIO, false); + obs_data_set_default_int(data, ST_SOURCE_AUDIO_LAYOUT, static_cast(SPEAKERS_UNKNOWN)); obs_data_set_default_bool(data, ST_SCALING, false); obs_data_set_default_string(data, ST_SCALING_SIZE, "100x100"); obs_data_set_default_int(data, ST_SCALING_METHOD, (int64_t)obs_scale_type::OBS_SCALE_BILINEAR); @@ -524,13 +493,13 @@ static bool modified_properties(obs_properties_t* pr, obs_property_t* p, obs_dat obs_source_release(target); } - if (obs_properties_get(pr, ST_AUDIO) == p) { - bool show = obs_data_get_bool(data, ST_AUDIO); - obs_property_set_visible(obs_properties_get(pr, ST_AUDIO_LAYOUT), show); + if (obs_properties_get(pr, ST_SOURCE_AUDIO) == p) { + bool show = obs_data_get_bool(data, ST_SOURCE_AUDIO); + obs_property_set_visible(obs_properties_get(pr, ST_SOURCE_AUDIO_LAYOUT), show); return true; } - if (obs_properties_get(pr, ST_SCALING) == p) { + if (util::are_property_groups_broken() && (obs_properties_get(pr, ST_SCALING) == p)) { bool show = obs_data_get_bool(data, ST_SCALING); obs_property_set_visible(obs_properties_get(pr, ST_SCALING_METHOD), show); obs_property_set_visible(obs_properties_get(pr, ST_SCALING_SIZE), show); @@ -579,90 +548,145 @@ obs_properties_t* source::mirror::mirror_factory::get_properties2(source::mirror obs_properties_t* pr = obs_properties_create(); obs_property_t* p = nullptr; - p = obs_properties_add_list(pr, ST_SOURCE, D_TRANSLATE(ST_SOURCE), OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_STRING); - obs_property_set_long_description(p, D_TRANSLATE(D_DESC(ST_SOURCE))); - obs_property_set_modified_callback(p, modified_properties); - obs_property_list_add_string(p, "", ""); - obs::source_tracker::get()->enumerate( - [&p](std::string name, obs_source_t*) { - obs_property_list_add_string(p, std::string(name + " (Source)").c_str(), name.c_str()); - return false; - }, - obs::source_tracker::filter_sources); - obs::source_tracker::get()->enumerate( - [&p](std::string name, obs_source_t*) { - obs_property_list_add_string(p, std::string(name + " (Scene)").c_str(), name.c_str()); - return false; - }, - obs::source_tracker::filter_scenes); + { + obs_properties_t* grp = pr; + if (!util::are_property_groups_broken()) { + grp = obs_properties_create(); + p = obs_properties_add_group(pr, ST_SOURCE, D_TRANSLATE(ST_SOURCE), OBS_GROUP_NORMAL, grp); + obs_property_set_long_description(p, D_TRANSLATE(D_DESC(ST_SOURCE))); + } - p = obs_properties_add_text(pr, ST_SOURCE_SIZE, D_TRANSLATE(ST_SOURCE_SIZE), OBS_TEXT_DEFAULT); - obs_property_set_long_description(p, D_TRANSLATE(D_DESC(ST_SOURCE_SIZE))); - obs_property_set_enabled(p, false); + { + p = obs_properties_add_list(grp, ST_SOURCE, D_TRANSLATE(ST_SOURCE), OBS_COMBO_TYPE_LIST, + OBS_COMBO_FORMAT_STRING); + obs_property_set_long_description(p, D_TRANSLATE(D_DESC(ST_SOURCE))); + obs_property_set_modified_callback(p, modified_properties); - p = obs_properties_add_bool(pr, ST_AUDIO, D_TRANSLATE(ST_AUDIO)); - obs_property_set_long_description(p, D_TRANSLATE(D_DESC(ST_AUDIO))); - obs_property_set_modified_callback(p, modified_properties); - p = obs_properties_add_list(pr, ST_AUDIO_LAYOUT, D_TRANSLATE(ST_AUDIO_LAYOUT), OBS_COMBO_TYPE_LIST, - OBS_COMBO_FORMAT_INT); - obs_property_list_add_int(p, D_TRANSLATE(ST_AUDIO_LAYOUT_(Unknown)), static_cast(SPEAKERS_UNKNOWN)); - obs_property_list_add_int(p, D_TRANSLATE(ST_AUDIO_LAYOUT_(Mono)), static_cast(SPEAKERS_MONO)); - obs_property_list_add_int(p, D_TRANSLATE(ST_AUDIO_LAYOUT_(Stereo)), static_cast(SPEAKERS_STEREO)); - obs_property_list_add_int(p, D_TRANSLATE(ST_AUDIO_LAYOUT_(StereoLFE)), static_cast(SPEAKERS_2POINT1)); - obs_property_list_add_int(p, D_TRANSLATE(ST_AUDIO_LAYOUT_(Quadraphonic)), static_cast(SPEAKERS_4POINT0)); - obs_property_list_add_int(p, D_TRANSLATE(ST_AUDIO_LAYOUT_(QuadraphonicLFE)), - static_cast(SPEAKERS_4POINT1)); - obs_property_list_add_int(p, D_TRANSLATE(ST_AUDIO_LAYOUT_(Surround)), static_cast(SPEAKERS_5POINT1)); - obs_property_list_add_int(p, D_TRANSLATE(ST_AUDIO_LAYOUT_(FullSurround)), static_cast(SPEAKERS_7POINT1)); - obs_property_set_long_description(p, D_TRANSLATE(D_DESC(ST_AUDIO_LAYOUT))); + obs_property_list_add_string(p, "", ""); + obs::source_tracker::get()->enumerate( + [&p](std::string name, obs_source_t*) { + std::stringstream sstr; + sstr << name << " (" << D_TRANSLATE(S_SOURCETYPE_SOURCE) << ")"; + obs_property_list_add_string(p, sstr.str().c_str(), name.c_str()); + return false; + }, + obs::source_tracker::filter_sources); + obs::source_tracker::get()->enumerate( + [&p](std::string name, obs_source_t*) { + std::stringstream sstr; + sstr << name << " (" << D_TRANSLATE(S_SOURCETYPE_SCENE) << ")"; + obs_property_list_add_string(p, sstr.str().c_str(), name.c_str()); + return false; + }, + obs::source_tracker::filter_scenes); + } - p = obs_properties_add_bool(pr, ST_SCALING, D_TRANSLATE(ST_SCALING)); - obs_property_set_long_description(p, D_TRANSLATE(D_DESC(ST_SCALING))); - obs_property_set_modified_callback(p, modified_properties); + p = obs_properties_add_text(grp, ST_SOURCE_SIZE, D_TRANSLATE(ST_SOURCE_SIZE), OBS_TEXT_DEFAULT); + obs_property_set_long_description(p, D_TRANSLATE(D_DESC(ST_SOURCE_SIZE))); + obs_property_set_enabled(p, false); - p = obs_properties_add_list(pr, ST_SCALING_METHOD, D_TRANSLATE(ST_SCALING_METHOD), OBS_COMBO_TYPE_LIST, - OBS_COMBO_FORMAT_INT); - obs_property_set_long_description(p, D_TRANSLATE(D_DESC(ST_SCALING_METHOD))); - obs_property_set_modified_callback(p, modified_properties); - obs_property_list_add_int(p, D_TRANSLATE(ST_SCALING_METHOD_POINT), (int64_t)obs_scale_type::OBS_SCALE_POINT); - obs_property_list_add_int(p, D_TRANSLATE(ST_SCALING_METHOD_BILINEAR), (int64_t)obs_scale_type::OBS_SCALE_BILINEAR); - obs_property_list_add_int(p, D_TRANSLATE(ST_SCALING_METHOD_BICUBIC), (int64_t)obs_scale_type::OBS_SCALE_BICUBIC); - obs_property_list_add_int(p, D_TRANSLATE(ST_SCALING_METHOD_LANCZOS), (int64_t)obs_scale_type::OBS_SCALE_LANCZOS); + p = obs_properties_add_bool(grp, ST_SOURCE_AUDIO, D_TRANSLATE(ST_SOURCE_AUDIO)); + obs_property_set_long_description(p, D_TRANSLATE(D_DESC(ST_SOURCE_AUDIO))); + obs_property_set_modified_callback(p, modified_properties); - p = obs_properties_add_text(pr, ST_SCALING_SIZE, D_TRANSLATE(ST_SCALING_SIZE), OBS_TEXT_DEFAULT); - obs_property_set_long_description(p, D_TRANSLATE(D_DESC(ST_SCALING_SIZE))); + { + p = obs_properties_add_list(grp, ST_SOURCE_AUDIO_LAYOUT, D_TRANSLATE(ST_SOURCE_AUDIO_LAYOUT), + OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT); + obs_property_list_add_int(p, D_TRANSLATE(ST_SOURCE_AUDIO_LAYOUT_(Unknown)), + static_cast(SPEAKERS_UNKNOWN)); + obs_property_list_add_int(p, D_TRANSLATE(ST_SOURCE_AUDIO_LAYOUT_(Mono)), + static_cast(SPEAKERS_MONO)); + obs_property_list_add_int(p, D_TRANSLATE(ST_SOURCE_AUDIO_LAYOUT_(Stereo)), + static_cast(SPEAKERS_STEREO)); + obs_property_list_add_int(p, D_TRANSLATE(ST_SOURCE_AUDIO_LAYOUT_(StereoLFE)), + static_cast(SPEAKERS_2POINT1)); + obs_property_list_add_int(p, D_TRANSLATE(ST_SOURCE_AUDIO_LAYOUT_(Quadraphonic)), + static_cast(SPEAKERS_4POINT0)); + obs_property_list_add_int(p, D_TRANSLATE(ST_SOURCE_AUDIO_LAYOUT_(QuadraphonicLFE)), + static_cast(SPEAKERS_4POINT1)); + obs_property_list_add_int(p, D_TRANSLATE(ST_SOURCE_AUDIO_LAYOUT_(Surround)), + static_cast(SPEAKERS_5POINT1)); + obs_property_list_add_int(p, D_TRANSLATE(ST_SOURCE_AUDIO_LAYOUT_(FullSurround)), + static_cast(SPEAKERS_7POINT1)); + obs_property_set_long_description(p, D_TRANSLATE(D_DESC(ST_SOURCE_AUDIO_LAYOUT))); + } + } - p = obs_properties_add_bool(pr, ST_SCALING_TRANSFORMKEEPORIGINAL, D_TRANSLATE(ST_SCALING_TRANSFORMKEEPORIGINAL)); - obs_property_set_long_description(p, D_TRANSLATE(D_DESC(ST_SCALING_TRANSFORMKEEPORIGINAL))); + { + obs_properties_t* grp = pr; + if (!util::are_property_groups_broken()) { + grp = obs_properties_create(); + p = obs_properties_add_group(pr, ST_SCALING, D_TRANSLATE(ST_SCALING), OBS_GROUP_CHECKABLE, grp); + obs_property_set_long_description(p, D_TRANSLATE(D_DESC(ST_SCALING))); + } else { + p = obs_properties_add_bool(pr, ST_SCALING, D_TRANSLATE(ST_SCALING)); + obs_property_set_long_description(p, D_TRANSLATE(D_DESC(ST_SCALING))); + obs_property_set_modified_callback(p, modified_properties); + } - p = obs_properties_add_list(pr, ST_SCALING_BOUNDS, D_TRANSLATE(ST_SCALING_BOUNDS), OBS_COMBO_TYPE_LIST, - OBS_COMBO_FORMAT_INT); - obs_property_set_long_description(p, D_TRANSLATE(D_DESC(ST_SCALING_BOUNDS))); - obs_property_set_modified_callback(p, modified_properties); - obs_property_list_add_int(p, D_TRANSLATE(ST_SCALING_BOUNDS_STRETCH), (int64_t)obs_bounds_type::OBS_BOUNDS_STRETCH); - obs_property_list_add_int(p, D_TRANSLATE(ST_SCALING_BOUNDS_FIT), (int64_t)obs_bounds_type::OBS_BOUNDS_SCALE_INNER); - obs_property_list_add_int(p, D_TRANSLATE(ST_SCALING_BOUNDS_FILL), (int64_t)obs_bounds_type::OBS_BOUNDS_SCALE_OUTER); - obs_property_list_add_int(p, D_TRANSLATE(ST_SCALING_BOUNDS_FILLWIDTH), - (int64_t)obs_bounds_type::OBS_BOUNDS_SCALE_TO_WIDTH); - obs_property_list_add_int(p, D_TRANSLATE(ST_SCALING_BOUNDS_FILLHEIGHT), - (int64_t)obs_bounds_type::OBS_BOUNDS_SCALE_TO_HEIGHT); + p = obs_properties_add_bool(grp, ST_SCALING_TRANSFORMKEEPORIGINAL, + D_TRANSLATE(ST_SCALING_TRANSFORMKEEPORIGINAL)); + obs_property_set_long_description(p, D_TRANSLATE(D_DESC(ST_SCALING_TRANSFORMKEEPORIGINAL))); + obs_property_set_modified_callback(p, modified_properties); - p = obs_properties_add_list(pr, ST_SCALING_ALIGNMENT, D_TRANSLATE(ST_SCALING_ALIGNMENT), OBS_COMBO_TYPE_LIST, - OBS_COMBO_FORMAT_INT); - obs_property_set_long_description(p, D_TRANSLATE(D_DESC(ST_SCALING_ALIGNMENT))); - obs_property_list_add_int(p, obs_module_recursive_text("\\@" S_ALIGNMENT_LEFT "\\@ \\@" S_ALIGNMENT_TOP "\\@"), - OBS_ALIGN_LEFT | OBS_ALIGN_TOP); - obs_property_list_add_int(p, obs_module_recursive_text("\\@" S_ALIGNMENT_TOP "\\@"), OBS_ALIGN_TOP); - obs_property_list_add_int(p, obs_module_recursive_text("\\@" S_ALIGNMENT_RIGHT "\\@ \\@" S_ALIGNMENT_TOP "\\@"), - OBS_ALIGN_RIGHT | OBS_ALIGN_TOP); - obs_property_list_add_int(p, obs_module_recursive_text("\\@" S_ALIGNMENT_LEFT "\\@"), OBS_ALIGN_LEFT); - obs_property_list_add_int(p, obs_module_recursive_text("\\@" S_ALIGNMENT_CENTER "\\@"), OBS_ALIGN_CENTER); - obs_property_list_add_int(p, obs_module_recursive_text("\\@" S_ALIGNMENT_RIGHT "\\@"), OBS_ALIGN_RIGHT); - obs_property_list_add_int(p, obs_module_recursive_text("\\@" S_ALIGNMENT_LEFT "\\@ \\@" S_ALIGNMENT_BOTTOM "\\@"), - OBS_ALIGN_LEFT | OBS_ALIGN_BOTTOM); - obs_property_list_add_int(p, obs_module_recursive_text("\\@" S_ALIGNMENT_BOTTOM "\\@"), OBS_ALIGN_BOTTOM); - obs_property_list_add_int(p, obs_module_recursive_text("\\@" S_ALIGNMENT_RIGHT "\\@ \\@" S_ALIGNMENT_BOTTOM "\\@"), - OBS_ALIGN_RIGHT | OBS_ALIGN_BOTTOM); + p = obs_properties_add_text(grp, ST_SCALING_SIZE, D_TRANSLATE(ST_SCALING_SIZE), OBS_TEXT_DEFAULT); + obs_property_set_long_description(p, D_TRANSLATE(D_DESC(ST_SCALING_SIZE))); + + { + p = obs_properties_add_list(grp, ST_SCALING_METHOD, D_TRANSLATE(ST_SCALING_METHOD), OBS_COMBO_TYPE_LIST, + OBS_COMBO_FORMAT_INT); + obs_property_set_long_description(p, D_TRANSLATE(D_DESC(ST_SCALING_METHOD))); + obs_property_set_modified_callback(p, modified_properties); + obs_property_list_add_int(p, D_TRANSLATE(ST_SCALING_METHOD_POINT), + (int64_t)obs_scale_type::OBS_SCALE_POINT); + obs_property_list_add_int(p, D_TRANSLATE(ST_SCALING_METHOD_BILINEAR), + (int64_t)obs_scale_type::OBS_SCALE_BILINEAR); + obs_property_list_add_int(p, D_TRANSLATE(ST_SCALING_METHOD_BICUBIC), + (int64_t)obs_scale_type::OBS_SCALE_BICUBIC); + obs_property_list_add_int(p, D_TRANSLATE(ST_SCALING_METHOD_LANCZOS), + (int64_t)obs_scale_type::OBS_SCALE_LANCZOS); + } + + { + p = obs_properties_add_list(grp, ST_SCALING_BOUNDS, D_TRANSLATE(ST_SCALING_BOUNDS), OBS_COMBO_TYPE_LIST, + OBS_COMBO_FORMAT_INT); + obs_property_set_long_description(p, D_TRANSLATE(D_DESC(ST_SCALING_BOUNDS))); + obs_property_set_modified_callback(p, modified_properties); + obs_property_list_add_int(p, D_TRANSLATE(ST_SCALING_BOUNDS_STRETCH), + (int64_t)obs_bounds_type::OBS_BOUNDS_STRETCH); + obs_property_list_add_int(p, D_TRANSLATE(ST_SCALING_BOUNDS_FIT), + (int64_t)obs_bounds_type::OBS_BOUNDS_SCALE_INNER); + obs_property_list_add_int(p, D_TRANSLATE(ST_SCALING_BOUNDS_FILL), + (int64_t)obs_bounds_type::OBS_BOUNDS_SCALE_OUTER); + obs_property_list_add_int(p, D_TRANSLATE(ST_SCALING_BOUNDS_FILLWIDTH), + (int64_t)obs_bounds_type::OBS_BOUNDS_SCALE_TO_WIDTH); + obs_property_list_add_int(p, D_TRANSLATE(ST_SCALING_BOUNDS_FILLHEIGHT), + (int64_t)obs_bounds_type::OBS_BOUNDS_SCALE_TO_HEIGHT); + } + + { + p = obs_properties_add_list(grp, ST_SCALING_ALIGNMENT, D_TRANSLATE(ST_SCALING_ALIGNMENT), + OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT); + obs_property_set_long_description(p, D_TRANSLATE(D_DESC(ST_SCALING_ALIGNMENT))); + obs_property_list_add_int(p, + obs_module_recursive_text("\\@" S_ALIGNMENT_LEFT "\\@ \\@" S_ALIGNMENT_TOP "\\@"), + OBS_ALIGN_LEFT | OBS_ALIGN_TOP); + obs_property_list_add_int(p, obs_module_recursive_text("\\@" S_ALIGNMENT_TOP "\\@"), OBS_ALIGN_TOP); + obs_property_list_add_int( + p, obs_module_recursive_text("\\@" S_ALIGNMENT_RIGHT "\\@ \\@" S_ALIGNMENT_TOP "\\@"), + OBS_ALIGN_RIGHT | OBS_ALIGN_TOP); + obs_property_list_add_int(p, obs_module_recursive_text("\\@" S_ALIGNMENT_LEFT "\\@"), OBS_ALIGN_LEFT); + obs_property_list_add_int(p, obs_module_recursive_text("\\@" S_ALIGNMENT_CENTER "\\@"), OBS_ALIGN_CENTER); + obs_property_list_add_int(p, obs_module_recursive_text("\\@" S_ALIGNMENT_RIGHT "\\@"), OBS_ALIGN_RIGHT); + obs_property_list_add_int( + p, obs_module_recursive_text("\\@" S_ALIGNMENT_LEFT "\\@ \\@" S_ALIGNMENT_BOTTOM "\\@"), + OBS_ALIGN_LEFT | OBS_ALIGN_BOTTOM); + obs_property_list_add_int(p, obs_module_recursive_text("\\@" S_ALIGNMENT_BOTTOM "\\@"), OBS_ALIGN_BOTTOM); + obs_property_list_add_int( + p, obs_module_recursive_text("\\@" S_ALIGNMENT_RIGHT "\\@ \\@" S_ALIGNMENT_BOTTOM "\\@"), + OBS_ALIGN_RIGHT | OBS_ALIGN_BOTTOM); + } + } return pr; } diff --git a/source/sources/source-mirror.hpp b/source/sources/source-mirror.hpp index e385b6ba..1f0eaf08 100644 --- a/source/sources/source-mirror.hpp +++ b/source/sources/source-mirror.hpp @@ -49,26 +49,13 @@ namespace source { }; class mirror_instance : public obs::source_instance { - bool _active; - float_t _tick; + // Source + std::shared_ptr _source; + std::string _source_name; - // Video Rendering - std::shared_ptr _scene; - std::shared_ptr _scene_texture_renderer; - std::shared_ptr _scene_texture; - bool _scene_rendered; - uint32_t _rescale_alignment; - - // Rescaling - bool _rescale_enabled; - uint32_t _rescale_width; - uint32_t _rescale_height; - bool _rescale_keep_orig_size; - obs_scale_type _rescale_type; - obs_bounds_type _rescale_bounds; - - // Audio Rendering + // Audio bool _audio_enabled; + speaker_layout _audio_layout; std::condition_variable _audio_notify; std::thread _audio_thread; bool _audio_kill_thread; @@ -77,16 +64,29 @@ namespace source { std::mutex _audio_lock_capturer; std::queue> _audio_data_queue; std::queue> _audio_data_free_queue; - speaker_layout _audio_layout; - // Input - std::shared_ptr _source; - obs_sceneitem_t* _source_item; - std::string _source_name; + // Scaling + bool _rescale_enabled; + uint32_t _rescale_width; + uint32_t _rescale_height; + bool _rescale_keep_orig_size; + obs_scale_type _rescale_type; + obs_bounds_type _rescale_bounds; + uint32_t _rescale_alignment; + + // Caching + bool _cache_enabled; + bool _cache_rendered; + std::shared_ptr _cache_renderer; + std::shared_ptr _cache_texture; + + // Scene + std::shared_ptr _scene; + std::shared_ptr _source_item; private: - void release_input(); - void acquire_input(std::string source_name); + void release(); + void acquire(std::string source_name); public: mirror_instance(obs_data_t* settings, obs_source_t* self); @@ -99,9 +99,6 @@ namespace source { virtual void load(obs_data_t*) override; virtual void save(obs_data_t*) override; - virtual void activate() override; - virtual void deactivate() override; - virtual void video_tick(float) override; virtual void video_render(gs_effect_t*) override; @@ -114,7 +111,8 @@ namespace source { void on_audio_data(obs::source* source, const audio_data* audio, bool muted); }; - class mirror_factory : public obs::source_factory { + class mirror_factory + : public obs::source_factory { static std::shared_ptr factory_instance; public: // Singleton @@ -137,11 +135,11 @@ namespace source { mirror_factory(); virtual ~mirror_factory() override; - virtual const char* get_name(); + virtual const char* get_name() override; - virtual void get_defaults2(obs_data_t* data); + virtual void get_defaults2(obs_data_t* data) override; - virtual obs_properties_t* get_properties2(source::mirror::mirror_instance* data); + virtual obs_properties_t* get_properties2(source::mirror::mirror_instance* data) override; }; } // namespace mirror }; // namespace source diff --git a/source/strings.hpp b/source/strings.hpp index 2a6ec2a3..aae4e4ac 100644 --- a/source/strings.hpp +++ b/source/strings.hpp @@ -33,6 +33,7 @@ #define S_FILEFILTERS_ANY "*.*" #define S_VERSION "Version" +#define S_COMMIT "Commit" #define S_ADVANCED "Advanced" @@ -78,5 +79,3 @@ #define S_CHANNEL_GREEN "Channel.Green" #define S_CHANNEL_BLUE "Channel.Blue" #define S_CHANNEL_ALPHA "Channel.Alpha" - -