Bug 923992 - Call GetDefaultAudioEndpoint when needed rather than caching its result when called at cubeb init time. r=padenot
authorMatthew Gregan <kinetik@flim.org>
Thu, 28 Nov 2013 14:12:45 +1300
changeset 158006 41c241079f2515c0ac764516c38564617e02bfc3
parent 158005 6c15f3df605a3d1d9df06bc12673dec99b9c2143
child 158007 c84597fb24e0c690da82e303444da6b5ae1ec6bc
push id36906
push usermgregan@mozilla.com
push dateFri, 29 Nov 2013 03:44:16 +0000
treeherdermozilla-inbound@41c241079f25 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerspadenot
bugs923992
milestone28.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 923992 - Call GetDefaultAudioEndpoint when needed rather than caching its result when called at cubeb init time. r=padenot
media/libcubeb/README_MOZILLA
media/libcubeb/src/cubeb_wasapi.cpp
--- a/media/libcubeb/README_MOZILLA
+++ b/media/libcubeb/README_MOZILLA
@@ -1,8 +1,8 @@
 The source from this directory was copied from the cubeb 
 git repository using the update.sh script.  The only changes
 made were those applied by update.sh and the addition of
 Makefile.in build files for the Mozilla build system.
 
 The cubeb git repository is: git://github.com/kinetiknz/cubeb.git
 
-The git commit ID used was e92a27c96c0efd33acf983e4c873376ff4cae3d8.
+The git commit ID used was a137c78c4ba1a6b0ebde5548664bdf6390ef5437.
--- a/media/libcubeb/src/cubeb_wasapi.cpp
+++ b/media/libcubeb/src/cubeb_wasapi.cpp
@@ -83,17 +83,16 @@ typedef HANDLE (WINAPI *set_mm_thread_ch
 typedef BOOL (WINAPI *revert_mm_thread_characteristics_function)(HANDLE handle);
 
 extern cubeb_ops const wasapi_ops;
 }
 
 struct cubeb
 {
   cubeb_ops const * ops;
-  IMMDevice * device;
   /* Library dynamically opened to increase the render
    * thread priority, and the two function pointers we need. */
   HMODULE mmcss_module;
   set_mm_thread_characteristics_function set_mm_thread_characteristics;
   revert_mm_thread_characteristics_function revert_mm_thread_characteristics;
 };
 
 
@@ -395,17 +394,17 @@ wasapi_stream_render_loop(LPVOID stream)
         LOG("failed to get buffer.");
         is_playing = false;
       }
     }
     break;
     default:
       LOG("case %d not handled in render loop.", waitResult);
       abort();
-    };
+    }
   }
 
   if (FAILED(hr)) {
     stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_STOPPED);
   }
 
   stm->context->revert_mm_thread_characteristics(mmcss_handle);
 
@@ -419,57 +418,70 @@ HANDLE WINAPI set_mm_thread_characterist
 {
   return (HANDLE)1;
 }
 
 BOOL WINAPI revert_mm_thread_characteristics_noop(HANDLE mmcss_handle)
 {
   return true;
 }
