in src/cubeb_wasapi.cpp [1376:1524]
static unsigned int __stdcall wasapi_stream_render_loop(LPVOID stream)
{
AutoRegisterThread raii("cubeb rendering thread");
cubeb_stream * stm = static_cast<cubeb_stream *>(stream);
auto_stream_ref stream_ref(stm);
struct auto_com {
auto_com()
{
HRESULT hr = CoInitializeEx(nullptr, COINIT_MULTITHREADED);
XASSERT(SUCCEEDED(hr));
}
~auto_com() { CoUninitialize(); }
} com;
bool is_playing = true;
HANDLE wait_array[4] = {stm->shutdown_event, stm->reconfigure_event,
stm->refill_event, stm->input_available_event};
HANDLE mmcss_handle = NULL;
HRESULT hr = 0;
DWORD mmcss_task_index = 0;
// Signal wasapi_stream_start that we've initialized COM and incremented
// the stream's ref_count.
BOOL ok = SetEvent(stm->thread_ready_event);
if (!ok) {
LOG("thread_ready SetEvent failed: %lx", GetLastError());
return 0;
}
/* We could consider using "Pro Audio" here for WebAudio and
maybe WebRTC. */
mmcss_handle = AvSetMmThreadCharacteristicsA("Audio", &mmcss_task_index);
if (!mmcss_handle) {
/* This is not fatal, but we might glitch under heavy load. */
LOG("Unable to use mmcss to bump the render thread priority: %lx",
GetLastError());
}
while (is_playing) {
DWORD waitResult = WaitForMultipleObjects(ARRAY_LENGTH(wait_array),
wait_array, FALSE, INFINITE);
switch (waitResult) {
case WAIT_OBJECT_0: { /* shutdown */
is_playing = false;
/* We don't check if the drain is actually finished here, we just want to
shutdown. */
if (stm->draining) {
wasapi_state_callback(stm, stm->user_ptr, CUBEB_STATE_DRAINED);
}
continue;
}
case WAIT_OBJECT_0 + 1: { /* reconfigure */
auto_lock lock(stm->stream_reset_lock);
if (!stm->active) {
/* Avoid reconfiguring, stream start will handle it. */
LOG("Stream is not active, ignoring reconfigure.");
continue;
}
XASSERT(stm->output_client || stm->input_client);
LOG("Reconfiguring the stream");
/* Close the stream */
bool was_running = false;
if (stm->output_client) {
was_running = stm->output_client->Stop() == S_OK;
LOG("Output stopped.");
}
if (stm->input_client) {
was_running = stm->input_client->Stop() == S_OK;
LOG("Input stopped.");
}
close_wasapi_stream(stm);
LOG("Stream closed.");
/* Reopen a stream and start it immediately. This will automatically
pick the new default device for this role. */
int r = setup_wasapi_stream(stm);
if (r != CUBEB_OK) {
LOG("Error setting up the stream during reconfigure.");
/* Don't destroy the stream here, since we expect the caller to do
so after the error has propagated via the state callback. */
is_playing = false;
hr = E_FAIL;
continue;
}
LOG("Stream setup successfuly.");
XASSERT(stm->output_client || stm->input_client);
if (was_running && stm->output_client) {
hr = stm->output_client->Start();
if (FAILED(hr)) {
LOG("Error starting output after reconfigure, error: %lx", hr);
is_playing = false;
continue;
}
LOG("Output started after reconfigure.");
}
if (was_running && stm->input_client) {
hr = stm->input_client->Start();
if (FAILED(hr)) {
LOG("Error starting input after reconfiguring, error: %lx", hr);
is_playing = false;
continue;
}
LOG("Input started after reconfigure.");
}
break;
}
case WAIT_OBJECT_0 + 2: /* refill */
XASSERT((has_input(stm) && has_output(stm)) ||
(!has_input(stm) && has_output(stm)));
is_playing = stm->refill_callback(stm);
break;
case WAIT_OBJECT_0 + 3: { /* input available */
HRESULT rv = get_input_buffer(stm);
if (FAILED(rv)) {
is_playing = false;
continue;
}
if (!has_output(stm)) {
is_playing = stm->refill_callback(stm);
}
break;
}
default:
LOG("case %lu not handled in render loop.", waitResult);
XASSERT(false);
}
}
// Stop audio clients since this thread will no longer service
// the events.
if (stm->output_client) {
stm->output_client->Stop();
}
if (stm->input_client) {
stm->input_client->Stop();
}
if (mmcss_handle) {
AvRevertMmThreadCharacteristics(mmcss_handle);
}
if (FAILED(hr)) {
wasapi_state_callback(stm, stm->user_ptr, CUBEB_STATE_ERROR);
}
return 0;
}