Skip to content

Commit 67fd862

Browse files
committed
libobs: Fix audio duplication
This fixes the following bug: - a source might be copied into the same scene or through a nested scene. The audio level will then increase by +6 dBFS. An earlier fix [1] dealt with this bug at the scene audio rendering level, which leaves some edge cases since the fix is not located directly in the core audio callback. The current fix consists in: - tagging individual sources which appear several times in the audio tree at each tick; - promote them to root_nodes sources; - bypass their mixing in scenes and transitions. Due to being mixed as root_nodes, the audio of duplicated sources appears only once in the final audio mix. [1] #10537 Signed-off-by: pkv <pkv@obsproject.com>
1 parent 8d2d2f4 commit 67fd862

5 files changed

Lines changed: 61 additions & 16 deletions

File tree

libobs/obs-audio.c

Lines changed: 41 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -33,13 +33,48 @@ static void push_audio_tree(obs_source_t *parent, obs_source_t *source, void *p)
3333

3434
if (da_find(audio->render_order, &source, 0) == DARRAY_INVALID) {
3535
obs_source_t *s = obs_source_get_ref(source);
36-
if (s)
36+
if (s) {
3737
da_push_back(audio->render_order, &s);
38+
s->audio_is_duplicated = false;
39+
}
3840
}
3941

4042
UNUSED_PARAMETER(parent);
4143
}
4244

45+
static inline bool is_individual_audio_source(obs_source_t *source)
46+
{
47+
return source->info.type == OBS_SOURCE_TYPE_INPUT && (source->info.output_flags & OBS_SOURCE_AUDIO) &&
48+
!(source->info.output_flags & OBS_SOURCE_COMPOSITE);
49+
}
50+
51+
/*
52+
* This version of push_audio_tree has the purpose of detecting sources which appear several times in the audio tree.
53+
* They are then tagged as such to avoid their mixing in scenes and transitions and mixed directly as root_nodes.
54+
*/
55+
static void push_audio_tree2(obs_source_t *parent, obs_source_t *source, void *p)
56+
{
57+
struct obs_core_audio *audio = p;
58+
size_t idx = da_find(audio->render_order, &source, 0);
59+
60+
if (idx == DARRAY_INVALID) {
61+
/* First time we see this source → add to render order */
62+
obs_source_t *s = obs_source_get_ref(source);
63+
if (s) {
64+
da_push_back(audio->render_order, &s);
65+
s->audio_is_duplicated = false;
66+
}
67+
} else {
68+
/* Source already present in tree → mark as duplicated if applicable */
69+
obs_source_t *s = audio->render_order.array[idx];
70+
if (is_individual_audio_source(s) && !s->audio_is_duplicated) {
71+
da_push_back(audio->root_nodes, &source);
72+
s->audio_is_duplicated = true;
73+
}
74+
}
75+
UNUSED_PARAMETER(parent);
76+
}
77+
4378
static inline size_t convert_time_to_frames(size_t sample_rate, uint64_t t)
4479
{
4580
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
508543
continue;
509544
if (!obs_source_active(source))
510545
continue;
511-
512-
obs_source_enum_active_tree(source, push_audio_tree, audio);
513-
push_audio_tree(NULL, source, audio);
514-
546+
/* first, add top - level sources as root_nodes */
515547
if (obs->video.mixes.array[j]->mix_audio)
516548
da_push_back(audio->root_nodes, &source);
549+
/* build audio tree and tag duplicate individual sources */
550+
obs_source_enum_active_tree(source, push_audio_tree2, audio);
551+
/* add top - level sources to audio tree */
552+
push_audio_tree(NULL, source, audio);
517553
}
518554
pthread_mutex_unlock(&view->channels_mutex);
519555
}

libobs/obs-internal.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -824,6 +824,8 @@ struct obs_source {
824824
int64_t sync_offset;
825825
int64_t last_sync_offset;
826826
float balance;
827+
/* audio_is_duplicated: tracks whether a source appears multiple times in the audio tree during this tick */
828+
bool audio_is_duplicated;
827829

828830
/* async video data */
829831
gs_texture_t *async_textures[MAX_AV_PLANES];

libobs/obs-scene.c

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1696,19 +1696,23 @@ static bool scene_audio_render(void *data, uint64_t *ts_out, struct obs_source_a
16961696
}
16971697

16981698
obs_source_get_audio_mix(source, &child_audio);
1699-
for (size_t mix = 0; mix < MAX_AUDIO_MIXES; mix++) {
1700-
if ((mixers & (1 << mix)) == 0)
1701-
continue;
17021699

1703-
for (size_t ch = 0; ch < channels; ch++) {
1704-
float *out = audio_output->output[mix].data[ch];
1705-
float *in = child_audio.output[mix].data[ch];
1706-
if (apply_buf)
1707-
mix_audio_with_buf(out, in, buf, pos, count);
1708-
else
1709-
mix_audio(out, in, pos, count);
1700+
if (!source->audio_is_duplicated) {
1701+
for (size_t mix = 0; mix < MAX_AUDIO_MIXES; mix++) {
1702+
if ((mixers & (1 << mix)) == 0)
1703+
continue;
1704+
1705+
for (size_t ch = 0; ch < channels; ch++) {
1706+
float *out = audio_output->output[mix].data[ch];
1707+
float *in = child_audio.output[mix].data[ch];
1708+
if (apply_buf)
1709+
mix_audio_with_buf(out, in, buf, pos, count);
1710+
else
1711+
mix_audio(out, in, pos, count);
1712+
}
17101713
}
17111714
}
1715+
17121716
item = item->next;
17131717
}
17141718

libobs/obs-source-transition.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -854,7 +854,7 @@ static void process_audio(obs_source_t *transition, obs_source_t *child, struct
854854
uint64_t min_ts, uint32_t mixers, size_t channels, size_t sample_rate,
855855
obs_transition_audio_mix_callback_t mix)
856856
{
857-
bool valid = child && !child->audio_pending && child->audio_ts;
857+
bool valid = child && !child->audio_pending && child->audio_ts && !child->audio_is_duplicated;
858858
struct obs_source_audio_mix child_audio;
859859
uint64_t ts;
860860
size_t pos;

libobs/obs-source.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -393,6 +393,9 @@ static obs_source_t *obs_source_create_internal(const char *id, const char *name
393393
source->flags = source->default_flags;
394394
source->enabled = true;
395395

396+
/* audio deduplication initialization */
397+
source->audio_is_duplicated = false;
398+
396399
obs_source_init_finalize(source, canvas);
397400
if (!private) {
398401
if (canvas)

0 commit comments

Comments
 (0)