diff --git a/data/locale/en-US.ini b/data/locale/en-US.ini index b90e65e9..bcbdf6db 100644 --- a/data/locale/en-US.ini +++ b/data/locale/en-US.ini @@ -79,6 +79,7 @@ Source.Mirror.Source="Source" Source.Mirror.Source.Description="Which Source should be mirrored?" 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.Scaling="Rescale Source" Source.Mirror.Scaling.Description="Should the source be rescaled?" Source.Mirror.Scaling.Method="Filter" diff --git a/source/source-mirror.cpp b/source/source-mirror.cpp index bb657ff5..43fac6a3 100644 --- a/source/source-mirror.cpp +++ b/source/source-mirror.cpp @@ -22,10 +22,14 @@ #include #include #include +#include +#include +#include #define S_SOURCE_MIRROR "Source.Mirror" #define P_SOURCE "Source.Mirror.Source" #define P_SOURCE_SIZE "Source.Mirror.Source.Size" +#define P_SOURCE_AUDIO "Source.Mirror.Source.Audio" #define P_SCALING "Source.Mirror.Scaling" #define P_SCALING_METHOD "Source.Mirror.Scaling.Method" #define P_SCALING_METHOD_POINT "Source.Mirror.Scaling.Method.Point" @@ -59,7 +63,7 @@ Source::MirrorAddon::MirrorAddon() { memset(&osi, 0, sizeof(obs_source_info)); osi.id = "obs-stream-effects-source-mirror"; osi.type = OBS_SOURCE_TYPE_INPUT; - osi.output_flags = OBS_SOURCE_VIDEO | OBS_SOURCE_CUSTOM_DRAW; + osi.output_flags = OBS_SOURCE_VIDEO | OBS_SOURCE_AUDIO | OBS_SOURCE_CUSTOM_DRAW; osi.get_name = get_name; osi.get_defaults = get_defaults; @@ -86,6 +90,7 @@ const char * Source::MirrorAddon::get_name(void *) { void Source::MirrorAddon::get_defaults(obs_data_t *data) { obs_data_set_default_string(data, P_SOURCE, ""); + obs_data_set_default_bool(data, P_SOURCE_AUDIO, false); obs_data_set_default_bool(data, P_SCALING, false); obs_data_set_default_string(data, P_SCALING_SIZE, "100x100"); obs_data_set_default_int(data, P_SCALING_METHOD, (int64_t)ScalingMethod::Bilinear); @@ -139,6 +144,9 @@ obs_properties_t * Source::MirrorAddon::get_properties(void *) { obs_property_set_long_description(p, P_TRANSLATE(P_DESC(P_SOURCE_SIZE))); obs_property_set_enabled(p, false); + p = obs_properties_add_bool(pr, P_SOURCE_AUDIO, P_TRANSLATE(P_SOURCE_AUDIO)); + obs_property_set_long_description(p, P_TRANSLATE(P_DESC(P_SOURCE_AUDIO))); + p = obs_properties_add_bool(pr, P_SCALING, P_TRANSLATE(P_SCALING)); obs_property_set_long_description(p, P_TRANSLATE(P_DESC(P_SCALING))); obs_property_set_modified_callback(p, modified_properties); @@ -167,39 +175,60 @@ void * Source::MirrorAddon::create(obs_data_t *data, obs_source_t *source) { } void Source::MirrorAddon::destroy(void *p) { - delete static_cast(p); + if (p) { + delete static_cast(p); + } } uint32_t Source::MirrorAddon::get_width(void *p) { - return static_cast(p)->get_width(); + if (p) { + return static_cast(p)->get_width(); + } + return 0; } uint32_t Source::MirrorAddon::get_height(void *p) { - return static_cast(p)->get_height(); + if (p) { + return static_cast(p)->get_height(); + } + return 0; } void Source::MirrorAddon::update(void *p, obs_data_t *data) { - static_cast(p)->update(data); + if (p) { + static_cast(p)->update(data); + } } void Source::MirrorAddon::activate(void *p) { - static_cast(p)->activate(); + if (p) { + static_cast(p)->activate(); + } } void Source::MirrorAddon::deactivate(void *p) { - static_cast(p)->deactivate(); + if (p) { + static_cast(p)->deactivate(); + } } void Source::MirrorAddon::video_tick(void *p, float t) { - static_cast(p)->video_tick(t); + if (p) { + static_cast(p)->video_tick(t); + } } void Source::MirrorAddon::video_render(void *p, gs_effect_t *ef) { - static_cast(p)->video_render(ef); + if (p) { + static_cast(p)->video_render(ef); + } } + void Source::MirrorAddon::enum_active_sources(void *p, obs_source_enum_proc_t enum_callback, void *param) { - static_cast(p)->enum_active_sources(enum_callback, param); + if (p) { + static_cast(p)->enum_active_sources(enum_callback, param); + } } Source::Mirror::Mirror(obs_data_t* data, obs_source_t* src) { @@ -212,10 +241,21 @@ Source::Mirror::Mirror(obs_data_t* data, obs_source_t* src) { m_sampler = std::make_shared(); m_scalingEffect = obs_get_base_effect(obs_base_effect::OBS_EFFECT_DEFAULT); + m_audioData.resize(MAX_AUDIO_CHANNELS); + for (size_t idx = 0; idx < m_audioData.size(); idx++) { + m_audioData[idx].resize(AUDIO_OUTPUT_FRAMES); + } + m_audioThread = std::thread(std::bind(&Source::Mirror::audio_output_cb, this)); + update(data); } -Source::Mirror::~Mirror() {} +Source::Mirror::~Mirror() { + m_audioKill = true; + m_audioNotify.notify_all(); + if (m_audioThread.joinable()) + m_audioThread.join(); +} uint32_t Source::Mirror::get_width() { if (m_rescale && m_width > 0 && !m_keepOriginalSize) { @@ -241,10 +281,14 @@ void Source::Mirror::update(obs_data_t* data) { if (sourceName != m_mirrorName) { try { m_mirrorSource = std::make_unique(sourceName, m_source); + m_mirrorAudio = std::make_unique(m_mirrorSource->get_object()); + m_mirrorAudio->set_callback(std::bind(&Source::Mirror::audio_capture_cb, this, + std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)); m_mirrorName = sourceName; } catch (...) { } } + m_enableAudio = obs_data_get_bool(data, P_SOURCE_AUDIO); // Rescaling m_rescale = obs_data_get_bool(data, P_SCALING); @@ -319,6 +363,16 @@ void Source::Mirror::deactivate() { m_active = false; } +static inline void mix_audio(float *p_out, float *p_in, + size_t pos, size_t count) { + register float *out = p_out; + register float *in = p_in + pos; + register float *end = in + count; + + while (in < end) + *(out++) += *(in++); +} + void Source::Mirror::video_tick(float time) { m_tick += time; @@ -390,6 +444,49 @@ void Source::Mirror::video_render(gs_effect_t*) { } } +void Source::Mirror::audio_capture_cb(void* data, const audio_data* audio, bool muted) { + std::unique_lock ulock(m_audioLock); + if (!m_enableAudio) { + return; + } + + audio_t* aud = obs_get_audio(); + audio_output_info const* aoi = audio_output_get_info(aud); + + std::bitset<8> layout; + for (size_t plane = 0; plane < MAX_AV_PLANES; plane++) { + float *samples = (float*)audio->data[plane]; + if (!samples) { + m_audioOutput.data[plane] = nullptr; + continue; + } + layout.set(plane); + + memcpy(m_audioData[plane].data(), audio->data[plane], audio->frames * sizeof(float_t)); + m_audioOutput.data[plane] = (uint8_t*)m_audioData[plane].data(); + } + m_audioOutput.format = aoi->format; + m_audioOutput.frames = audio->frames; + m_audioOutput.timestamp = audio->timestamp; + m_audioOutput.samples_per_sec = aoi->samples_per_sec; + m_audioOutput.speakers = aoi->speakers; + + m_audioExists = true; + m_audioNotify.notify_all(); +} + +void Source::Mirror::audio_output_cb() { + std::unique_lock ulock(m_audioLock); + + while (!m_audioKill) { + if (m_audioExists) { + obs_source_output_audio(m_source, &m_audioOutput); + m_audioExists = false; + } + m_audioNotify.wait(ulock, [this]() { return m_audioExists || m_audioKill; }); + } +} + void Source::Mirror::enum_active_sources(obs_source_enum_proc_t enum_callback, void *param) { if (m_mirrorSource) { enum_callback(m_source, m_mirrorSource->get_object(), param); diff --git a/source/source-mirror.h b/source/source-mirror.h index 4c352479..55f7c67c 100644 --- a/source/source-mirror.h +++ b/source/source-mirror.h @@ -22,8 +22,13 @@ #include "gs-rendertarget.h" #include "gs-sampler.h" #include "gfx-source-texture.h" +#include "obs-audio-capture.h" #include #include +#include +#include +#include +#include namespace Source { class MirrorAddon { @@ -69,6 +74,17 @@ namespace Source { std::unique_ptr m_renderTargetScale; std::shared_ptr m_sampler; + // Audio + bool m_enableAudio = false; + std::unique_ptr m_mirrorAudio; + std::mutex m_audioLock; + std::condition_variable m_audioNotify; + obs_source_audio m_audioOutput; + std::vector> m_audioData; + std::thread m_audioThread; + bool m_audioKill = false; + bool m_audioExists = false; + public: Mirror(obs_data_t*, obs_source_t*); ~Mirror(); @@ -81,6 +97,8 @@ namespace Source { void deactivate(); void video_tick(float); void video_render(gs_effect_t*); + void audio_capture_cb(void* data, const audio_data* audio, bool muted); + void audio_output_cb(); void enum_active_sources(obs_source_enum_proc_t, void *); }; };