From a504691af83dd8bbb755b5ae8dea4a30c6fe7361 Mon Sep 17 00:00:00 2001 From: jpark37 Date: Sat, 25 Sep 2021 12:14:01 -0700 Subject: [PATCH] win-wasapi: Clean reset on initialization failure Do not store IAudioClient and IAudioCaptureClient onto the source object until initialization is almost complete, right before the capture thread is created. We don't to retain objects from failed attempts. Also clear them when stopping. --- plugins/win-wasapi/win-wasapi.cpp | 99 ++++++++++++++++++++++--------- 1 file changed, 70 insertions(+), 29 deletions(-) diff --git a/plugins/win-wasapi/win-wasapi.cpp b/plugins/win-wasapi/win-wasapi.cpp index 1d143406b..892e068a1 100644 --- a/plugins/win-wasapi/win-wasapi.cpp +++ b/plugins/win-wasapi/win-wasapi.cpp @@ -61,11 +61,22 @@ class WASAPISource { void Stop(); void Reconnect(); - ComPtr InitDevice(); - void InitClient(IMMDevice *device); - void ClearBuffer(IMMDevice *device); - void InitFormat(WAVEFORMATEX *wfex); - void InitCapture(); + static ComPtr InitDevice(IMMDeviceEnumerator *enumerator, + bool isDefaultDevice, + bool isInputDevice, + const string device_id, + wstring &default_id); + static ComPtr InitClient(IMMDevice *device, + bool isInputDevice, + enum speaker_layout &speakers, + enum audio_format &format, + uint32_t &sampleRate); + static void InitFormat(const WAVEFORMATEX *wfex, + enum speaker_layout &speakers, + enum audio_format &format, uint32_t &sampleRate); + static void ClearBuffer(IMMDevice *device); + static ComPtr InitCapture(IAudioClient *client, + HANDLE receiveSignal); void Initialize(); bool TryInitialize(); @@ -186,6 +197,9 @@ void WASAPISource::Stop() WaitForSingleObject(reconnectThread, INFINITE); ResetEvent(stopSignal); + + capture.Clear(); + client.Clear(); } WASAPISource::~WASAPISource() @@ -215,7 +229,11 @@ void WASAPISource::Update(obs_data_t *settings) Start(); } -ComPtr WASAPISource::InitDevice() +ComPtr WASAPISource::InitDevice(IMMDeviceEnumerator *enumerator, + bool isDefaultDevice, + bool isInputDevice, + const string device_id, + wstring &default_id) { ComPtr device; @@ -252,23 +270,26 @@ ComPtr WASAPISource::InitDevice() #define BUFFER_TIME_100NS (5 * 10000000) -void WASAPISource::InitClient(IMMDevice *device) +ComPtr WASAPISource::InitClient(IMMDevice *device, + bool isInputDevice, + enum speaker_layout &speakers, + enum audio_format &format, + uint32_t &sampleRate) { - CoTaskMemPtr wfex; - HRESULT res; - DWORD flags = AUDCLNT_STREAMFLAGS_EVENTCALLBACK; - - res = device->Activate(__uuidof(IAudioClient), CLSCTX_ALL, nullptr, - (void **)client.Assign()); + ComPtr client; + HRESULT res = device->Activate(__uuidof(IAudioClient), CLSCTX_ALL, + nullptr, (void **)client.Assign()); if (FAILED(res)) throw HRError("Failed to activate client context", res); + CoTaskMemPtr wfex; res = client->GetMixFormat(&wfex); if (FAILED(res)) throw HRError("Failed to get mix format", res); - InitFormat(wfex); + InitFormat(wfex, speakers, format, sampleRate); + DWORD flags = AUDCLNT_STREAMFLAGS_EVENTCALLBACK; if (!isInputDevice) flags |= AUDCLNT_STREAMFLAGS_LOOPBACK; @@ -276,6 +297,8 @@ void WASAPISource::InitClient(IMMDevice *device) BUFFER_TIME_100NS, 0, wfex, nullptr); if (FAILED(res)) throw HRError("Failed to initialize audio client", res); + + return client; } void WASAPISource::ClearBuffer(IMMDevice *device) @@ -340,7 +363,9 @@ static speaker_layout ConvertSpeakerLayout(DWORD layout, WORD channels) return (speaker_layout)channels; } -void WASAPISource::InitFormat(WAVEFORMATEX *wfex) +void WASAPISource::InitFormat(const WAVEFORMATEX *wfex, + enum speaker_layout &speakers, + enum audio_format &format, uint32_t &sampleRate) { DWORD layout = 0; @@ -350,15 +375,16 @@ void WASAPISource::InitFormat(WAVEFORMATEX *wfex) } /* WASAPI is always float */ - sampleRate = wfex->nSamplesPerSec; - format = AUDIO_FORMAT_FLOAT; speakers = ConvertSpeakerLayout(layout, wfex->nChannels); + format = AUDIO_FORMAT_FLOAT; + sampleRate = wfex->nSamplesPerSec; } -void WASAPISource::InitCapture() +ComPtr WASAPISource::InitCapture(IAudioClient *client, + HANDLE receiveSignal) { - HRESULT res = client->GetService(__uuidof(IAudioCaptureClient), - (void **)capture.Assign()); + ComPtr capture; + HRESULT res = client->GetService(IID_PPV_ARGS(capture.Assign())); if (FAILED(res)) throw HRError("Failed to create capture context", res); @@ -366,25 +392,40 @@ void WASAPISource::InitCapture() if (FAILED(res)) throw HRError("Failed to set event handle", res); - captureThread = CreateThread(nullptr, 0, WASAPISource::CaptureThread, - this, 0, nullptr); - if (!captureThread.Valid()) - throw "Failed to create capture thread"; + res = client->Start(); + if (FAILED(res)) + throw HRError("Failed to start capture client", res); - client->Start(); - active = true; + return capture; } void WASAPISource::Initialize() { - ComPtr device = InitDevice(); + ComPtr device = InitDevice(enumerator, isDefaultDevice, + isInputDevice, device_id, + default_id); device_name = GetDeviceName(device); - InitClient(device); + ComPtr temp_client = + InitClient(device, isInputDevice, speakers, format, sampleRate); if (!isInputDevice) ClearBuffer(device); - InitCapture(); + ComPtr temp_capture = + InitCapture(temp_client, receiveSignal); + + client = std::move(temp_client); + capture = std::move(temp_capture); + + captureThread = CreateThread(nullptr, 0, WASAPISource::CaptureThread, + this, 0, nullptr); + if (!captureThread.Valid()) { + capture.Clear(); + client.Clear(); + throw "Failed to create capture thread"; + } + + active = true; blog(LOG_INFO, "WASAPI: Device '%s' [%" PRIu32 " Hz] initialized", device_name.c_str(), sampleRate);