diff --git a/libobs/obs-audio.c b/libobs/obs-audio.c index 0b9569d6a..413376206 100644 --- a/libobs/obs-audio.c +++ b/libobs/obs-audio.c @@ -33,13 +33,48 @@ static void push_audio_tree(obs_source_t *parent, obs_source_t *source, void *p) if (da_find(audio->render_order, &source, 0) == DARRAY_INVALID) { obs_source_t *s = obs_source_get_ref(source); - if (s) + if (s) { da_push_back(audio->render_order, &s); + s->audio_is_duplicated = false; + } } UNUSED_PARAMETER(parent); } +static inline bool is_individual_audio_source(obs_source_t *source) +{ + return source->info.type == OBS_SOURCE_TYPE_INPUT && (source->info.output_flags & OBS_SOURCE_AUDIO) && + !(source->info.output_flags & OBS_SOURCE_COMPOSITE); +} + +/* + * This version of push_audio_tree has the purpose of detecting sources which appear several times in the audio tree. + * They are then tagged as such to avoid their mixing in scenes and transitions and mixed directly as root_nodes. + */ +static void push_audio_tree2(obs_source_t *parent, obs_source_t *source, void *p) +{ + struct obs_core_audio *audio = p; + size_t idx = da_find(audio->render_order, &source, 0); + + if (idx == DARRAY_INVALID) { + /* First time we see this source → add to render order */ + obs_source_t *s = obs_source_get_ref(source); + if (s) { + da_push_back(audio->render_order, &s); + s->audio_is_duplicated = false; + } + } else { + /* Source already present in tree → mark as duplicated if applicable */ + obs_source_t *s = audio->render_order.array[idx]; + if (is_individual_audio_source(s) && !s->audio_is_duplicated) { + da_push_back(audio->root_nodes, &source); + s->audio_is_duplicated = true; + } + } + UNUSED_PARAMETER(parent); +} + static inline size_t convert_time_to_frames(size_t sample_rate, uint64_t t) { return (size_t)util_mul_div64(t, sample_rate, 1000000000ULL); @@ -508,12 +543,13 @@ bool audio_callback(void *param, uint64_t start_ts_in, uint64_t end_ts_in, uint6 continue; if (!obs_source_active(source)) continue; - - obs_source_enum_active_tree(source, push_audio_tree, audio); - push_audio_tree(NULL, source, audio); - + /* first, add top - level sources as root_nodes */ if (obs->video.mixes.array[j]->mix_audio) da_push_back(audio->root_nodes, &source); + /* build audio tree and tag duplicate individual sources */ + obs_source_enum_active_tree(source, push_audio_tree2, audio); + /* add top - level sources to audio tree */ + push_audio_tree(NULL, source, audio); } pthread_mutex_unlock(&view->channels_mutex); } diff --git a/libobs/obs-internal.h b/libobs/obs-internal.h index b836a7d8a..1151ffecb 100644 --- a/libobs/obs-internal.h +++ b/libobs/obs-internal.h @@ -824,6 +824,8 @@ struct obs_source { int64_t sync_offset; int64_t last_sync_offset; float balance; + /* audio_is_duplicated: tracks whether a source appears multiple times in the audio tree during this tick */ + bool audio_is_duplicated; /* async video data */ gs_texture_t *async_textures[MAX_AV_PLANES]; diff --git a/libobs/obs-scene.c b/libobs/obs-scene.c index ca4418510..4c8b60375 100644 --- a/libobs/obs-scene.c +++ b/libobs/obs-scene.c @@ -1697,19 +1697,23 @@ static bool scene_audio_render(void *data, uint64_t *ts_out, struct obs_source_a } obs_source_get_audio_mix(source, &child_audio); - for (size_t mix = 0; mix < MAX_AUDIO_MIXES; mix++) { - if ((mixers & (1 << mix)) == 0) - continue; - for (size_t ch = 0; ch < channels; ch++) { - float *out = audio_output->output[mix].data[ch]; - float *in = child_audio.output[mix].data[ch]; - if (apply_buf) - mix_audio_with_buf(out, in, buf, pos, count); - else - mix_audio(out, in, pos, count); + if (!source->audio_is_duplicated) { + for (size_t mix = 0; mix < MAX_AUDIO_MIXES; mix++) { + if ((mixers & (1 << mix)) == 0) + continue; + + for (size_t ch = 0; ch < channels; ch++) { + float *out = audio_output->output[mix].data[ch]; + float *in = child_audio.output[mix].data[ch]; + if (apply_buf) + mix_audio_with_buf(out, in, buf, pos, count); + else + mix_audio(out, in, pos, count); + } } } + item = item->next; } diff --git a/libobs/obs-source-transition.c b/libobs/obs-source-transition.c index dfd1009d2..92b5d107b 100644 --- a/libobs/obs-source-transition.c +++ b/libobs/obs-source-transition.c @@ -854,7 +854,7 @@ static void process_audio(obs_source_t *transition, obs_source_t *child, struct uint64_t min_ts, uint32_t mixers, size_t channels, size_t sample_rate, obs_transition_audio_mix_callback_t mix) { - bool valid = child && !child->audio_pending && child->audio_ts; + bool valid = child && !child->audio_pending && child->audio_ts && !child->audio_is_duplicated; struct obs_source_audio_mix child_audio; uint64_t ts; size_t pos; diff --git a/libobs/obs-source.c b/libobs/obs-source.c index 528a569a2..1b776ae50 100644 --- a/libobs/obs-source.c +++ b/libobs/obs-source.c @@ -393,6 +393,9 @@ static obs_source_t *obs_source_create_internal(const char *id, const char *name source->flags = source->default_flags; source->enabled = true; + /* audio deduplication initialization */ + source->audio_is_duplicated = false; + obs_source_init_finalize(source, canvas); if (!private) { if (canvas)