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.Size.Description="The size of the source being mirrored. (Automatically updated)"
Source.Mirror.Source.Audio="Enable Audio" Source.Mirror.Source.Audio="Enable Audio"
Source.Mirror.Source.Audio.Description="Enables audio mirroring from this source." Source.Mirror.Source.Audio.Description="Enables audio mirroring from this source."
Source.Mirror.Audio.Layout="Audio Layout" Source.Mirror.Source.Audio.Layout="Audio Layout"
Source.Mirror.Audio.Layout.Unknown="Unknown" 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.Audio.Layout.Mono="Mono" Source.Mirror.Source.Audio.Layout.Unknown="Unknown"
Source.Mirror.Audio.Layout.Stereo="Stereo" Source.Mirror.Source.Audio.Layout.Mono="Mono"
Source.Mirror.Audio.Layout.StereoLFE="Stereo with LFE" Source.Mirror.Source.Audio.Layout.Stereo="Stereo"
Source.Mirror.Audio.Layout.Quadraphonic="Quadraphonic" Source.Mirror.Source.Audio.Layout.StereoLFE="Stereo with LFE"
Source.Mirror.Audio.Layout.QuadraphonicLFE="Quadraphonic With LFE" Source.Mirror.Source.Audio.Layout.Quadraphonic="Quadraphonic"
Source.Mirror.Audio.Layout.Surround="Surround" Source.Mirror.Source.Audio.Layout.QuadraphonicLFE="Quadraphonic With LFE"
Source.Mirror.Audio.Layout.FullSurround="Full Surround" Source.Mirror.Source.Audio.Layout.Surround="Surround"
Source.Mirror.Source.Audio.Layout.FullSurround="Full Surround"
Source.Mirror.Scaling="Rescale Source" Source.Mirror.Scaling="Rescale Source"
Source.Mirror.Scaling.Description="Should the source be rescaled?" Source.Mirror.Scaling.Description="Should the source be rescaled?"
Source.Mirror.Scaling.Method="Filter" Source.Mirror.Scaling.Method="Filter"

View file

