Bug 848954 - Part 12 - Make sure COM is initialized when calling into WASAPI functions. r=kinetik
authorPaul Adenot <paul@paul.cx>
Tue, 26 Aug 2014 17:01:35 +0200
changeset 223502 6a0199460e68771286d87578be5d280976c2705a
parent 223501 ec95ea99d750c2b129124e0ef1de89b7fcb4b6d5
child 223503 dc47d39b955e7750479383384d05015b39a8d0f4
push id3979
push userraliiev@mozilla.com
push dateMon, 13 Oct 2014 16:35:44 +0000
treeherdermozilla-beta@30f2cc610691 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskinetik
bugs848954
milestone34.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 848954 - Part 12 - Make sure COM is initialized when calling into WASAPI functions. r=kinetik
media/libcubeb/src/cubeb_wasapi.cpp
--- a/media/libcubeb/src/cubeb_wasapi.cpp
+++ b/media/libcubeb/src/cubeb_wasapi.cpp
@@ -63,16 +63,45 @@ SafeRelease(HANDLE handle)
 template <typename T>
 void SafeRelease(T * ptr)
 {
   if (ptr) {
     ptr->Release();
   }
 }
 
+struct auto_com {
+  auto_com()
+  : need_uninit(true) {
+    HRESULT hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
+    // This is for information purposes only, in anycase, COM is initialized
+    // at the end of the constructor.
+    if (hr == RPC_E_CHANGED_MODE) {
+      // This is an error, COM was not initialized by this function, so it is
+      // not necessary to uninit it.
+      LOG("COM already initialized in STA.");
+      need_uninit = false;
+    } else if (hr == S_FALSE) {
+      // This is not an error. We are allowed to call CoInitializeEx more than
+      // once, as long as it is matches by an CoUninitialize call.
+      // We do that in the dtor which is guaranteed to be called.
+      LOG("COM already initialized in MTA");
+    } else if (hr == S_OK) {
+      LOG("COM initialized.");
+    }
+  }
+  ~auto_com() {
+    if (need_uninit) {
+      CoUninitialize();
+    }
+  }
+private:
+  bool need_uninit;
+};
+
 typedef HANDLE (WINAPI *set_mm_thread_characteristics_function)(
                                       const char* TaskName, LPDWORD TaskIndex);
 typedef BOOL (WINAPI *revert_mm_thread_characteristics_function)(HANDLE handle);
 
 extern cubeb_ops const wasapi_ops;
 }
 
 struct cubeb
@@ -239,31 +268,27 @@ wasapi_stream_render_loop(LPVOID stream)
   cubeb_stream * stm = static_cast<cubeb_stream *>(stream);
 
   bool is_playing = true;
   HANDLE wait_array[2] = {stm->shutdown_event, stm->refill_event};
   HANDLE mmcss_handle = NULL;
   HRESULT hr;
   bool first = true;
   DWORD mmcss_task_index = 0;
+  auto_com com;
 
   /* We could consider using "Pro Audio" here for WebAudio and
    * maybe WebRTC. */
   mmcss_handle =
     stm->context->set_mm_thread_characteristics("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: %x", GetLastError());
   }
 
-  hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
-  if (FAILED(hr)) {
-    LOG("could not initialize COM in render thread: %x", hr);
-    return hr;
-  }
 
   while (is_playing) {
     DWORD waitResult = WaitForMultipleObjects(ARRAY_LENGTH(wait_array),
                                               wait_array,
                                               FALSE,
                                               INFINITE);
 
     switch (waitResult) {
@@ -324,17 +349,16 @@ wasapi_stream_render_loop(LPVOID stream)
   }
 
   if (FAILED(hr)) {
     stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_STOPPED);
   }
 
   stm->context->revert_mm_thread_characteristics(mmcss_handle);
 
