2023-02-28 01:15:26 +00:00
// AUTOGENERATED COPYRIGHT HEADER START
// Copyright (C) 2019-2023 Michael Fabian 'Xaymar' Dirks <info@xaymar.com>
// Copyright (C) 2022 lainon <GermanAizek@yandex.ru>
// AUTOGENERATED COPYRIGHT HEADER END
2017-12-14 07:06:09 +00:00
2019-01-14 10:23:21 +00:00
# include "source-mirror.hpp"
2020-04-05 04:13:14 +00:00
# include "strings.hpp"
2018-04-27 21:38:49 +00:00
# include <bitset>
2018-11-07 14:24:25 +00:00
# include <cstring>
2018-04-27 21:38:49 +00:00
# include <functional>
2018-11-07 14:24:25 +00:00
# include <memory>
2019-10-16 02:24:18 +00:00
# include <sstream>
2019-09-04 01:03:41 +00:00
# include <stdexcept>
2018-11-07 14:24:25 +00:00
# include <vector>
2020-02-13 04:17:06 +00:00
# include "obs/gs/gs-helper.hpp"
2019-04-02 17:55:45 +00:00
# include "obs/obs-source-tracker.hpp"
2019-02-11 02:54:16 +00:00
# include "obs/obs-tools.hpp"
2021-09-07 02:22:46 +00:00
# include "util/util-logging.hpp"
# ifdef _DEBUG
# define ST_PREFIX "<%s> "
# define D_LOG_ERROR(x, ...) P_LOG_ERROR(ST_PREFIX##x, __FUNCTION_SIG__, __VA_ARGS__)
# define D_LOG_WARNING(x, ...) P_LOG_WARN(ST_PREFIX##x, __FUNCTION_SIG__, __VA_ARGS__)
# define D_LOG_INFO(x, ...) P_LOG_INFO(ST_PREFIX##x, __FUNCTION_SIG__, __VA_ARGS__)
# define D_LOG_DEBUG(x, ...) P_LOG_DEBUG(ST_PREFIX##x, __FUNCTION_SIG__, __VA_ARGS__)
# else
# define ST_PREFIX "<source::mirror> "
# define D_LOG_ERROR(...) P_LOG_ERROR(ST_PREFIX __VA_ARGS__)
# define D_LOG_WARNING(...) P_LOG_WARN(ST_PREFIX __VA_ARGS__)
# define D_LOG_INFO(...) P_LOG_INFO(ST_PREFIX __VA_ARGS__)
# define D_LOG_DEBUG(...) P_LOG_DEBUG(ST_PREFIX __VA_ARGS__)
# endif
2017-12-14 07:06:09 +00:00
2019-01-14 10:23:21 +00:00
// OBS
# ifdef _MSC_VER
# pragma warning(push)
2022-08-28 12:28:37 +00:00
# pragma warning(disable : 4464)
# pragma warning(disable : 4820)
# pragma warning(disable : 5220)
# else
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wall"
# pragma GCC diagnostic ignored "-Wextra"
2019-01-14 10:23:21 +00:00
# endif
2018-11-08 07:08:38 +00:00
# include <media-io/audio-io.h>
2019-01-14 10:23:21 +00:00
# ifdef _MSC_VER
# pragma warning(pop)
2022-08-28 12:28:37 +00:00
# else
# pragma GCC diagnostic pop
2019-01-14 10:23:21 +00:00
# endif
2018-11-08 07:08:38 +00:00
2021-06-08 02:30:20 +00:00
# define ST_I18N "Source.Mirror"
# define ST_I18N_SOURCE ST_I18N ".Source"
# define ST_KEY_SOURCE "Source.Mirror.Source"
# define ST_I18N_SOURCE_AUDIO ST_I18N_SOURCE ".Audio"
# define ST_KEY_SOURCE_AUDIO "Source.Mirror.Audio"
# define ST_I18N_SOURCE_AUDIO_LAYOUT ST_I18N_SOURCE_AUDIO ".Layout"
# define ST_KEY_SOURCE_AUDIO_LAYOUT "Source.Mirror.Audio.Layout"
# define ST_I18N_SOURCE_AUDIO_LAYOUT_(x) ST_I18N_SOURCE_AUDIO_LAYOUT "." D_VSTR(x)
2019-10-16 02:24:18 +00:00
2020-04-05 16:52:06 +00:00
using namespace streamfx : : source : : mirror ;
2020-01-13 21:40:15 +00:00
2021-04-16 23:43:30 +00:00
static constexpr std : : string_view HELP_URL = " https://github.com/Xaymar/obs-StreamFX/wiki/Source-Mirror " ;
2020-04-05 16:52:06 +00:00
mirror_audio_data : : mirror_audio_data ( const audio_data * audio , speaker_layout layout )
2018-11-07 14:24:25 +00:00
{
2020-03-03 01:17:26 +00:00
// Build a clone of a packet.
audio_t * oad = obs_get_audio ( ) ;
const audio_output_info * aoi = audio_output_get_info ( oad ) ;
2020-03-20 22:59:42 +00:00
osa . frames = audio - > frames ;
osa . timestamp = audio - > timestamp ;
osa . speakers = layout ;
osa . format = aoi - > format ;
osa . samples_per_sec = aoi - > samples_per_sec ;
2020-03-03 01:17:26 +00:00
data . resize ( MAX_AV_PLANES ) ;
2020-04-08 21:38:42 +00:00
for ( std : : size_t idx = 0 ; idx < MAX_AV_PLANES ; idx + + ) {
2020-03-03 01:17:26 +00:00
if ( ! audio - > data [ idx ] ) {
osa . data [ idx ] = nullptr ;
continue ;
}
2020-03-20 22:59:42 +00:00
2020-03-03 01:17:26 +00:00
data [ idx ] . resize ( audio - > frames * get_audio_bytes_per_channel ( osa . format ) ) ;
memcpy ( data [ idx ] . data ( ) , audio - > data [ idx ] , data [ idx ] . size ( ) ) ;
osa . data [ idx ] = data [ idx ] . data ( ) ;
}
2017-12-14 07:06:09 +00:00
}
2023-05-13 12:35:46 +00:00
mirror_instance : : mirror_instance ( obs_data_t * settings , obs_source_t * self ) : obs : : source_instance ( settings , self ) , _source ( ) , _source_child ( ) , _signal_rename ( ) , _audio_enabled ( false ) , _audio_layout ( SPEAKERS_UNKNOWN )
2020-12-04 12:15:31 +00:00
{
update ( settings ) ;
}
2020-03-03 01:17:26 +00:00
2020-04-05 16:52:06 +00:00
mirror_instance : : ~ mirror_instance ( )
2018-11-07 14:24:25 +00:00
{
2019-10-16 02:24:18 +00:00
release ( ) ;
2018-04-27 21:38:49 +00:00
}
2017-12-14 07:06:09 +00:00
2020-08-10 01:29:05 +00:00
uint32_t mirror_instance : : get_width ( )
2018-11-07 14:24:25 +00:00
{
2021-01-30 19:49:11 +00:00
return _source_size . first ? _source_size . first : 1 ;
2017-12-14 07:06:09 +00:00
}
2020-08-10 01:29:05 +00:00
uint32_t mirror_instance : : get_height ( )
2018-11-07 14:24:25 +00:00
{
2021-01-30 19:49:11 +00:00
return _source_size . second ? _source_size . second : 1 ;
2019-10-16 02:24:18 +00:00
}
2020-04-05 16:52:06 +00:00
void mirror_instance : : load ( obs_data_t * data )
2019-10-16 02:24:18 +00:00
{
2020-04-05 04:02:03 +00:00
update ( data ) ;
}
2019-10-16 02:24:18 +00:00
2020-08-10 01:29:05 +00:00
void mirror_instance : : migrate ( obs_data_t * data , uint64_t version )
2020-04-05 04:02:03 +00:00
{
2019-10-16 02:24:18 +00:00
switch ( version ) {
case 0 :
2021-06-08 02:30:20 +00:00
obs_data_set_int ( data , ST_KEY_SOURCE_AUDIO_LAYOUT , obs_data_get_int ( data , " Source.Mirror.Audio.Layout " ) ) ;
2019-10-16 02:24:18 +00:00
obs_data_unset_user_value ( data , " Source.Mirror.Audio.Layout " ) ;
2020-01-13 21:40:15 +00:00
case STREAMFX_VERSION :
2019-10-16 02:24:18 +00:00
break ;
2018-11-07 13:11:38 +00:00
}
2017-12-14 07:06:09 +00:00
}
2020-04-05 16:52:06 +00:00
void mirror_instance : : update ( obs_data_t * data )
2018-11-07 14:24:25 +00:00
{
2019-01-23 20:05:10 +00:00
// Audio
2021-06-08 02:30:20 +00:00
_audio_enabled = obs_data_get_bool ( data , ST_KEY_SOURCE_AUDIO ) ;
_audio_layout = static_cast < speaker_layout > ( obs_data_get_int ( data , ST_KEY_SOURCE_AUDIO_LAYOUT ) ) ;
2020-03-21 07:59:35 +00:00
// Acquire new source.
2021-06-08 02:30:20 +00:00
acquire ( obs_data_get_string ( data , ST_KEY_SOURCE ) ) ;
2017-12-14 07:06:09 +00:00
}
2020-04-05 16:52:06 +00:00
void mirror_instance : : save ( obs_data_t * data )
2018-11-07 14:24:25 +00:00
{
2019-10-16 02:24:18 +00:00
if ( _source ) {
2021-06-08 02:30:20 +00:00
obs_data_set_string ( data , ST_KEY_SOURCE , obs_source_get_name ( _source . get ( ) ) ) ;
2020-02-14 06:55:18 +00:00
} else {
2021-06-08 02:30:20 +00:00
obs_data_unset_user_value ( data , ST_KEY_SOURCE ) ;
2019-10-16 02:24:18 +00:00
}
2017-12-14 07:06:09 +00:00
}
2020-05-31 00:44:29 +00:00
void mirror_instance : : video_tick ( float_t time ) { }
2020-02-14 06:55:18 +00:00
2020-04-05 16:52:06 +00:00
void mirror_instance : : video_render ( gs_effect_t * effect )
2018-11-07 14:24:25 +00:00
{
2020-02-14 06:55:18 +00:00
if ( ! _source )
2017-12-14 07:06:09 +00:00
return ;
2020-02-14 06:55:18 +00:00
if ( ( obs_source_get_output_flags ( _source . get ( ) ) & OBS_SOURCE_VIDEO ) = = 0 )
2019-01-31 01:57:18 +00:00
return ;
2023-04-05 16:24:26 +00:00
# if defined(ENABLE_PROFILING) && !defined(D_PLATFORM_MAC) && _DEBUG
2023-05-13 12:35:46 +00:00
streamfx : : obs : : gs : : debug_marker gdmp { streamfx : : obs : : gs : : debug_color_source , " Source Mirror '%s' for '%s' " , obs_source_get_name ( _self ) , obs_source_get_name ( _source . get ( ) ) } ;
2020-04-25 23:04:04 +00:00
# endif
2020-05-31 00:44:29 +00:00
_source_size . first = obs_source_get_width ( _source . get ( ) ) ;
_source_size . second = obs_source_get_height ( _source . get ( ) ) ;
2020-02-14 06:55:18 +00:00
obs_source_video_render ( _source . get ( ) ) ;
2017-12-14 07:06:09 +00:00
}
2018-04-24 11:48:42 +00:00
2020-04-05 16:52:06 +00:00
void mirror_instance : : enum_active_sources ( obs_source_enum_proc_t cb , void * ptr )
2018-11-07 14:24:25 +00:00
{
2020-03-03 01:17:26 +00:00
if ( ! _source )
2020-02-14 06:55:18 +00:00
return ;
cb ( _self , _source . get ( ) , ptr ) ;
2018-11-08 07:08:38 +00:00
}
2020-04-05 16:52:06 +00:00
void mirror_instance : : enum_all_sources ( obs_source_enum_proc_t cb , void * ptr )
2019-10-15 11:34:47 +00:00
{
2020-02-14 06:55:18 +00:00
if ( ! _source )
return ;
cb ( _self , _source . get ( ) , ptr ) ;
}
2020-04-05 16:52:06 +00:00
void mirror_instance : : acquire ( std : : string source_name )
2022-08-27 11:17:47 +00:00
{
try {
release ( ) ;
2020-03-03 01:17:26 +00:00
2022-08-27 11:17:47 +00:00
// Find source by name if possible.
decltype ( _source ) source { source_name } ;
if ( ( ! source ) | | ( source = = _self ) ) { // If we failed, just exit early.
return ;
}
2020-02-14 06:55:18 +00:00
2022-08-27 11:17:47 +00:00
// Everything went well, store.
_source_child = std : : make_shared < : : streamfx : : obs : : source_active_child > ( _self , source ) ;
_source = std : : move ( source ) ;
_source_size . first = obs_source_get_width ( _source ) ;
_source_size . second = obs_source_get_height ( _source ) ;
// Listen to any audio the source spews out.
if ( _audio_enabled ) {
_signal_audio = std : : make_shared < obs : : audio_signal_handler > ( _source ) ;
2023-05-13 12:35:46 +00:00
_signal_audio - > event . add ( std : : bind ( & mirror_instance : : on_audio , this , std : : placeholders : : _1 , std : : placeholders : : _2 , std : : placeholders : : _3 ) ) ;
2022-08-27 11:17:47 +00:00
}
} catch ( . . . ) {
release ( ) ;
2020-03-03 01:17:26 +00:00
}
2018-11-08 07:08:38 +00:00
}
2020-04-05 16:52:06 +00:00
void mirror_instance : : release ( )
2020-02-14 06:55:18 +00:00
{
_signal_audio . reset ( ) ;
_signal_rename . reset ( ) ;
_source_child . reset ( ) ;
2022-05-28 18:17:04 +00:00
_source . release ( ) ;
2018-04-24 11:48:42 +00:00
}
2019-04-02 17:44:48 +00:00
2022-05-28 18:17:04 +00:00
void mirror_instance : : on_audio ( : : streamfx : : obs : : source , const audio_data * audio , bool )
2019-04-02 17:44:48 +00:00
{
2020-03-03 01:17:26 +00:00
// Immediately quit if there isn't any actual audio to send out.
2020-02-14 06:55:18 +00:00
if ( ! _audio_enabled ) {
2019-04-02 17:44:48 +00:00
return ;
}
2020-03-03 01:17:26 +00:00
// Detect Audio Layout from underlying audio.
speaker_layout detected_layout ;
if ( _audio_layout ! = SPEAKERS_UNKNOWN ) {
detected_layout = _audio_layout ;
} else {
std : : bitset < MAX_AV_PLANES > layout_detection ;
2020-04-08 21:38:42 +00:00
for ( std : : size_t idx = 0 ; idx < MAX_AV_PLANES ; idx + + ) {
2020-03-03 01:17:26 +00:00
layout_detection . set ( idx , audio - > data [ idx ] ! = nullptr ) ;
2019-04-03 00:58:54 +00:00
}
2020-03-03 01:17:26 +00:00
switch ( layout_detection . to_ulong ( ) ) {
case 0b00000001 :
detected_layout = SPEAKERS_MONO ;
break ;
case 0b00000011 :
detected_layout = SPEAKERS_STEREO ;
break ;
case 0b00000111 :
detected_layout = SPEAKERS_2POINT1 ;
break ;
case 0b00001111 :
detected_layout = SPEAKERS_4POINT0 ;
break ;
case 0b00011111 :
detected_layout = SPEAKERS_4POINT1 ;
break ;
case 0b00111111 :
detected_layout = SPEAKERS_5POINT1 ;
break ;
case 0b11111111 :
detected_layout = SPEAKERS_7POINT1 ;
break ;
default :
detected_layout = SPEAKERS_UNKNOWN ;
break ;
2019-08-04 10:34:42 +00:00
}
2019-04-03 00:58:54 +00:00
}
2020-03-20 22:59:42 +00:00
{
std : : unique_lock < std : : mutex > ul ( _audio_queue_lock ) ;
_audio_queue . emplace ( audio , detected_layout ) ;
}
2020-03-03 01:17:26 +00:00
// Create a clone of the audio data and push it to the thread pool.
2020-04-05 16:52:06 +00:00
streamfx : : threadpool ( ) - > push ( std : : bind ( & mirror_instance : : audio_output , this , std : : placeholders : : _1 ) , nullptr ) ;
2020-03-03 01:17:26 +00:00
}
2019-04-02 17:44:48 +00:00
2020-04-05 16:52:06 +00:00
void mirror_instance : : audio_output ( std : : shared_ptr < void > data )
2020-03-03 01:17:26 +00:00
{
2020-03-20 22:59:42 +00:00
std : : unique_lock < std : : mutex > ul ( _audio_queue_lock ) ;
while ( _audio_queue . size ( ) > 0 ) {
obs_source_output_audio ( _self , & ( ( _audio_queue . front ( ) ) . osa ) ) ;
_audio_queue . pop ( ) ;
}
2019-04-02 17:44:48 +00:00
}
2019-10-15 11:34:47 +00:00
2020-04-05 16:52:06 +00:00
mirror_factory : : mirror_factory ( )
2019-10-15 11:34:47 +00:00
{
2021-06-08 02:16:33 +00:00
_info . id = S_PREFIX " source-mirror " ;
2019-10-15 11:34:47 +00:00
_info . type = OBS_SOURCE_TYPE_INPUT ;
2020-02-14 06:55:18 +00:00
_info . output_flags = OBS_SOURCE_VIDEO | OBS_SOURCE_CUSTOM_DRAW | OBS_SOURCE_AUDIO ;
2019-10-15 11:34:47 +00:00
2022-05-11 03:33:38 +00:00
support_active_child_sources ( true ) ;
support_child_sources ( true ) ;
2019-12-14 01:37:04 +00:00
finish_setup ( ) ;
2020-06-14 02:53:15 +00:00
register_proxy ( " obs-stream-effects-source-mirror " ) ;
2019-10-15 11:34:47 +00:00
}
2020-04-05 16:52:06 +00:00
mirror_factory : : ~ mirror_factory ( ) { }
2019-10-15 11:34:47 +00:00
2020-04-05 16:52:06 +00:00
const char * mirror_factory : : get_name ( )
2019-10-15 11:34:47 +00:00
{
2021-06-08 02:30:20 +00:00
return D_TRANSLATE ( ST_I18N ) ;
2019-10-15 11:34:47 +00:00
}
2020-04-05 16:52:06 +00:00
void mirror_factory : : get_defaults2 ( obs_data_t * data )
2019-10-15 11:34:47 +00:00
{
2021-06-08 02:30:20 +00:00
obs_data_set_default_string ( data , ST_KEY_SOURCE , " " ) ;
obs_data_set_default_bool ( data , ST_KEY_SOURCE_AUDIO , false ) ;
obs_data_set_default_int ( data , ST_KEY_SOURCE_AUDIO_LAYOUT , static_cast < int64_t > ( SPEAKERS_UNKNOWN ) ) ;
2019-10-15 11:34:47 +00:00
}
2019-12-16 22:53:20 +00:00
static bool modified_properties ( obs_properties_t * pr , obs_property_t * p , obs_data_t * data ) noexcept
2022-08-27 11:17:47 +00:00
{
try {
if ( obs_properties_get ( pr , ST_KEY_SOURCE_AUDIO ) = = p ) {
bool show = obs_data_get_bool ( data , ST_KEY_SOURCE_AUDIO ) ;
obs_property_set_visible ( obs_properties_get ( pr , ST_KEY_SOURCE_AUDIO_LAYOUT ) , show ) ;
return true ;
}
return false ;
} catch ( . . . ) {
return false ;
2019-10-15 11:34:47 +00:00
}
}
2020-04-05 16:52:06 +00:00
obs_properties_t * mirror_factory : : get_properties2 ( mirror_instance * data )
2019-10-15 11:34:47 +00:00
{
obs_properties_t * pr = obs_properties_create ( ) ;
obs_property_t * p = nullptr ;
2021-04-16 23:43:30 +00:00
# ifdef ENABLE_FRONTEND
{
2023-05-13 12:35:46 +00:00
obs_properties_add_button2 ( pr , S_MANUAL_OPEN , D_TRANSLATE ( S_MANUAL_OPEN ) , streamfx : : source : : mirror : : mirror_factory : : on_manual_open , nullptr ) ;
2021-04-16 23:43:30 +00:00
}
# endif
2019-10-16 02:24:18 +00:00
{
2023-05-13 12:35:46 +00:00
p = obs_properties_add_list ( pr , ST_KEY_SOURCE , D_TRANSLATE ( ST_I18N_SOURCE ) , OBS_COMBO_TYPE_LIST , OBS_COMBO_FORMAT_STRING ) ;
2020-02-13 04:18:28 +00:00
obs_property_set_modified_callback ( p , modified_properties ) ;
obs_property_list_add_string ( p , " " , " " ) ;
2023-05-14 04:50:27 +00:00
obs : : source_tracker : : instance ( ) - > enumerate (
2023-02-16 04:33:33 +00:00
[ & p ] ( std : : string name , : : streamfx : : obs : : source ) {
2020-02-13 04:18:28 +00:00
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 ) ;
2023-05-14 04:50:27 +00:00
obs : : source_tracker : : instance ( ) - > enumerate (
2023-02-16 04:33:33 +00:00
[ & p ] ( std : : string name , : : streamfx : : obs : : source ) {
2020-02-13 04:18:28 +00:00
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 ) ;
}
2019-10-16 02:24:18 +00:00
2020-02-13 04:18:28 +00:00
{
2021-06-08 02:30:20 +00:00
p = obs_properties_add_bool ( pr , ST_KEY_SOURCE_AUDIO , D_TRANSLATE ( ST_I18N_SOURCE_AUDIO ) ) ;
2020-02-13 04:18:28 +00:00
obs_property_set_modified_callback ( p , modified_properties ) ;
}
2019-10-16 02:24:18 +00:00
2020-02-13 04:18:28 +00:00
{
2023-05-13 12:35:46 +00:00
p = obs_properties_add_list ( pr , ST_KEY_SOURCE_AUDIO_LAYOUT , D_TRANSLATE ( ST_I18N_SOURCE_AUDIO_LAYOUT ) , OBS_COMBO_TYPE_LIST , OBS_COMBO_FORMAT_INT ) ;
obs_property_list_add_int ( p , D_TRANSLATE ( ST_I18N_SOURCE_AUDIO_LAYOUT_ ( Unknown ) ) , static_cast < int64_t > ( SPEAKERS_UNKNOWN ) ) ;
obs_property_list_add_int ( p , D_TRANSLATE ( ST_I18N_SOURCE_AUDIO_LAYOUT_ ( Mono ) ) , static_cast < int64_t > ( SPEAKERS_MONO ) ) ;
obs_property_list_add_int ( p , D_TRANSLATE ( ST_I18N_SOURCE_AUDIO_LAYOUT_ ( Stereo ) ) , static_cast < int64_t > ( SPEAKERS_STEREO ) ) ;
obs_property_list_add_int ( p , D_TRANSLATE ( ST_I18N_SOURCE_AUDIO_LAYOUT_ ( StereoLFE ) ) , static_cast < int64_t > ( SPEAKERS_2POINT1 ) ) ;
obs_property_list_add_int ( p , D_TRANSLATE ( ST_I18N_SOURCE_AUDIO_LAYOUT_ ( Quadraphonic ) ) , static_cast < int64_t > ( SPEAKERS_4POINT0 ) ) ;
obs_property_list_add_int ( p , D_TRANSLATE ( ST_I18N_SOURCE_AUDIO_LAYOUT_ ( QuadraphonicLFE ) ) , static_cast < int64_t > ( SPEAKERS_4POINT1 ) ) ;
obs_property_list_add_int ( p , D_TRANSLATE ( ST_I18N_SOURCE_AUDIO_LAYOUT_ ( Surround ) ) , static_cast < int64_t > ( SPEAKERS_5POINT1 ) ) ;
obs_property_list_add_int ( p , D_TRANSLATE ( ST_I18N_SOURCE_AUDIO_LAYOUT_ ( FullSurround ) ) , static_cast < int64_t > ( SPEAKERS_7POINT1 ) ) ;
2019-10-16 02:24:18 +00:00
}
2019-10-15 11:34:47 +00:00
return pr ;
}
2020-04-05 16:52:06 +00:00
2021-04-16 23:43:30 +00:00
# ifdef ENABLE_FRONTEND
bool mirror_factory : : on_manual_open ( obs_properties_t * props , obs_property_t * property , void * data )
2022-08-27 11:17:47 +00:00
{
try {
streamfx : : open_url ( HELP_URL ) ;
return false ;
} catch ( const std : : exception & ex ) {
D_LOG_ERROR ( " Failed to open manual due to error: %s " , ex . what ( ) ) ;
return false ;
} catch ( . . . ) {
D_LOG_ERROR ( " Failed to open manual due to unknown error. " , " " ) ;
return false ;
}
2021-04-16 23:43:30 +00:00
}
# endif
2023-05-14 06:52:43 +00:00
std : : shared_ptr < mirror_factory > mirror_factory : : instance ( )
2022-08-27 11:17:47 +00:00
{
2023-05-14 06:52:43 +00:00
static std : : weak_ptr < mirror_factory > winst ;
static std : : mutex mtx ;
std : : unique_lock < decltype ( mtx ) > lock ( mtx ) ;
auto instance = winst . lock ( ) ;
if ( ! instance ) {
instance = std : : shared_ptr < mirror_factory > ( new mirror_factory ( ) ) ;
winst = instance ;
2022-08-27 11:17:47 +00:00
}
2023-05-14 06:52:43 +00:00
return instance ;
2020-04-05 16:52:06 +00:00
}
2023-05-14 06:52:43 +00:00
static std : : shared_ptr < mirror_factory > loader_instance ;
2020-04-05 16:52:06 +00:00
2023-05-14 06:52:43 +00:00
static auto loader = streamfx : : loader (
[ ] ( ) { // Initalizer
loader_instance = mirror_factory : : instance ( ) ;
} ,
[ ] ( ) { // Finalizer
loader_instance . reset ( ) ;
} ,
streamfx : : loader_priority : : NORMAL ) ;