@ -22,6 +22,7 @@
#include <cstring> #include <cstring>
#include <functional> #include <functional>
#include <memory> #include <memory>
#include <sstream>
#include <stdexcept> #include <stdexcept>
#include <vector> #include <vector>
#include "obs/obs-source-tracker.hpp" #include "obs/obs-source-tracker.hpp"
@ -33,6 +34,7 @@
#pragma warning(push) #pragma warning(push)
#pragma warning(disable : 4201) #pragma warning(disable : 4201)
#endif #endif
#include <graphics/matrix4.h>
#include <media-io/audio-io.h> #include <media-io/audio-io.h>
#include <obs-config.h> #include <obs-config.h>
#ifdef _MSC_VER #ifdef _MSC_VER
@ -40,157 +42,153 @@
#endif #endif
#define ST "Source.Mirror" #define ST "Source.Mirror"
#define ST_SOURCE "Source.Mirror.Source" #define ST_SOURCE ST ".Source"
#define ST_SOURCE_SIZE "Source.Mirror.Source.Size" #define ST_SOURCE_SIZE ST_SOURCE ".Size"
#define ST_AUDIO "Source.Mirror.Source.Audio" #define ST_SOURCE_AUDIO ST_SOURCE ".Audio"
#define ST_AUDIO_LAYOUT ST ".Audio.Layout" #define ST_SOURCE_AUDIO_LAYOUT ST_SOURCE_AUDIO ".Layout"
#define ST_AUDIO_LAYOUT_(x) ST_AUDIO_LAYOUT "." D_VSTR(x) #define ST_SOURCE_AUDIO_LAYOUT_(x) ST_SOURCE_AUDIO_LAYOUT "." D_VSTR(x)
#define ST_SCALING "Source.Mirror.Scaling" #define ST_SCALING ST ".Scaling"
#define ST_SCALING_METHOD "Source.Mirror.Scaling.Method" #define ST_SCALING_METHOD ST_SCALING ".Method"
#define ST_SCALING_METHOD_POINT "Source.Mirror.Scaling.Method.Point" #define ST_SCALING_METHOD_POINT ST_SCALING ".Method.Point"
#define ST_SCALING_METHOD_BILINEAR "Source.Mirror.Scaling.Method.Bilinear" #define ST_SCALING_METHOD_BILINEAR ST_SCALING ".Method.Bilinear"
#define ST_SCALING_METHOD_BICUBIC "Source.Mirror.Scaling.Method.Bicubic" #define ST_SCALING_METHOD_BICUBIC ST_SCALING ".Method.Bicubic"
#define ST_SCALING_METHOD_LANCZOS "Source.Mirror.Scaling.Method.Lanczos" #define ST_SCALING_METHOD_LANCZOS ST_SCALING ".Method.Lanczos"
#define ST_SCALING_SIZE "Source.Mirror.Scaling.Size" #define ST_SCALING_SIZE ST_SCALING ".Size"
#define ST_SCALING_TRANSFORMKEEPORIGINAL "Source.Mirror.Scaling.TransformKeepOriginal" #define ST_SCALING_TRANSFORMKEEPORIGINAL ST_SCALING ".TransformKeepOriginal"
#define ST_SCALING_BOUNDS "Source.Mirror.Scaling.Bounds" #define ST_SCALING_BOUNDS ST_SCALING ".Bounds"
#define ST_SCALING_BOUNDS_STRETCH "Source.Mirror.Scaling.Bounds.Stretch" #define ST_SCALING_BOUNDS_STRETCH ST_SCALING ".Bounds.Stretch"
#define ST_SCALING_BOUNDS_FIT "Source.Mirror.Scaling.Bounds.Fit" #define ST_SCALING_BOUNDS_FIT ST_SCALING ".Bounds.Fit"
#define ST_SCALING_BOUNDS_FILL "Source.Mirror.Scaling.Bounds.Fill" #define ST_SCALING_BOUNDS_FILL ST_SCALING ".Bounds.Fill"
#define ST_SCALING_BOUNDS_FILLWIDTH "Source.Mirror.Scaling.Bounds.FillWidth" #define ST_SCALING_BOUNDS_FILLWIDTH ST_SCALING ".Bounds.FillWidth"
#define ST_SCALING_BOUNDS_FILLHEIGHT "Source.Mirror.Scaling.Bounds.FillHeight" #define ST_SCALING_BOUNDS_FILLHEIGHT ST_SCALING ".Bounds.FillHeight"
#define ST_SCALING_ALIGNMENT "Source.Mirror.Scaling.Alignment" #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. _source_item.reset();
if (this->_source_item) { if (_source) {
obs_sceneitem_remove(this->_source_item); _source->events.rename.clear();
this->_source_item = nullptr; _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. using namespace std::placeholders;
obs_source_t* ref_source = obs_get_source_by_name(source_name.c_str());
if (!ref_source) { // Try and get source by name.
// Early-Exit: Unable to find a source with this name, likely has been released. std::shared_ptr<obs_source_t> source = std::shared_ptr<obs_source_t>(
#ifdef _DEBUG obs_get_source_by_name(source_name.c_str()), [](obs_source_t* ref) { obs_source_release(ref); });
P_LOG_DEBUG("<Source Mirror:%s> Unable to find target source '%s'.", obs_source_get_name(this->_self), if (!source) { // If we failed, just exit early.
source_name.c_str());
#endif
return; return;
} else if (ref_source == this->_self) { } else if (source.get() == _self) { // Otherwise, if we somehow found self, also early exit.
// 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);
return; return;
} }
std::shared_ptr<obs::source> new_source = std::make_shared<obs::source>(ref_source, true, false); // 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());
// It looks like everything is in order, so continue now. if (!item) { // Can't add this source to our scene, probably due to one or more issues with it.
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
return; return;
} }
// If everything worked fine, we now set everything up. // It seems everything has worked out, so let's update our state.
this->_source = new_source; _source = std::make_shared<obs::source>(source.get(), true, true);
this->_source->events.rename += std::bind(&source::mirror::mirror_instance::on_source_rename, this, _source_name = obs_source_get_name(source.get());
std::placeholders::_1, std::placeholders::_2, std::placeholders::_3); _source_item = std::shared_ptr<obs_sceneitem_t>(item, [](obs_sceneitem_t* ref) { obs_sceneitem_remove(ref); });
if ((obs_source_get_output_flags(this->_source->get()) & OBS_SOURCE_AUDIO) != 0) {
this->_source->events.audio_data += // And let's hook up all our events too.
std::bind(&source::mirror::mirror_instance::on_audio_data, this, std::placeholders::_1, _source->events.rename.add(std::bind(&source::mirror::mirror_instance::on_source_rename, this, _1, _2, _3));
std::placeholders::_2, std::placeholders::_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) 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), : obs::source_instance(settings, self), _source(), _source_name(), _audio_enabled(), _audio_layout(),
_rescale_enabled(false), _rescale_width(1), _rescale_height(1), _rescale_keep_orig_size(false), _audio_kill_thread(), _audio_have_output(), _rescale_enabled(), _rescale_width(), _rescale_height(),
_rescale_type(obs_scale_type::OBS_SCALE_BICUBIC), _rescale_bounds(obs_bounds_type::OBS_BOUNDS_STRETCH), _rescale_keep_orig_size(), _rescale_type(), _rescale_bounds(), _rescale_alignment(), _cache_enabled(),
_audio_enabled(false), _audio_kill_thread(false), _audio_have_output(false), _audio_layout(SPEAKERS_UNKNOWN), _cache_rendered()
_source_item(nullptr)
{ {
// Initialize Video Rendering // Create Internal Scene
this->_scene = _scene = std::shared_ptr<obs_source_t>(obs_scene_get_source(obs_scene_create_private("")),
std::make_shared<obs::source>(obs_scene_get_source(obs_scene_create_private("Source Mirror Internal Scene"))); [](obs_source_t* ref) { obs_source_release(ref); });
this->_scene_texture_renderer =
std::make_shared<gfx::source_texture>(this->_scene, std::make_shared<obs::source>(this->_self, false, false));
// Initialize Audio Rendering // Create Cache Renderer
this->_audio_thread = std::thread(std::bind(&source::mirror::mirror_instance::audio_output_cb, this)); _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() source::mirror::mirror_instance::~mirror_instance()
{ {
release_input(); release();
// Finalize Audio Rendering // Kill Audio Thread
this->_audio_kill_thread = true; _audio_kill_thread = true;
this->_audio_notify.notify_all(); _audio_notify.notify_all();
if (this->_audio_thread.joinable()) { if (_audio_thread.joinable()) {
this->_audio_thread.join(); _audio_thread.join();
} }
// Finalize Video Rendering // Delete Cache Renderer
this->_scene_texture_renderer.reset(); _cache_renderer.reset();
this->_scene.reset();
// Delete Internal Scene
_scene.reset();
} }
uint32_t source::mirror::mirror_instance::get_width() uint32_t source::mirror::mirror_instance::get_width()
{ {
if (_source) { if (!_source || !_source_item || !(obs_source_get_output_flags(_source->get()) & OBS_SOURCE_VIDEO))
if ((obs_source_get_output_flags(this->_source->get()) & OBS_SOURCE_VIDEO) == 0) { return 0;
return 0; if (_rescale_enabled && _rescale_width > 0 && !_rescale_keep_orig_size)
} return _rescale_width;
if (this->_rescale_enabled && this->_rescale_width > 0 && !this->_rescale_keep_orig_size) { return _source->width();
return this->_rescale_width;
} else {
return _source->width();
}
}
return 0;
} }
uint32_t source::mirror::mirror_instance::get_height() uint32_t source::mirror::mirror_instance::get_height()
{ {
if (_source) { if (!_source || !_source_item || !(obs_source_get_output_flags(_source->get()) & OBS_SOURCE_VIDEO))
if ((obs_source_get_output_flags(this->_source->get()) & OBS_SOURCE_VIDEO) == 0) { return 0;
return 0; if (_rescale_enabled && _rescale_height > 0 && !_rescale_keep_orig_size)
} return _rescale_height;
if (this->_rescale_enabled && this->_rescale_height > 0 && !this->_rescale_keep_orig_size) { return _source->height();
return this->_rescale_height; }
} else {
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) void source::mirror::mirror_instance::update(obs_data_t* data)
{ {
{ // User changed the source we are tracking. convert_config(data);
release_input();
this->_source_name = obs_data_get_string(data, ST_SOURCE); if (this->_source_name != obs_data_get_string(data, ST_SOURCE)) {
if (this->_source_name.length() > 0) { // Mirrored source was changed, release and reacquire.
acquire_input(this->_source_name); release();
}
// Acquire the new source.
acquire(obs_data_get_string(data, ST_SOURCE));
} }
// Audio // Audio
this->_audio_enabled = obs_data_get_bool(data, ST_AUDIO); this->_audio_enabled = obs_data_get_bool(data, ST_SOURCE_AUDIO);
this->_audio_layout = static_cast<speaker_layout>(obs_data_get_int(data, ST_AUDIO_LAYOUT)); this->_audio_layout = static_cast<speaker_layout>(obs_data_get_int(data, ST_SOURCE_AUDIO_LAYOUT));
// Rescaling // Rescaling
this->_rescale_enabled = obs_data_get_bool(data, ST_SCALING); 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; this->update(data);
// No source, delayed acquire.
if (!this->_source_item && this->_source_name.length() > 0) {
this->acquire_input(this->_source_name.c_str());
}
} }
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) void source::mirror::mirror_instance::video_tick(float time)
{ {
this->_tick += time; if (_source_item && ((obs_source_get_output_flags(_source->get()) & OBS_SOURCE_VIDEO) != 0)) {
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)) {
obs_transform_info info; obs_transform_info info;
info.pos.x = 0;
info.pos.y = 0; /// Position, Rotation, Scale, Alignment
info.rot = 0; vec2_set(&info.pos, 0, 0);
info.scale.x = 1.f; info.rot = 0;
info.scale.y = 1.f; vec2_set(&info.scale, 1., 1.);
info.alignment = OBS_ALIGN_LEFT | OBS_ALIGN_TOP; info.alignment = OBS_ALIGN_LEFT | OBS_ALIGN_TOP;
info.bounds.x = float_t(this->get_width());
info.bounds.y = float_t(this->get_height()); /// Bounding Box
info.bounds_alignment = _rescale_alignment; if (_rescale_enabled && _rescale_keep_orig_size) {
info.bounds_type = obs_bounds_type::OBS_BOUNDS_STRETCH; vec2_set(&info.bounds, static_cast<float_t>(_rescale_width), static_cast<float_t>(_rescale_height));
if (this->_rescale_enabled) { } else {
info.bounds_type = this->_rescale_bounds; 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); info.bounds_alignment = _rescale_alignment;
obs_sceneitem_set_scale_filter(this->_source_item, info.bounds_type = OBS_BOUNDS_STRETCH;
this->_rescale_enabled ? this->_rescale_type : obs_scale_type::OBS_SCALE_POINT); 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) void source::mirror::mirror_instance::video_render(gs_effect_t* effect)
{ {
if ((this->_rescale_width == 0) || (this->_rescale_height == 0) || !this->_source_item if (!_source || !_source_item)
|| !this->_scene_texture_renderer || !this->_source) {
return; return;
}
// Don't bother rendering sources that aren't video. if ((obs_source_get_output_flags(_source->get()) & OBS_SOURCE_VIDEO) == 0)
if ((obs_source_get_output_flags(this->_source->get()) & OBS_SOURCE_VIDEO) == 0) {
return; return;
}
GS_DEBUG_MARKER_BEGIN_FORMAT(GS_DEBUG_COLOR_SOURCE, "Source Mirror: %s", obs_source_get_name(_source->get())); 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. // Rendering depends on cached or uncached.
if (!_scene_rendered) { if (_cache_enabled || _rescale_enabled) {
GS_DEBUG_MARKER_BEGIN_FORMAT(GS_DEBUG_COLOR_RENDER_VIDEO, "Cache: %s", if (!_cache_rendered) {
obs_source_get_name(_source->get())); uint32_t width = get_width();
// Override render size if rescaling is enabled. uint32_t height = get_height();
uint32_t render_width = this->_source->width(); if (_rescale_enabled) {
uint32_t render_height = this->_source->height(); width = _rescale_width;
if (_rescale_enabled) { height = _rescale_height;
render_width = _rescale_width; }
render_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) { if (!_cache_texture)
// Don't render if width or height are 0.
return; return;
}
try { if (!effect)
_scene_texture = this->_scene_texture_renderer->render(render_width, render_height); effect = obs_get_base_effect(OBS_EFFECT_DEFAULT);
_scene_rendered = true;
} catch (...) { GS_DEBUG_MARKER_BEGIN(GS_DEBUG_COLOR_ITEM_TEXTURE, "render_cache");
// If we fail to render the source, just render nothing. gs_effect_set_texture(gs_effect_get_param_by_name(effect, "image"), _cache_texture->get_object());
return; while (gs_effect_loop(effect, "Draw")) {
gs_draw_sprite(nullptr, 0, get_width(), get_height());
} }
GS_DEBUG_MARKER_END(); GS_DEBUG_MARKER_END();
} } else {
obs_source_video_render(_scene.get());
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());
}
} }
GS_DEBUG_MARKER_END(); GS_DEBUG_MARKER_END();
} }
void source::mirror::mirror_instance::audio_output_cb() noexcept try { 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) void source::mirror::mirror_instance::enum_active_sources(obs_source_enum_proc_t enum_callback, void* param)
{ {
if (this->_scene) { if (_scene) {
enum_callback(this->_self, this->_scene->get(), param); 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) void source::mirror::mirror_instance::enum_all_sources(obs_source_enum_proc_t enum_callback, void* param)
{ {
if (this->_scene) { if (_scene) {
enum_callback(this->_self, this->_scene->get(), param); enum_callback(_self, _scene.get(), param);
} }
} if (_source) {
enum_callback(_self, _source->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;
} }
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) 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_source_save(_self);
obs_data_set_string(ref, ST_SOURCE, obs_source_get_name(source->get()));
obs_source_update(this->_self, ref);
obs_data_release(ref);
} }
void source::mirror::mirror_instance::on_audio_data(obs::source*, const audio_data* audio, bool) 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) void source::mirror::mirror_factory::get_defaults2(obs_data_t* data)
{ {
obs_data_set_default_string(data, ST_SOURCE, ""); obs_data_set_default_string(data, ST_SOURCE, "");
obs_data_set_default_bool(data, ST_AUDIO, false); obs_data_set_default_bool(data, ST_SOURCE_AUDIO, false);
obs_data_set_default_int(data, ST_AUDIO_LAYOUT, static_cast<int64_t>(SPEAKERS_UNKNOWN)); 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_bool(data, ST_SCALING, false);
obs_data_set_default_string(data, ST_SCALING_SIZE, "100x100"); 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); 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); obs_source_release(target);
} }
if (obs_properties_get(pr, ST_AUDIO) == p) { if (obs_properties_get(pr, ST_SOURCE_AUDIO) == p) {
bool show = obs_data_get_bool(data, ST_AUDIO); bool show = obs_data_get_bool(data, ST_SOURCE_AUDIO);
obs_property_set_visible(obs_properties_get(pr, ST_AUDIO_LAYOUT), show); obs_property_set_visible(obs_properties_get(pr, ST_SOURCE_AUDIO_LAYOUT), show);
return true; 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); 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_METHOD), show);
obs_property_set_visible(obs_properties_get(pr, ST_SCALING_SIZE), 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_properties_t* pr = obs_properties_create();
obs_property_t* p = nullptr; 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_properties_t* grp = pr;
obs_property_set_modified_callback(p, modified_properties); if (!util::are_property_groups_broken()) {
obs_property_list_add_string(p, "", ""); grp = obs_properties_create();
obs::source_tracker::get()->enumerate( p = obs_properties_add_group(pr, ST_SOURCE, D_TRANSLATE(ST_SOURCE), OBS_GROUP_NORMAL, grp);
[&p](std::string name, obs_source_t*) { obs_property_set_long_description(p, D_TRANSLATE(D_DESC(ST_SOURCE)));
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);
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))); p = obs_properties_add_list(grp, ST_SOURCE, D_TRANSLATE(ST_SOURCE), OBS_COMBO_TYPE_LIST,
obs_property_set_enabled(p, false); 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_list_add_string(p, "", "");
obs_property_set_long_description(p, D_TRANSLATE(D_DESC(ST_AUDIO))); obs::source_tracker::get()->enumerate(
obs_property_set_modified_callback(p, modified_properties); [&p](std::string name, obs_source_t*) {
p = obs_properties_add_list(pr, ST_AUDIO_LAYOUT, D_TRANSLATE(ST_AUDIO_LAYOUT), OBS_COMBO_TYPE_LIST, std::stringstream sstr;
OBS_COMBO_FORMAT_INT); sstr << name << " (" << D_TRANSLATE(S_SOURCETYPE_SOURCE) << ")";
obs_property_list_add_int(p, D_TRANSLATE(ST_AUDIO_LAYOUT_(Unknown)), static_cast<int64_t>(SPEAKERS_UNKNOWN)); obs_property_list_add_string(p, sstr.str().c_str(), name.c_str());
obs_property_list_add_int(p, D_TRANSLATE(ST_AUDIO_LAYOUT_(Mono)), static_cast<int64_t>(SPEAKERS_MONO)); return false;
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::source_tracker::filter_sources);
obs_property_list_add_int(p, D_TRANSLATE(ST_AUDIO_LAYOUT_(Quadraphonic)), static_cast<int64_t>(SPEAKERS_4POINT0)); obs::source_tracker::get()->enumerate(
obs_property_list_add_int(p, D_TRANSLATE(ST_AUDIO_LAYOUT_(QuadraphonicLFE)), [&p](std::string name, obs_source_t*) {
static_cast<int64_t>(SPEAKERS_4POINT1)); std::stringstream sstr;
obs_property_list_add_int(p, D_TRANSLATE(ST_AUDIO_LAYOUT_(Surround)), static_cast<int64_t>(SPEAKERS_5POINT1)); sstr << name << " (" << D_TRANSLATE(S_SOURCETYPE_SCENE) << ")";
obs_property_list_add_int(p, D_TRANSLATE(ST_AUDIO_LAYOUT_(FullSurround)), static_cast<int64_t>(SPEAKERS_7POINT1)); obs_property_list_add_string(p, sstr.str().c_str(), name.c_str());
obs_property_set_long_description(p, D_TRANSLATE(D_DESC(ST_AUDIO_LAYOUT))); return false;
},
obs::source_tracker::filter_scenes);
}
p = obs_properties_add_bool(pr, ST_SCALING, D_TRANSLATE(ST_SCALING)); 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_SCALING))); obs_property_set_long_description(p, D_TRANSLATE(D_DESC(ST_SOURCE_SIZE)));
obs_property_set_modified_callback(p, modified_properties); obs_property_set_enabled(p, false);
p = obs_properties_add_list(pr, ST_SCALING_METHOD, D_TRANSLATE(ST_SCALING_METHOD), OBS_COMBO_TYPE_LIST, p = obs_properties_add_bool(grp, ST_SOURCE_AUDIO, D_TRANSLATE(ST_SOURCE_AUDIO));
OBS_COMBO_FORMAT_INT); obs_property_set_long_description(p, D_TRANSLATE(D_DESC(ST_SOURCE_AUDIO)));
obs_property_set_long_description(p, D_TRANSLATE(D_DESC(ST_SCALING_METHOD))); obs_property_set_modified_callback(p, modified_properties);
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_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, p = obs_properties_add_bool(grp, ST_SCALING_TRANSFORMKEEPORIGINAL,
OBS_COMBO_FORMAT_INT); D_TRANSLATE(ST_SCALING_TRANSFORMKEEPORIGINAL));
obs_property_set_long_description(p, D_TRANSLATE(D_DESC(ST_SCALING_BOUNDS))); obs_property_set_long_description(p, D_TRANSLATE(D_DESC(ST_SCALING_TRANSFORMKEEPORIGINAL)));
obs_property_set_modified_callback(p, modified_properties); 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(pr, ST_SCALING_ALIGNMENT, D_TRANSLATE(ST_SCALING_ALIGNMENT), OBS_COMBO_TYPE_LIST, p = obs_properties_add_text(grp, ST_SCALING_SIZE, D_TRANSLATE(ST_SCALING_SIZE), OBS_TEXT_DEFAULT);
OBS_COMBO_FORMAT_INT); obs_property_set_long_description(p, D_TRANSLATE(D_DESC(ST_SCALING_SIZE)));
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); p = obs_properties_add_list(grp, ST_SCALING_METHOD, D_TRANSLATE(ST_SCALING_METHOD), OBS_COMBO_TYPE_LIST,
obs_property_list_add_int(p, obs_module_recursive_text("\\@" S_ALIGNMENT_TOP "\\@"), OBS_ALIGN_TOP); OBS_COMBO_FORMAT_INT);
obs_property_list_add_int(p, obs_module_recursive_text("\\@" S_ALIGNMENT_RIGHT "\\@ \\@" S_ALIGNMENT_TOP "\\@"), obs_property_set_long_description(p, D_TRANSLATE(D_DESC(ST_SCALING_METHOD)));
OBS_ALIGN_RIGHT | OBS_ALIGN_TOP); obs_property_set_modified_callback(p, modified_properties);
obs_property_list_add_int(p, obs_module_recursive_text("\\@" S_ALIGNMENT_LEFT "\\@"), OBS_ALIGN_LEFT); obs_property_list_add_int(p, D_TRANSLATE(ST_SCALING_METHOD_POINT),
obs_property_list_add_int(p, obs_module_recursive_text("\\@" S_ALIGNMENT_CENTER "\\@"), OBS_ALIGN_CENTER); (int64_t)obs_scale_type::OBS_SCALE_POINT);
obs_property_list_add_int(p, obs_module_recursive_text("\\@" S_ALIGNMENT_RIGHT "\\@"), OBS_ALIGN_RIGHT); obs_property_list_add_int(p, D_TRANSLATE(ST_SCALING_METHOD_BILINEAR),
obs_property_list_add_int(p, obs_module_recursive_text("\\@" S_ALIGNMENT_LEFT "\\@ \\@" S_ALIGNMENT_BOTTOM "\\@"), (int64_t)obs_scale_type::OBS_SCALE_BILINEAR);
OBS_ALIGN_LEFT | OBS_ALIGN_BOTTOM); obs_property_list_add_int(p, D_TRANSLATE(ST_SCALING_METHOD_BICUBIC),
obs_property_list_add_int(p, obs_module_recursive_text("\\@" S_ALIGNMENT_BOTTOM "\\@"), OBS_ALIGN_BOTTOM); (int64_t)obs_scale_type::OBS_SCALE_BICUBIC);
obs_property_list_add_int(p, obs_module_recursive_text("\\@" S_ALIGNMENT_RIGHT "\\@ \\@" S_ALIGNMENT_BOTTOM "\\@"), obs_property_list_add_int(p, D_TRANSLATE(ST_SCALING_METHOD_LANCZOS),
OBS_ALIGN_RIGHT | OBS_ALIGN_BOTTOM); (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; return pr;
} }

