source-mirror: Fix potential lockup and rescaling with original size

Getting the resolution of a source is very expensive in libOBS, as libOBS does not cache it and instead always calls into the filters and sources to determine the actual source. This also leads to potential lockups due to the filter list mutex being locked for the target source.

Therefore instead of calling it multiple times, cache the result of the call, if that result is even necessary. This reduces the need to synchronize lightly parallelized work (UI and libOBS code) and helps against the potential race condition in libOBS.
This commit is contained in:
Michael Fabian 'Xaymar' Dirks 2019-12-16 23:53:20 +01:00
parent 01f23e17fe
commit 1c7fd1495c
2 changed files with 31 additions and 34 deletions

View file

@ -142,20 +142,12 @@ source::mirror::mirror_instance::~mirror_instance()
uint32_t source::mirror::mirror_instance::get_width() uint32_t source::mirror::mirror_instance::get_width()
{ {
if (!_source || !_source_item || !(obs_source_get_output_flags(_source->get()) & OBS_SOURCE_VIDEO)) return _source_size.first;
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() uint32_t source::mirror::mirror_instance::get_height()
{ {
if (!_source || !_source_item || !(obs_source_get_output_flags(_source->get()) & OBS_SOURCE_VIDEO)) return _source_size.second;
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) static void convert_config(obs_data_t* data)
@ -242,21 +234,28 @@ void source::mirror::mirror_instance::save(obs_data_t* data)
void source::mirror::mirror_instance::video_tick(float time) void source::mirror::mirror_instance::video_tick(float time)
{ {
if (_source && ((obs_source_get_output_flags(_source->get()) & OBS_SOURCE_VIDEO) != 0)) {
if (_rescale_keep_orig_size || !_rescale_enabled) {
_source_size.first = _source->width();
_source_size.second = _source->height();
} else {
_source_size.first = _rescale_width;
_source_size.second = _rescale_height;
}
} else {
_source_size.first = 0;
_source_size.second = 0;
}
if (_source_item && ((obs_source_get_output_flags(_source->get()) & OBS_SOURCE_VIDEO) != 0)) { if (_source_item && ((obs_source_get_output_flags(_source->get()) & OBS_SOURCE_VIDEO) != 0)) {
obs_transform_info info; obs_transform_info info;
/// Position, Rotation, Scale, Alignment /// Position, Rotation, Scale, Alignment, Bounding Box
vec2_set(&info.pos, 0, 0); vec2_set(&info.pos, 0, 0);
info.rot = 0; info.rot = 0;
vec2_set(&info.scale, 1., 1.); vec2_set(&info.scale, 1., 1.);
info.alignment = OBS_ALIGN_LEFT | OBS_ALIGN_TOP; info.alignment = OBS_ALIGN_LEFT | OBS_ALIGN_TOP;
vec2_set(&info.bounds, static_cast<float_t>(_source_size.first), static_cast<float_t>(_source_size.second));
/// 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()));
}
info.bounds_alignment = _rescale_alignment; info.bounds_alignment = _rescale_alignment;
info.bounds_type = OBS_BOUNDS_STRETCH; info.bounds_type = OBS_BOUNDS_STRETCH;
@ -284,18 +283,11 @@ void source::mirror::mirror_instance::video_render(gs_effect_t* effect)
// Rendering depends on cached or uncached. // Rendering depends on cached or uncached.
if (_cache_enabled || _rescale_enabled) { if (_cache_enabled || _rescale_enabled) {
if (!_cache_rendered) { if (!_cache_rendered) {
uint32_t width = get_width(); if (!_source_size.first || !_source_size.second)
uint32_t height = get_height();
if (_rescale_enabled) {
width = _rescale_width;
height = _rescale_height;
}
if (!width || !height)
return; return;
try { try {
_cache_texture = this->_cache_renderer->render(width, height); _cache_texture = this->_cache_renderer->render(_source_size.first, _source_size.second);
_cache_rendered = true; _cache_rendered = true;
} catch (...) { } catch (...) {
} }
@ -310,7 +302,7 @@ void source::mirror::mirror_instance::video_render(gs_effect_t* effect)
GS_DEBUG_MARKER_BEGIN(GS_DEBUG_COLOR_ITEM_TEXTURE, "render_cache"); 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()); gs_effect_set_texture(gs_effect_get_param_by_name(effect, "image"), _cache_texture->get_object());
while (gs_effect_loop(effect, "Draw")) { while (gs_effect_loop(effect, "Draw")) {
gs_draw_sprite(nullptr, 0, get_width(), get_height()); gs_draw_sprite(nullptr, 0, _source_size.first, _source_size.second);
} }
GS_DEBUG_MARKER_END(); GS_DEBUG_MARKER_END();
} else { } else {
@ -320,7 +312,8 @@ void source::mirror::mirror_instance::video_render(gs_effect_t* effect)
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 {
std::unique_lock<std::mutex> ulock(this->_audio_lock_outputter); std::unique_lock<std::mutex> ulock(this->_audio_lock_outputter);
while (!this->_audio_kill_thread) { while (!this->_audio_kill_thread) {
@ -359,9 +352,9 @@ 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 (_scene) { /* if (_scene) {
enum_callback(_self, _scene.get(), param); enum_callback(_self, _scene.get(), param);
} }*/
if (_source) { if (_source) {
enum_callback(_self, _source->get(), param); enum_callback(_self, _source->get(), param);
} }
@ -369,9 +362,9 @@ void source::mirror::mirror_instance::enum_active_sources(obs_source_enum_proc_t
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 (_scene) { /* if (_scene) {
enum_callback(_self, _scene.get(), param); enum_callback(_self, _scene.get(), param);
} }*/
if (_source) { if (_source) {
enum_callback(_self, _source->get(), param); enum_callback(_self, _source->get(), param);
} }
@ -479,7 +472,8 @@ void source::mirror::mirror_factory::get_defaults2(obs_data_t* data)
obs_data_set_default_int(data, ST_SCALING_ALIGNMENT, OBS_ALIGN_CENTER); obs_data_set_default_int(data, ST_SCALING_ALIGNMENT, OBS_ALIGN_CENTER);
} }
static bool modified_properties(obs_properties_t* pr, obs_property_t* p, obs_data_t* data) noexcept try { static bool modified_properties(obs_properties_t* pr, obs_property_t* p, obs_data_t* data) noexcept
try {
if (obs_properties_get(pr, ST_SOURCE) == p) { if (obs_properties_get(pr, ST_SOURCE) == p) {
obs_source_t* target = obs_get_source_by_name(obs_data_get_string(data, ST_SOURCE)); obs_source_t* target = obs_get_source_by_name(obs_data_get_string(data, ST_SOURCE));
if (target) { if (target) {

View file

@ -53,6 +53,9 @@ namespace source {
std::shared_ptr<obs::source> _source; std::shared_ptr<obs::source> _source;
std::string _source_name; std::string _source_name;
// Cached Data
std::pair<uint32_t, uint32_t> _source_size;
// Audio // Audio
bool _audio_enabled; bool _audio_enabled;
speaker_layout _audio_layout; speaker_layout _audio_layout;