+
+HRESULT get_default_endpoint(IMMDevice ** device)
+{
+  IMMDeviceEnumerator * enumerator;
+  HRESULT hr = CoCreateInstance(__uuidof(MMDeviceEnumerator),
+                                NULL, CLSCTX_INPROC_SERVER,
+                                IID_PPV_ARGS(&enumerator));
+  if (FAILED(hr)) {
+    LOG("Could not get device enumerator.");
+    return hr;
+  }
+  /* eMultimedia is okay for now ("Music, movies, narration, [...]").
+   * We will need to change this when we distinguish streams by use-case, other
+   * possible values being eConsole ("Games, system notification sounds [...]")
+   * and eCommunication ("Voice communication"). */
+  hr = enumerator->GetDefaultAudioEndpoint(eRender, eMultimedia, device);
+  if (FAILED(hr)) {
+    LOG("Could not get default audio endpoint.");
+    SafeRelease(enumerator);
+    return hr;
+  }
+
+  SafeRelease(enumerator);
+
+  return ERROR_SUCCESS;
+}
 } // namespace anonymous
 
 extern "C" {
 int wasapi_init(cubeb ** context, char const * context_name)
 {
   HRESULT hr;
-  IMMDeviceEnumerator * enumerator = NULL;
 
   hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
   if (FAILED(hr)) {
     LOG("Could not init COM.");
     return CUBEB_ERROR;
   }
 
+  /* 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.");
+    return CUBEB_ERROR;
+  }
+  SafeRelease(device);
+
   cubeb * ctx = (cubeb *)calloc(1, sizeof(cubeb));
 
   ctx->ops = &wasapi_ops;
 
-  hr = CoCreateInstance(__uuidof(MMDeviceEnumerator),
-                        NULL, CLSCTX_INPROC_SERVER,
-                        IID_PPV_ARGS(&enumerator));
-  if (FAILED(hr)) {
-    LOG("Could not get device enumerator.");
-    wasapi_destroy(ctx);
-    return CUBEB_ERROR;
-  }
-
-  /* eMultimedia is okay for now ("Music, movies, narration, [...]").
-   * We will need to change this when we distinguish streams by use-case, other
-   * possible values being eConsole ("Games, system notification sounds [...]")
-   * and eCommunication ("Voice communication"). */
-  hr = enumerator->GetDefaultAudioEndpoint(eRender,
-                                           eMultimedia,
-                                           &ctx->device);
-  if (FAILED(hr)) {
-    LOG("Could not get default audio endpoint.");
-    SafeRelease(enumerator);
-    wasapi_destroy(ctx);
-    return CUBEB_ERROR;
-  }
-
   ctx->mmcss_module = LoadLibraryA("Avrt.dll");
 
   if (ctx->mmcss_module) {
     ctx->set_mm_thread_characteristics =
       (set_mm_thread_characteristics_function) GetProcAddress(
           ctx->mmcss_module, "AvSetMmThreadCharacteristicsA");
     ctx->revert_mm_thread_characteristics =
       (revert_mm_thread_characteristics_function) GetProcAddress(
@@ -480,59 +492,63 @@ int wasapi_init(cubeb ** context, char c
     }
   } else {
     // This is not a fatal error, but we might end up glitching when
     // the system is under high load.
     LOG("Could not load Avrt.dll");
     ctx->set_mm_thread_characteristics = &set_mm_thread_characteristics_noop;
     ctx->revert_mm_thread_characteristics = &revert_mm_thread_characteristics_noop;
   }
-  
-  SafeRelease(enumerator);
 
   *context = ctx;
 
   return CUBEB_OK;
 }
 }
 
 namespace {
 
 void wasapi_destroy(cubeb * context)
 {
-  SafeRelease(context->device);
   if (context->mmcss_module) {
     FreeLibrary(context->mmcss_module);
   }
   free(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;
 
   assert(ctx && max_channels);
 
-  hr = ctx->device->Activate(__uuidof(IAudioClient),
-                             CLSCTX_INPROC_SERVER,
-                             NULL, (void **)&client);
+  IMMDevice * device;
+  HRESULT hr = get_default_endpoint(&device);
+  if (FAILED(hr)) {
+    return CUBEB_ERROR;
+  }
+
+  hr = device->Activate(__uuidof(IAudioClient),
+                        CLSCTX_INPROC_SERVER,
+                        NULL, (void **)&client);
+  SafeRelease(device);
   if (FAILED(hr)) {
     return CUBEB_ERROR;
   }
 
   hr = client->GetMixFormat(&mix_format);
   if (FAILED(hr)) {
+    SafeRelease(client);
     return CUBEB_ERROR;
   }
 
   *max_channels = mix_format->nChannels;
 
   CoTaskMemFree(mix_format);
   SafeRelease(client);
 
@@ -541,26 +557,36 @@ 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;
 
-  hr = ctx->device->Activate(__uuidof(IAudioClient),
-                             CLSCTX_INPROC_SERVER,
-                             NULL, (void **)&client);
+  IMMDevice * 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);
   if (FAILED(hr)) {
     return CUBEB_ERROR;
   }
 
   /* The second parameter is for exclusive mode, that we don't use. */
-  hr= client->GetDevicePeriod(&default_period, NULL);
+  hr = client->GetDevicePeriod(&default_period, NULL);
+  if (FAILED(hr)) {
+    SafeRelease(client);
+    return CUBEB_ERROR;
+  }
 
   /* 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);
 
@@ -569,26 +595,31 @@ wasapi_get_min_latency(cubeb * ctx, cube
 
 int
 wasapi_get_preferred_sample_rate(cubeb * ctx, uint32_t * rate)
 {
   HRESULT hr;
   IAudioClient * client;
   WAVEFORMATEX * mix_format;
 
-  hr = ctx->device->Activate(__uuidof(IAudioClient),
-                             CLSCTX_INPROC_SERVER,
-                             NULL, (void **)&client);
+  IMMDevice * 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);
   if (FAILED(hr)) {
     return CUBEB_ERROR;
   }
 
   hr = client->GetMixFormat(&mix_format);
-
   if (FAILED(hr)) {
     SafeRelease(client);
     return CUBEB_ERROR;
   }
 
   *rate = mix_format->nSamplesPerSec;
 
   CoTaskMemFree(mix_format);
@@ -706,30 +737,43 @@ wasapi_stream_init(cubeb * context, cube
 
   if (!stm->refill_event) {
     SafeRelease(stm->shutdown_event);
     LOG("Can't create the refill event, error: %d.", GetLastError());
     wasapi_stream_destroy(stm);
     return CUBEB_ERROR;
   }
 
+  IMMDevice * device;
+  hr = get_default_endpoint(&device);
+  if (FAILED(hr)) {
+    wasapi_stream_destroy(stm);
+    return CUBEB_ERROR;
+  }
+
   /* Get a client. We will get all other interfaces we need from
    * this pointer. */
-  hr = context->device->Activate(__uuidof(IAudioClient),
-                                 CLSCTX_INPROC_SERVER,
-                                 NULL, (void **)&stm->client);
+  hr = device->Activate(__uuidof(IAudioClient),
+                        CLSCTX_INPROC_SERVER,
+                        NULL, (void **)&stm->client);
+  SafeRelease(device);
   if (FAILED(hr)) {
     LOG("Could not activate the device to get an audio client.");
     wasapi_stream_destroy(stm);
     return CUBEB_ERROR;
   }
 
   /* We have to distinguish between the format the mixer uses,
   * and the format the stream we want to play uses. */
-  stm->client->GetMixFormat(&mix_format);
+  hr = stm->client->GetMixFormat(&mix_format);
+  if (FAILED(hr)) {
+    LOG("Could not fetch current mix format from the audio client.");
+    wasapi_stream_destroy(stm);
+    return CUBEB_ERROR;
+  }
 
   handle_channel_layout(stm, &mix_format, &stream_params);
 
   /* Shared mode WASAPI always supports float32 sample format, so this
    * is safe. */
   stm->mix_params.format = CUBEB_SAMPLE_FLOAT32NE;
 
   stm->mix_params.rate = mix_format->nSamplesPerSec;
@@ -805,25 +849,25 @@ wasapi_stream_init(cubeb * context, cube
   hr = stm->client->SetEventHandle(stm->refill_event);
   if (FAILED(hr)) {
     LOG("Could set the event handle for the client.");
     wasapi_stream_destroy(stm);
     return CUBEB_ERROR;
   }
 
   hr = stm->client->GetService(__uuidof(IAudioRenderClient),
-                                     (void **)&stm->render_client);
+                               (void **)&stm->render_client);
   if (FAILED(hr)) {
     LOG("Could not get the render client.");
     wasapi_stream_destroy(stm);
     return CUBEB_ERROR;
   }
 
   hr = stm->client->GetService(__uuidof(IAudioClock),
-                                     (void **)&stm->audio_clock);
+                               (void **)&stm->audio_clock);
   if (FAILED(hr)) {
     LOG("Could not get the IAudioClock.");
     wasapi_stream_destroy(stm);
     return CUBEB_ERROR;
   }
 
   hr = stm->audio_clock->GetFrequency(&stm->clock_freq);
   if (FAILED(hr)) {