static unsigned int __stdcall wasapi_stream_render_loop()

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;
}