From 410ba9df88b9c40514e9687e39e79bf417000d7f Mon Sep 17 00:00:00 2001 From: Michael Fabian 'Xaymar' Dirks Date: Fri, 27 Apr 2018 23:38:49 +0200 Subject: [PATCH] source-mirror: Implement mirroring of Source Audio From this point on, Source Mirror is now capable of real-time mirroring of Video and Audio. This can help if you need different filters per scene for your microphone or voice chat, depending on the scene (audio ducking for pause scene, no audio ducking for live gaming). --- data/locale/en-US.ini | 1 + source/source-mirror.cpp | 119 +++++++++++++++++++++++++++++++++++---- source/source-mirror.h | 18 ++++++ 3 files changed, 127 insertions(+), 11 deletions(-) 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 *); }; };