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.
master
jpark37 2021-09-25 12:14:01 -07:00
parent b166b0488b
commit a504691af8
1 changed files with 70 additions and 29 deletions

View File

@ -61,11 +61,22 @@ class WASAPISource {
void Stop();
void Reconnect();
ComPtr<IMMDevice> InitDevice();
void InitClient(IMMDevice *device);
void ClearBuffer(IMMDevice *device);
void InitFormat(WAVEFORMATEX *wfex);
void InitCapture();
static ComPtr<IMMDevice> InitDevice(IMMDeviceEnumerator *enumerator,
bool isDefaultDevice,
bool isInputDevice,
const string device_id,
wstring &default_id);
static ComPtr<IAudioClient> 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<IAudioCaptureClient> 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<IMMDevice> WASAPISource::InitDevice()
ComPtr<IMMDevice> WASAPISource::InitDevice(IMMDeviceEnumerator *enumerator,
bool isDefaultDevice,
bool isInputDevice,
const string device_id,
wstring &default_id)
{
ComPtr<IMMDevice> device;
@ -252,23 +270,26 @@ ComPtr<IMMDevice> WASAPISource::InitDevice()
#define BUFFER_TIME_100NS (5 * 10000000)
void WASAPISource::InitClient(IMMDevice *device)
ComPtr<IAudioClient> WASAPISource::InitClient(IMMDevice *device,
bool isInputDevice,
enum speaker_layout &speakers,
enum audio_format &format,
uint32_t &sampleRate)
{
CoTaskMemPtr<WAVEFORMATEX> wfex;
HRESULT res;
DWORD flags = AUDCLNT_STREAMFLAGS_EVENTCALLBACK;
res = device->Activate(__uuidof(IAudioClient), CLSCTX_ALL, nullptr,
(void **)client.Assign());
ComPtr<IAudioClient> 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<WAVEFORMATEX> 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<IAudioCaptureClient> WASAPISource::InitCapture(IAudioClient *client,
HANDLE receiveSignal)
{
HRESULT res = client->GetService(__uuidof(IAudioCaptureClient),
(void **)capture.Assign());
ComPtr<IAudioCaptureClient> 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<IMMDevice> device = InitDevice();
ComPtr<IMMDevice> device = InitDevice(enumerator, isDefaultDevice,
isInputDevice, device_id,
default_id);
device_name = GetDeviceName(device);
InitClient(device);
ComPtr<IAudioClient> temp_client =
InitClient(device, isInputDevice, speakers, format, sampleRate);
if (!isInputDevice)
ClearBuffer(device);
InitCapture();
ComPtr<IAudioCaptureClient> 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);