-  CoUninitialize();
   return 0;
 }
 
 void wasapi_destroy(cubeb * context);
 
 HANDLE WINAPI set_mm_thread_characteristics_noop(const char *, LPDWORD mmcss_task_index)
 {
   return (HANDLE)1;
@@ -371,22 +395,17 @@ HRESULT get_default_endpoint(IMMDevice *
   return ERROR_SUCCESS;
 }
 } // namespace anonymous
 
 extern "C" {
 int wasapi_init(cubeb ** context, char const * context_name)
 {
   HRESULT hr;
-
-  hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
-  if (FAILED(hr)) {
-    LOG("Could not init COM.");
-    return CUBEB_ERROR;
-  }
+  auto_com com;
 
   /* We don't use the device yet, but need to make sure we can initialize one
      so that this backend is not incorrectly enabled on platforms that don't
      support WASAPI. */
   IMMDevice * device;
   hr = get_default_endpoint(&device);
   if (FAILED(hr)) {
     LOG("Could not get device.");
@@ -438,23 +457,25 @@ void wasapi_destroy(cubeb * context)
 char const* wasapi_get_backend_id(cubeb * context)
 {
   return "wasapi";
 }
 
 int
 wasapi_get_max_channel_count(cubeb * ctx, uint32_t * max_channels)
 {
+  HRESULT hr;
   IAudioClient * client;
   WAVEFORMATEX * mix_format;
+  auto_com com;
 
   assert(ctx && max_channels);
 
   IMMDevice * device;
-  HRESULT hr = get_default_endpoint(&device);
+  hr = get_default_endpoint(&device);
   if (FAILED(hr)) {
     return CUBEB_ERROR;
   }
 
   hr = device->Activate(__uuidof(IAudioClient),
                         CLSCTX_INPROC_SERVER,
                         NULL, (void **)&client);
   SafeRelease(device);
@@ -477,54 +498,61 @@ wasapi_get_max_channel_count(cubeb * ctx
 }
 
 int
 wasapi_get_min_latency(cubeb * ctx, cubeb_stream_params params, uint32_t * latency_ms)
 {
   HRESULT hr;
   IAudioClient * client;
   REFERENCE_TIME default_period;
+  auto_com com;
 
   IMMDevice * device;
   hr = get_default_endpoint(&device);
   if (FAILED(hr)) {
+    LOG("Could not get default endpoint:%x.", hr)
     return CUBEB_ERROR;
   }
 
   hr = device->Activate(__uuidof(IAudioClient),
                         CLSCTX_INPROC_SERVER,
                         NULL, (void **)&client);
   SafeRelease(device);
   if (FAILED(hr)) {
+    LOG("Could not activate device for latency: %x.", hr)
     return CUBEB_ERROR;
   }
 
   /* The second parameter is for exclusive mode, that we don't use. */
   hr = client->GetDevicePeriod(&default_period, NULL);
   if (FAILED(hr)) {
     SafeRelease(client);
+    LOG("Could not get device period: %x.", hr)
     return CUBEB_ERROR;
   }
 
+  LOG("default device period: %ld", default_period)
+
   /* According to the docs, the best latency we can achieve is by synchronizing
    * the stream and the engine.
    * http://msdn.microsoft.com/en-us/library/windows/desktop/dd370871%28v=vs.85%29.aspx */
   *latency_ms = hns_to_ms(default_period);
 
   SafeRelease(client);
 
   return CUBEB_OK;
 }
 
 int
 wasapi_get_preferred_sample_rate(cubeb * ctx, uint32_t * rate)
 {
   HRESULT hr;
   IAudioClient * client;
   WAVEFORMATEX * mix_format;
+  auto_com com;
 
   IMMDevice * device;
   hr = get_default_endpoint(&device);
   if (FAILED(hr)) {
     return CUBEB_ERROR;
   }
 
   hr = device->Activate(__uuidof(IAudioClient),
@@ -622,25 +650,20 @@ handle_channel_layout(cubeb_stream * stm
 int
 wasapi_stream_init(cubeb * context, cubeb_stream ** stream,
                    char const * stream_name, cubeb_stream_params stream_params,
                    unsigned int latency, cubeb_data_callback data_callback,
                    cubeb_state_callback state_callback, void * user_ptr)
 {
   HRESULT hr;
   WAVEFORMATEX * mix_format;
+  auto_com com;
 
   assert(context && stream);
 
-  hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
-  if (FAILED(hr)) {
-    LOG("Could not initialize COM.");
-    return CUBEB_ERROR;
-  }
-
   cubeb_stream * stm = (cubeb_stream *)calloc(1, sizeof(cubeb_stream));
 
   assert(stm);
 
   stm->context = context;
   stm->data_callback = data_callback;
   stm->state_callback = state_callback;
   stm->user_ptr = user_ptr;
@@ -803,17 +826,16 @@ void wasapi_stream_destroy(cubeb_stream 
   SafeRelease(stm->client);
   SafeRelease(stm->render_client);
   SafeRelease(stm->audio_clock);
 
   cubeb_resampler_destroy(stm->resampler);
 
   free(stm->mix_buffer);
   free(stm);
-  CoUninitialize();
 }
 
 int wasapi_stream_start(cubeb_stream * stm)
 {
   HRESULT hr;
 
   assert(stm);