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.
This commit is contained in:
Michael Fabian 'Xaymar' Dirks 2019-10-16 04:24:18 +02:00
parent 52c9e372ba
commit bacf52f9b6
4 changed files with 355 additions and 333 deletions

View file

@ -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"

View file

@ -22,6 +22,7 @@
#include <cstring>
#include <functional>
#include <memory>
#include <sstream>
#include <stdexcept>
#include <vector>
#include "obs/obs-source-tracker.hpp"
@ -33,6 +34,7 @@
#pragma warning(push)
#pragma warning(disable : 4201)
#endif
#include <graphics/matrix4.h>
#include <media-io/audio-io.h>
#include <obs-config.h>
#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("<Source Mirror:%s> 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<obs_source_t> source = std::shared_ptr<obs_source_t>(
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("<Source Mirror:%s> 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<obs::source> new_source = std::make_shared<obs::source>(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("<Source Mirror:%s> 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<obs::source>(source.get(), true, true);
_source_name = obs_source_get_name(source.get());
_source_item = std::shared_ptr<obs_sceneitem_t>(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::source>(obs_scene_get_source(obs_scene_create_private("Source Mirror Internal Scene")));
this->_scene_texture_renderer =
std::make_shared<gfx::source_texture>(this->_scene, std::make_shared<obs::source>(this->_self, false, false));
// Create Internal Scene
_scene = std::shared_ptr<obs_source_t>(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<gfx::source_texture>(_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<uint64_t>(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<speaker_layout>(obs_data_get_int(data, ST_AUDIO_LAYOUT));
this->_audio_enabled = obs_data_get_bool(data, ST_SOURCE_AUDIO);
this->_audio_layout = static_cast<speaker_layout>(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<float_t>(_rescale_width), static_cast<float_t>(_rescale_height));
} else {
vec2_set(&info.bounds, static_cast<float_t>(get_width()), static_cast<float_t>(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<int64_t>(SPEAKERS_UNKNOWN));
obs_data_set_default_bool(data, ST_SOURCE_AUDIO, false);
obs_data_set_default_int(data, ST_SOURCE_AUDIO_LAYOUT, static_cast<int64_t>(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<int64_t>(SPEAKERS_UNKNOWN));
obs_property_list_add_int(p, D_TRANSLATE(ST_AUDIO_LAYOUT_(Mono)), static_cast<int64_t>(SPEAKERS_MONO));
obs_property_list_add_int(p, D_TRANSLATE(ST_AUDIO_LAYOUT_(Stereo)), static_cast<int64_t>(SPEAKERS_STEREO));
obs_property_list_add_int(p, D_TRANSLATE(ST_AUDIO_LAYOUT_(StereoLFE)), static_cast<int64_t>(SPEAKERS_2POINT1));
obs_property_list_add_int(p, D_TRANSLATE(ST_AUDIO_LAYOUT_(Quadraphonic)), static_cast<int64_t>(SPEAKERS_4POINT0));
obs_property_list_add_int(p, D_TRANSLATE(ST_AUDIO_LAYOUT_(QuadraphonicLFE)),
static_cast<int64_t>(SPEAKERS_4POINT1));
obs_property_list_add_int(p, D_TRANSLATE(ST_AUDIO_LAYOUT_(Surround)), static_cast<int64_t>(SPEAKERS_5POINT1));
obs_property_list_add_int(p, D_TRANSLATE(ST_AUDIO_LAYOUT_(FullSurround)), static_cast<int64_t>(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<int64_t>(SPEAKERS_UNKNOWN));
obs_property_list_add_int(p, D_TRANSLATE(ST_SOURCE_AUDIO_LAYOUT_(Mono)),
static_cast<int64_t>(SPEAKERS_MONO));
obs_property_list_add_int(p, D_TRANSLATE(ST_SOURCE_AUDIO_LAYOUT_(Stereo)),
static_cast<int64_t>(SPEAKERS_STEREO));
obs_property_list_add_int(p, D_TRANSLATE(ST_SOURCE_AUDIO_LAYOUT_(StereoLFE)),
static_cast<int64_t>(SPEAKERS_2POINT1));
obs_property_list_add_int(p, D_TRANSLATE(ST_SOURCE_AUDIO_LAYOUT_(Quadraphonic)),
static_cast<int64_t>(SPEAKERS_4POINT0));
obs_property_list_add_int(p, D_TRANSLATE(ST_SOURCE_AUDIO_LAYOUT_(QuadraphonicLFE)),
static_cast<int64_t>(SPEAKERS_4POINT1));
obs_property_list_add_int(p, D_TRANSLATE(ST_SOURCE_AUDIO_LAYOUT_(Surround)),
static_cast<int64_t>(SPEAKERS_5POINT1));
obs_property_list_add_int(p, D_TRANSLATE(ST_SOURCE_AUDIO_LAYOUT_(FullSurround)),
static_cast<int64_t>(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;
}

View file

@ -49,26 +49,13 @@ namespace source {
};
class mirror_instance : public obs::source_instance {
bool _active;
float_t _tick;
// Source
std::shared_ptr<obs::source> _source;
std::string _source_name;
// Video Rendering
std::shared_ptr<obs::source> _scene;
std::shared_ptr<gfx::source_texture> _scene_texture_renderer;
std::shared_ptr<gs::texture> _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<std::shared_ptr<mirror_audio_data>> _audio_data_queue;
std::queue<std::shared_ptr<mirror_audio_data>> _audio_data_free_queue;
speaker_layout _audio_layout;
// Input
std::shared_ptr<obs::source> _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<gfx::source_texture> _cache_renderer;
std::shared_ptr<gs::texture> _cache_texture;
// Scene
std::shared_ptr<obs_source_t> _scene;
std::shared_ptr<obs_sceneitem_t> _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<source::mirror::mirror_factory, source::mirror::mirror_instance> {
class mirror_factory
: public obs::source_factory<source::mirror::mirror_factory, source::mirror::mirror_instance> {
static std::shared_ptr<source::mirror::mirror_factory> 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

View file

@ -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"