Skip to content

Commit 41ea4ab

Browse files
committed
libobs/audio-monitoring: Add audio sync feature for audio-only playback
This commit resolves audio lag that occurs when using audio monitor for extended periods on sources without video.
1 parent 407944a commit 41ea4ab

File tree

1 file changed

+68
-39
lines changed

1 file changed

+68
-39
lines changed

libobs/audio-monitoring/win32/wasapi-output.c

Lines changed: 68 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,10 @@
88
#include "wasapi-output.h"
99

1010
#define ACTUALLY_DEFINE_GUID(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \
11-
EXTERN_C const GUID DECLSPEC_SELECTANY name = {l, w1, w2, {b1, b2, b3, b4, b5, b6, b7, b8}}
11+
EXTERN_C const GUID DECLSPEC_SELECTANY name = {l, w1, w2, {b1, b2, b3, b4, b5, b6, b7, b8}}
1212

1313
#define do_log(level, format, ...) \
14-
blog(level, "[audio monitoring: '%s'] " format, obs_source_get_name(monitor->source), ##__VA_ARGS__)
14+
blog(level, "[audio monitoring: '%s'] " format, obs_source_get_name(monitor->source), ##__VA_ARGS__)
1515

1616
#define warn(format, ...) do_log(LOG_WARNING, format, ##__VA_ARGS__)
1717
#define info(format, ...) do_log(LOG_INFO, format, ##__VA_ARGS__)
@@ -50,7 +50,7 @@ struct audio_monitor {
5050
/* #define DEBUG_AUDIO */
5151

5252
static bool process_audio_delay(struct audio_monitor *monitor, float **data, uint32_t *frames, uint64_t ts,
53-
uint32_t pad)
53+
uint32_t pad, bool sync_to_video)
5454
{
5555
obs_source_t *s = monitor->source;
5656
uint64_t last_frame_ts = s->last_frame_ts;
@@ -71,34 +71,50 @@ static bool process_audio_delay(struct audio_monitor *monitor, float **data, uin
7171
deque_push_back(&monitor->delay_buffer, frames, sizeof(*frames));
7272
deque_push_back(&monitor->delay_buffer, *data, *frames * blocksize);
7373

74-
if (!monitor->prev_video_ts) {
75-
monitor->prev_video_ts = last_frame_ts;
76-
77-
} else if (monitor->prev_video_ts == last_frame_ts) {
78-
monitor->time_since_prev += util_mul_div64(*frames, 1000000000ULL, monitor->sample_rate);
79-
} else {
80-
monitor->time_since_prev = 0;
74+
if (sync_to_video) {
75+
if (!monitor->prev_video_ts) {
76+
monitor->prev_video_ts = last_frame_ts;
77+
} else if (monitor->prev_video_ts == last_frame_ts) {
78+
monitor->time_since_prev += util_mul_div64(*frames, 1000000000ULL, monitor->sample_rate);
79+
} else {
80+
monitor->time_since_prev = 0;
81+
}
8182
}
8283

8384
while (monitor->delay_buffer.size != 0) {
8485
size_t size;
85-
bool bad_diff;
8686

8787
deque_peek_front(&monitor->delay_buffer, &cur_ts, sizeof(ts));
8888
front_ts = cur_ts - util_mul_div64(pad, 1000000000ULL, monitor->sample_rate);
89-
diff = (int64_t)front_ts - (int64_t)last_frame_ts;
90-
bad_diff = !last_frame_ts || llabs(diff) > 5000000000 || monitor->time_since_prev > 100000000ULL;
9189

92-
/* delay audio if rushing */
93-
if (!bad_diff && diff > 75000000) {
90+
if (sync_to_video) {
91+
diff = (int64_t)front_ts - (int64_t)last_frame_ts;
92+
bool bad_diff = !last_frame_ts || llabs(diff) > 5000000000 ||
93+
monitor->time_since_prev > 100000000ULL;
94+
95+
/* delay audio if rushing */
96+
if (!bad_diff && diff > 10000000) {
97+
#ifdef DEBUG_AUDIO
98+
blog(LOG_INFO,
99+
"audio rushing, cutting audio, "
100+
"diff: %lld, delay buffer size: %lu, "
101+
"v: %llu: a: %llu",
102+
diff, (unsigned long)monitor->delay_buffer.size, last_frame_ts, front_ts);
103+
#endif
104+
return false;
105+
}
106+
} else {
107+
diff = (int64_t)front_ts - (int64_t)cur_time;
108+
if (diff > 10000000) {
94109
#ifdef DEBUG_AUDIO
95-
blog(LOG_INFO,
96-
"audio rushing, cutting audio, "
97-
"diff: %lld, delay buffer size: %lu, "
98-
"v: %llu: a: %llu",
99-
diff, (int)monitor->delay_buffer.size, last_frame_ts, front_ts);
110+
blog(LOG_INFO,
111+
"audio ahead of synctime, waiting, "
112+
"diff: %lld, delay buffer size: %lu, "
113+
"cur_time: %llu, front_ts: %llu",
114+
diff, (unsigned long)monitor->delay_buffer.size, cur_time, front_ts);
100115
#endif
101-
return false;
116+
return false;
117+
}
102118
}
103119

104120
deque_pop_front(&monitor->delay_buffer, NULL, sizeof(ts));
@@ -109,15 +125,30 @@ static bool process_audio_delay(struct audio_monitor *monitor, float **data, uin
109125
deque_pop_front(&monitor->delay_buffer, monitor->buf.array, size);
110126

111127
/* cut audio if dragging */
112-
if (!bad_diff && diff < -75000000 && monitor->delay_buffer.size > 0) {
128+
if (sync_to_video) {
129+
bool bad_diff = !last_frame_ts || llabs(diff) > 5000000000 ||
130+
monitor->time_since_prev > 100000000ULL;
131+
if (!bad_diff && diff < -10000000 && monitor->delay_buffer.size > 0) {
113132
#ifdef DEBUG_AUDIO
114-
blog(LOG_INFO,
115-
"audio dragging, cutting audio, "
116-
"diff: %lld, delay buffer size: %lu, "
117-
"v: %llu: a: %llu",
118-
diff, (int)monitor->delay_buffer.size, last_frame_ts, front_ts);
133+
blog(LOG_INFO,
134+
"audio dragging, cutting audio, "
135+
"diff: %lld, delay buffer size: %lu, "
136+
"v: %llu: a: %llu",
137+
diff, (unsigned long)monitor->delay_buffer.size, last_frame_ts, front_ts);
119138
#endif
120-
continue;
139+
continue;
140+
}
141+
} else {
142+
if (diff < -10000000 && monitor->delay_buffer.size > 0) {
143+
#ifdef DEBUG_AUDIO
144+
blog(LOG_INFO,
145+
"audio behind synctime, dropping, "
146+
"diff: %lld, delay buffer size: %lu, "
147+
"cur_time: %llu, front_ts: %llu",
148+
diff, (unsigned long)monitor->delay_buffer.size, cur_time, front_ts);
149+
#endif
150+
continue;
151+
}
121152
}
122153

123154
*data = monitor->buf.array;
@@ -154,7 +185,7 @@ static bool audio_monitor_init_wasapi(struct audio_monitor *monitor)
154185
HRESULT hr;
155186

156187
/* ------------------------------------------ *
157-
* Init device */
188+
* Init device */
158189

159190
hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, &IID_IMMDeviceEnumerator, (void **)&immde);
160191
if (FAILED(hr)) {
@@ -179,7 +210,7 @@ static bool audio_monitor_init_wasapi(struct audio_monitor *monitor)
179210
}
180211

181212
/* ------------------------------------------ *
182-
* Init client */
213+
* Init client */
183214

184215
hr = device->lpVtbl->Activate(device, &IID_IAudioClient, CLSCTX_ALL, NULL, (void **)&monitor->client);
185216
device->lpVtbl->Release(device);
@@ -194,14 +225,14 @@ static bool audio_monitor_init_wasapi(struct audio_monitor *monitor)
194225
goto fail;
195226
}
196227

197-
hr = monitor->client->lpVtbl->Initialize(monitor->client, AUDCLNT_SHAREMODE_SHARED, 0, 10000000, 0, wfex, NULL);
228+
hr = monitor->client->lpVtbl->Initialize(monitor->client, AUDCLNT_SHAREMODE_SHARED, 0, 1000000, 0, wfex, NULL);
198229
if (FAILED(hr)) {
199230
warn("%s: Failed to initialize: %08lX", __FUNCTION__, hr);
200231
goto fail;
201232
}
202233

203234
/* ------------------------------------------ *
204-
* Init resampler */
235+
* Init resampler */
205236

206237
const struct audio_output_info *info = audio_output_get_info(obs->audio.audio);
207238
WAVEFORMATEXTENSIBLE *ext = (WAVEFORMATEXTENSIBLE *)wfex;
@@ -224,7 +255,7 @@ static bool audio_monitor_init_wasapi(struct audio_monitor *monitor)
224255
}
225256

226257
/* ------------------------------------------ *
227-
* Init client */
258+
* Init client */
228259

229260
hr = monitor->client->lpVtbl->GetBufferSize(monitor->client, &frames);
230261
if (FAILED(hr)) {
@@ -310,13 +341,11 @@ static void on_audio_playback(void *param, obs_source_t *source, const struct au
310341
}
311342

312343
bool decouple_audio = source->async_unbuffered && source->async_decoupled;
344+
uint64_t ts = audio_data->timestamp - ts_offset;
313345

314-
if (monitor->source_has_video && !decouple_audio) {
315-
uint64_t ts = audio_data->timestamp - ts_offset;
316-
317-
if (!process_audio_delay(monitor, (float **)(&resample_data[0]), &resample_frames, ts, pad)) {
318-
goto unlock;
319-
}
346+
bool sync_to_video = monitor->source_has_video && !decouple_audio;
347+
if (!process_audio_delay(monitor, (float **)(&resample_data[0]), &resample_frames, ts, pad, sync_to_video)) {
348+
goto unlock;
320349
}
321350

322351
IAudioRenderClient *const render = monitor->render;

0 commit comments

Comments
 (0)