libobs/audio-monitoring: Add WASAPI init helper

audio_monitor_init_wasapi will be used for reconnect logic.
master
jpark37 2021-10-01 21:41:50 -07:00 committed by Jim
parent 63ffae7d74
commit 7af886581d
1 changed files with 122 additions and 116 deletions

View File

@ -139,95 +139,6 @@ static bool process_audio_delay(struct audio_monitor *monitor, float **data,
return false;
}
static void on_audio_playback(void *param, obs_source_t *source,
const struct audio_data *audio_data, bool muted)
{
struct audio_monitor *monitor = param;
IAudioRenderClient *render = monitor->render;
uint8_t *resample_data[MAX_AV_PLANES];
float vol = source->user_volume;
uint32_t resample_frames;
uint64_t ts_offset;
bool success;
BYTE *output;
if (!TryAcquireSRWLockExclusive(&monitor->playback_mutex)) {
return;
}
if (os_atomic_load_long(&source->activate_refs) == 0) {
goto unlock;
}
success = audio_resampler_resample(
monitor->resampler, resample_data, &resample_frames, &ts_offset,
(const uint8_t *const *)audio_data->data,
(uint32_t)audio_data->frames);
if (!success) {
goto unlock;
}
UINT32 pad = 0;
monitor->client->lpVtbl->GetCurrentPadding(monitor->client, &pad);
bool decouple_audio = source->async_unbuffered &&
source->async_decoupled;
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;
}
}
HRESULT hr =
render->lpVtbl->GetBuffer(render, resample_frames, &output);
if (FAILED(hr)) {
goto unlock;
}
if (!muted) {
/* apply volume */
if (!close_float(vol, 1.0f, EPSILON)) {
register float *cur = (float *)resample_data[0];
register float *end =
cur + resample_frames * monitor->channels;
while (cur < end)
*(cur++) *= vol;
}
memcpy(output, resample_data[0],
resample_frames * monitor->channels * sizeof(float));
}
render->lpVtbl->ReleaseBuffer(render, resample_frames,
muted ? AUDCLNT_BUFFERFLAGS_SILENT : 0);
unlock:
ReleaseSRWLockExclusive(&monitor->playback_mutex);
}
static inline void audio_monitor_free(struct audio_monitor *monitor)
{
if (monitor->ignore)
return;
if (monitor->source) {
obs_source_remove_audio_capture_callback(
monitor->source, on_audio_playback, monitor);
}
if (monitor->client)
monitor->client->lpVtbl->Stop(monitor->client);
safe_release(monitor->client);
safe_release(monitor->render);
audio_resampler_destroy(monitor->resampler);
circlebuf_free(&monitor->delay_buffer);
da_free(monitor->buf);
}
static enum speaker_layout convert_speaker_layout(DWORD layout, WORD channels)
{
switch (layout) {
@ -246,37 +157,14 @@ static enum speaker_layout convert_speaker_layout(DWORD layout, WORD channels)
return (enum speaker_layout)channels;
}
extern bool devices_match(const char *id1, const char *id2);
static bool audio_monitor_init(struct audio_monitor *monitor,
obs_source_t *source)
static bool audio_monitor_init_wasapi(struct audio_monitor *monitor)
{
bool success = false;
IMMDeviceEnumerator *immde = NULL;
WAVEFORMATEX *wfex = NULL;
bool success = false;
UINT32 frames;
HRESULT hr;
monitor->source = source;
const char *id = obs->audio.monitoring_device_id;
if (!id) {
warn("%s: No device ID set", __FUNCTION__);
return false;
}
if (source->info.output_flags & OBS_SOURCE_DO_NOT_SELF_MONITOR) {
obs_data_t *s = obs_source_get_settings(source);
const char *s_dev_id = obs_data_get_string(s, "device_id");
bool match = devices_match(s_dev_id, id);
obs_data_release(s);
if (match) {
monitor->ignore = true;
return true;
}
}
/* ------------------------------------------ *
* Init device */
@ -289,6 +177,7 @@ static bool audio_monitor_init(struct audio_monitor *monitor,
}
IMMDevice *device = NULL;
const char *const id = obs->audio.monitoring_device_id;
if (strcmp(id, "default") == 0) {
hr = immde->lpVtbl->GetDefaultAudioEndpoint(immde, eRender,
eConsole, &device);
@ -378,8 +267,6 @@ static bool audio_monitor_init(struct audio_monitor *monitor,
goto fail;
}
InitializeSRWLock(&monitor->playback_mutex);
success = true;
fail:
@ -389,6 +276,125 @@ fail:
return success;
}
static void on_audio_playback(void *param, obs_source_t *source,
const struct audio_data *audio_data, bool muted)
{
struct audio_monitor *monitor = param;
IAudioRenderClient *render = monitor->render;
uint8_t *resample_data[MAX_AV_PLANES];
float vol = source->user_volume;
uint32_t resample_frames;
uint64_t ts_offset;
bool success;
BYTE *output;
if (!TryAcquireSRWLockExclusive(&monitor->playback_mutex)) {
return;
}
if (os_atomic_load_long(&source->activate_refs) == 0) {
goto unlock;
}
success = audio_resampler_resample(
monitor->resampler, resample_data, &resample_frames, &ts_offset,
(const uint8_t *const *)audio_data->data,
(uint32_t)audio_data->frames);
if (!success) {
goto unlock;
}
UINT32 pad = 0;
monitor->client->lpVtbl->GetCurrentPadding(monitor->client, &pad);
bool decouple_audio = source->async_unbuffered &&
source->async_decoupled;
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;
}
}
HRESULT hr =
render->lpVtbl->GetBuffer(render, resample_frames, &output);
if (FAILED(hr)) {
goto unlock;
}
if (!muted) {
/* apply volume */
if (!close_float(vol, 1.0f, EPSILON)) {
register float *cur = (float *)resample_data[0];
register float *end =
cur + resample_frames * monitor->channels;
while (cur < end)
*(cur++) *= vol;
}
memcpy(output, resample_data[0],
resample_frames * monitor->channels * sizeof(float));
}
render->lpVtbl->ReleaseBuffer(render, resample_frames,
muted ? AUDCLNT_BUFFERFLAGS_SILENT : 0);
unlock:
ReleaseSRWLockExclusive(&monitor->playback_mutex);
}
static inline void audio_monitor_free(struct audio_monitor *monitor)
{
if (monitor->ignore)
return;
if (monitor->source) {
obs_source_remove_audio_capture_callback(
monitor->source, on_audio_playback, monitor);
}
if (monitor->client)
monitor->client->lpVtbl->Stop(monitor->client);
safe_release(monitor->client);
safe_release(monitor->render);
audio_resampler_destroy(monitor->resampler);
circlebuf_free(&monitor->delay_buffer);
da_free(monitor->buf);
}
extern bool devices_match(const char *id1, const char *id2);
static bool audio_monitor_init(struct audio_monitor *monitor,
obs_source_t *source)
{
monitor->source = source;
const char *id = obs->audio.monitoring_device_id;
if (!id) {
warn("%s: No device ID set", __FUNCTION__);
return false;
}
if (source->info.output_flags & OBS_SOURCE_DO_NOT_SELF_MONITOR) {
obs_data_t *s = obs_source_get_settings(source);
const char *s_dev_id = obs_data_get_string(s, "device_id");
bool match = devices_match(s_dev_id, id);
obs_data_release(s);
if (match) {
monitor->ignore = true;
return true;
}
}
InitializeSRWLock(&monitor->playback_mutex);
return audio_monitor_init_wasapi(monitor);
}
static void audio_monitor_init_final(struct audio_monitor *monitor)
{
if (monitor->ignore)