View file

@ -49,26 +49,13 @@ namespace source {
}; };
class mirror_instance : public obs::source_instance { class mirror_instance : public obs::source_instance {
bool _active; // Source
float_t _tick; std::shared_ptr<obs::source> _source;
std::string _source_name;
// Video Rendering // Audio
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
bool _audio_enabled; bool _audio_enabled;
speaker_layout _audio_layout;
std::condition_variable _audio_notify; std::condition_variable _audio_notify;
std::thread _audio_thread; std::thread _audio_thread;
bool _audio_kill_thread; bool _audio_kill_thread;
@ -77,16 +64,29 @@ namespace source {
std::mutex _audio_lock_capturer; 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_queue;
std::queue<std::shared_ptr<mirror_audio_data>> _audio_data_free_queue; std::queue<std::shared_ptr<mirror_audio_data>> _audio_data_free_queue;
speaker_layout _audio_layout;
// Input // Scaling
std::shared_ptr<obs::source> _source; bool _rescale_enabled;
obs_sceneitem_t* _source_item; uint32_t _rescale_width;
std::string _source_name; 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: private:
void release_input(); void release();
void acquire_input(std::string source_name); void acquire(std::string source_name);
public: public:
mirror_instance(obs_data_t* settings, obs_source_t* self); 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 load(obs_data_t*) override;
virtual void save(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_tick(float) override;
virtual void video_render(gs_effect_t*) 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); 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; static std::shared_ptr<source::mirror::mirror_factory> factory_instance;
public: // Singleton public: // Singleton
@ -137,11 +135,11 @@ namespace source {
mirror_factory(); mirror_factory();
virtual ~mirror_factory() override; 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 mirror
}; // namespace source }; // namespace source

View file

@ -33,6 +33,7 @@
#define S_FILEFILTERS_ANY "*.*" #define S_FILEFILTERS_ANY "*.*"
#define S_VERSION "Version" #define S_VERSION "Version"
#define S_COMMIT "Commit"
#define S_ADVANCED "Advanced" #define S_ADVANCED "Advanced"
@ -78,5 +79,3 @@
#define S_CHANNEL_GREEN "Channel.Green" #define S_CHANNEL_GREEN "Channel.Green"
#define S_CHANNEL_BLUE "Channel.Blue" #define S_CHANNEL_BLUE "Channel.Blue"
#define S_CHANNEL_ALPHA "Channel.Alpha" #define S_CHANNEL_ALPHA "Channel.Alpha"