Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
108 changes: 69 additions & 39 deletions libobs/audio-monitoring/win32/wasapi-output.c
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@
#include "wasapi-output.h"

#define ACTUALLY_DEFINE_GUID(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \
EXTERN_C const GUID DECLSPEC_SELECTANY name = {l, w1, w2, {b1, b2, b3, b4, b5, b6, b7, b8}}
EXTERN_C const GUID DECLSPEC_SELECTANY name = {l, w1, w2, {b1, b2, b3, b4, b5, b6, b7, b8}}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

clang-format issue ? this should not belong to this PR


#define do_log(level, format, ...) \
blog(level, "[audio monitoring: '%s'] " format, obs_source_get_name(monitor->source), ##__VA_ARGS__)
blog(level, "[audio monitoring: '%s'] " format, obs_source_get_name(monitor->source), ##__VA_ARGS__)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

clang-format issue


#define warn(format, ...) do_log(LOG_WARNING, format, ##__VA_ARGS__)
#define info(format, ...) do_log(LOG_INFO, format, ##__VA_ARGS__)
Expand Down Expand Up @@ -50,7 +50,7 @@ struct audio_monitor {
/* #define DEBUG_AUDIO */

static bool process_audio_delay(struct audio_monitor *monitor, float **data, uint32_t *frames, uint64_t ts,
uint32_t pad)
uint32_t pad, bool sync_to_video)
{
obs_source_t *s = monitor->source;
uint64_t last_frame_ts = s->last_frame_ts;
Expand All @@ -71,34 +71,50 @@ static bool process_audio_delay(struct audio_monitor *monitor, float **data, uin
deque_push_back(&monitor->delay_buffer, frames, sizeof(*frames));
deque_push_back(&monitor->delay_buffer, *data, *frames * blocksize);

if (!monitor->prev_video_ts) {
monitor->prev_video_ts = last_frame_ts;

} else if (monitor->prev_video_ts == last_frame_ts) {
monitor->time_since_prev += util_mul_div64(*frames, 1000000000ULL, monitor->sample_rate);
} else {
monitor->time_since_prev = 0;
if (sync_to_video) {
if (!monitor->prev_video_ts) {
monitor->prev_video_ts = last_frame_ts;
} else if (monitor->prev_video_ts == last_frame_ts) {
monitor->time_since_prev += util_mul_div64(*frames, 1000000000ULL, monitor->sample_rate);
} else {
monitor->time_since_prev = 0;
}
}

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

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

/* delay audio if rushing */
if (!bad_diff && diff > 75000000) {
if (sync_to_video) {
diff = (int64_t)front_ts - (int64_t)last_frame_ts;
bool bad_diff = !last_frame_ts || llabs(diff) > 5000000000 ||
monitor->time_since_prev > 100000000ULL;

/* delay audio if rushing */
if (!bad_diff && diff > 75000000) {
#ifdef DEBUG_AUDIO
blog(LOG_INFO,
"audio rushing, cutting audio, "
"diff: %lld, delay buffer size: %lu, "
"v: %llu: a: %llu",
diff, (unsigned long)monitor->delay_buffer.size, last_frame_ts, front_ts);
#endif
return false;
}
} else {
diff = (int64_t)front_ts - (int64_t)cur_time;
if (diff > 10000000) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

how did you come to that value of 10 ms ? (our audio tick is 1024 samples so at 48 kHz it is 21.3 ms so i am assuming you picked the middle ?) did you try other values ? larger or smaller ?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1024 samples is for video syncing only. for audio only source It is 10ms @48 kHz, for video I use 75ms(same as original). You may try looking full file to have a check(not diff only).

#ifdef DEBUG_AUDIO
blog(LOG_INFO,
"audio rushing, cutting audio, "
"diff: %lld, delay buffer size: %lu, "
"v: %llu: a: %llu",
diff, (int)monitor->delay_buffer.size, last_frame_ts, front_ts);
blog(LOG_INFO,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

clang-format issue

"audio ahead of synctime, waiting, "
"diff: %lld, delay buffer size: %lu, "
"cur_time: %llu, front_ts: %llu",
diff, (unsigned long)monitor->delay_buffer.size, cur_time, front_ts);
#endif
return false;
return false;
}
}

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

/* cut audio if dragging */
if (!bad_diff && diff < -75000000 && monitor->delay_buffer.size > 0) {
if (sync_to_video) {
bool bad_diff = !last_frame_ts || llabs(diff) > 5000000000 ||
monitor->time_since_prev > 100000000ULL;
if (!bad_diff && diff < -75000000 && monitor->delay_buffer.size > 0) {
#ifdef DEBUG_AUDIO
blog(LOG_INFO,
"audio dragging, cutting audio, "
"diff: %lld, delay buffer size: %lu, "
"v: %llu: a: %llu",
diff, (int)monitor->delay_buffer.size, last_frame_ts, front_ts);
blog(LOG_INFO,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

clang-format issue

"audio dragging, cutting audio, "
"diff: %lld, delay buffer size: %lu, "
"v: %llu: a: %llu",
diff, (unsigned long)monitor->delay_buffer.size, last_frame_ts, front_ts);
#endif
continue;
continue;
}
} else {
if (diff < -10000000 && monitor->delay_buffer.size > 0) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is where samples are dropped; how did you reach that value of 10 ms ? it might make drops more frequent and really affect audio quality to the benefit of better sync. I am not convinced that is a good trade-off (more on that later)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've tried other value and I think this the syncing problem will occur 1 per hour under 10ms. However, I can have a try if you think we need to implement somethings like [norihiro] have done before.

#ifdef DEBUG_AUDIO
blog(LOG_INFO,
"audio behind synctime, dropping, "
"diff: %lld, delay buffer size: %lu, "
"cur_time: %llu, front_ts: %llu",
diff, (unsigned long)monitor->delay_buffer.size, cur_time, front_ts);
#endif
continue;
}
}

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

/* ------------------------------------------ *
* Init device */
* Init device */
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

clang-format issue


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

/* ------------------------------------------ *
* Init client */
* Init client */
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

clang-format issue


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

hr = monitor->client->lpVtbl->Initialize(monitor->client, AUDCLNT_SHAREMODE_SHARED, 0, 10000000, 0, wfex, NULL);
/* Initialize the audio client. Use a buffer duration of 500000 * 100 ns = 50 ms */
hr = monitor->client->lpVtbl->Initialize(monitor->client, AUDCLNT_SHAREMODE_SHARED, 0, 500000, 0, wfex, NULL);
if (FAILED(hr)) {
warn("%s: Failed to initialize: %08lX", __FUNCTION__, hr);
goto fail;
}

/* ------------------------------------------ *
* Init resampler */
* Init resampler */
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

clang-format issue


const struct audio_output_info *info = audio_output_get_info(obs->audio.audio);
WAVEFORMATEXTENSIBLE *ext = (WAVEFORMATEXTENSIBLE *)wfex;
Expand All @@ -224,7 +256,7 @@ static bool audio_monitor_init_wasapi(struct audio_monitor *monitor)
}

/* ------------------------------------------ *
* Init client */
* Init client */
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

clang-format issue


hr = monitor->client->lpVtbl->GetBufferSize(monitor->client, &frames);
if (FAILED(hr)) {
Expand Down Expand Up @@ -310,13 +342,11 @@ static void on_audio_playback(void *param, obs_source_t *source, const struct au
}

bool decouple_audio = source->async_unbuffered && source->async_decoupled;
uint64_t ts = audio_data->timestamp - ts_offset;

if (monitor->source_has_video && !decouple_audio) {
uint64_t ts = audio_data->timestamp - ts_offset;

if (!process_audio_delay(monitor, (float **)(&resample_data[0]), &resample_frames, ts, pad)) {
goto unlock;
}
bool sync_to_video = monitor->source_has_video && !decouple_audio;
if (!process_audio_delay(monitor, (float **)(&resample_data[0]), &resample_frames, ts, pad, sync_to_video)) {
goto unlock;
}

IAudioRenderClient *const render = monitor->render;
Expand Down