/* * Modern effects for a modern Streamer * Copyright (C) 2017-2018 Michael Fabian Dirks * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ #include "obs-source-tracker.hpp" #include #include "obs/obs-tools.hpp" #include "plugin.hpp" static std::shared_ptr source_tracker_instance; void streamfx::obs::source_tracker::source_create_handler(void* ptr, calldata_t* data) noexcept try { streamfx::obs::source_tracker* self = reinterpret_cast(ptr); obs_source_t* target = nullptr; calldata_get_ptr(data, "source", &target); if (!target) { return; } const char* name = obs_source_get_name(target); if (!name) { // Do not track unnamed sources. return; } obs_weak_source_t* weak = obs_source_get_weak_source(target); if (!weak) { // This source has already been deleted, do not track. return; } { std::unique_lock ul(self->_lock); self->_sources.insert({std::string(name), {weak, streamfx::obs::obs_weak_source_deleter}}); } } catch (...) { DLOG_ERROR("Unexpected exception in function '%s'.", __FUNCTION_NAME__); } void streamfx::obs::source_tracker::source_destroy_handler(void* ptr, calldata_t* data) noexcept try { streamfx::obs::source_tracker* self = reinterpret_cast(ptr); obs_source_t* target = nullptr; calldata_get_ptr(data, "source", &target); if (!target) { return; } const char* name = obs_source_get_name(target); if (!name) { // Not tracking unnamed sources. return; } { std::unique_lock ul(self->_lock); auto found = self->_sources.find(std::string(name)); if (found == self->_sources.end()) { return; } self->_sources.erase(found); } } catch (...) { DLOG_ERROR("Unexpected exception in function '%s'.", __FUNCTION_NAME__); } void streamfx::obs::source_tracker::source_rename_handler(void* ptr, calldata_t* data) noexcept try { streamfx::obs::source_tracker* self = reinterpret_cast(ptr); obs_source_t* target = nullptr; const char* prev_name = nullptr; const char* new_name = nullptr; calldata_get_ptr(data, "source", &target); calldata_get_string(data, "prev_name", &prev_name); calldata_get_string(data, "new_name", &new_name); if (strcmp(prev_name, new_name) == 0) { // They weren't renamed at all, invalid event. return; } { std::unique_lock ul(self->_lock); auto found = self->_sources.find(std::string(prev_name)); if (found == self->_sources.end()) { // Untracked source, insert. obs_weak_source_t* weak = obs_source_get_weak_source(target); if (!weak) { return; } self->_sources.insert({new_name, {weak, streamfx::obs::obs_weak_source_deleter}}); return; } // Insert at new key, remove old pair. self->_sources.insert({new_name, found->second}); self->_sources.erase(found); } } catch (...) { DLOG_ERROR("Unexpected exception in function '%s'.", __FUNCTION_NAME__); } void streamfx::obs::source_tracker::initialize() { source_tracker_instance = std::make_shared(); } void streamfx::obs::source_tracker::finalize() { source_tracker_instance.reset(); } std::shared_ptr streamfx::obs::source_tracker::get() { return source_tracker_instance; } streamfx::obs::source_tracker::source_tracker() { auto osi = obs_get_signal_handler(); signal_handler_connect(osi, "source_create", &source_create_handler, this); signal_handler_connect(osi, "source_destroy", &source_destroy_handler, this); signal_handler_connect(osi, "source_rename", &source_rename_handler, this); } streamfx::obs::source_tracker::~source_tracker() { auto osi = obs_get_signal_handler(); if (osi) { signal_handler_disconnect(osi, "source_create", &source_create_handler, this); signal_handler_disconnect(osi, "source_destroy", &source_destroy_handler, this); signal_handler_disconnect(osi, "source_rename", &source_rename_handler, this); } this->_sources.clear(); } void streamfx::obs::source_tracker::enumerate(enumerate_cb_t ecb, filter_cb_t fcb) { // Need func-local copy, otherwise we risk corruption if a new source is created or destroyed. decltype(_sources) _clone; { std::unique_lock ul(_lock); _clone = _sources; } for (auto kv : _clone) { auto source = std::shared_ptr(obs_weak_source_get_source(kv.second.get()), streamfx::obs::obs_source_deleter); if (!source) { continue; } if (fcb) { if (fcb(kv.first, source.get())) { continue; } } if (ecb) { if (ecb(kv.first, source.get())) { break; } } } } bool streamfx::obs::source_tracker::filter_sources(std::string, obs_source_t* source) { return (obs_source_get_type(source) != OBS_SOURCE_TYPE_INPUT); } bool streamfx::obs::source_tracker::filter_audio_sources(std::string, obs_source_t* source) { uint32_t flags = obs_source_get_output_flags(source); return !(flags & OBS_SOURCE_AUDIO) || (obs_source_get_type(source) != OBS_SOURCE_TYPE_INPUT); } bool streamfx::obs::source_tracker::filter_video_sources(std::string, obs_source_t* source) { uint32_t flags = obs_source_get_output_flags(source); return !(flags & OBS_SOURCE_VIDEO) || (obs_source_get_type(source) != OBS_SOURCE_TYPE_INPUT); } bool streamfx::obs::source_tracker::filter_transitions(std::string, obs_source_t* source) { return (obs_source_get_type(source) != OBS_SOURCE_TYPE_TRANSITION); } bool streamfx::obs::source_tracker::filter_scenes(std::string, obs_source_t* source) { return (obs_source_get_type(source) != OBS_SOURCE_TYPE_SCENE); }