Bug 1103824 - Ensure first initialization of IAudioClient happens on STA thread. r=padenot

This commit is contained in:
Matthew Gregan 2015-05-20 13:22:38 +12:00
parent 4e2c55e078
commit 0bbda78298
2 changed files with 64 additions and 41 deletions

View File

@ -131,6 +131,15 @@ void InitLibrary()
Preferences::RegisterCallback(PrefChanged, PREF_VOLUME_SCALE); Preferences::RegisterCallback(PrefChanged, PREF_VOLUME_SCALE);
PrefChanged(PREF_CUBEB_LATENCY, nullptr); PrefChanged(PREF_CUBEB_LATENCY, nullptr);
Preferences::RegisterCallback(PrefChanged, PREF_CUBEB_LATENCY); Preferences::RegisterCallback(PrefChanged, PREF_CUBEB_LATENCY);
#if defined(XP_WIN)
// Ensure the first call to cubeb_init is made from the main thread.
MOZ_ASSERT(NS_IsMainThread() && !sCubebContext);
cubeb * context;
if (cubeb_init(&context, "FirstInitContext") == CUBEB_OK) {
cubeb_destroy(context);
}
#endif
} }
void ShutdownLibrary() void ShutdownLibrary()

View File

@ -144,28 +144,17 @@ private:
}; };
struct auto_com { struct auto_com {
auto_com() { auto_com(DWORD dwCoInit = COINIT_MULTITHREADED) {
result = CoInitializeEx(NULL, COINIT_MULTITHREADED); result = CoInitializeEx(NULL, dwCoInit);
} }
~auto_com() { ~auto_com() {
if (result == RPC_E_CHANGED_MODE) { if (ok()) {
// This is not an error, COM was not initialized by this function, so it is
// not necessary to uninit it.
LOG("COM already initialized in STA.\n");
} else if (result == 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\n");
}
if (SUCCEEDED(result)) {
CoUninitialize(); CoUninitialize();
} }
} }
bool ok() { bool ok() {
return result == RPC_E_CHANGED_MODE || SUCCEEDED(result); return SUCCEEDED(result);
} }
private:
HRESULT result; HRESULT result;
}; };
@ -492,6 +481,7 @@ wasapi_stream_render_loop(LPVOID stream)
auto_com com; auto_com com;
if (!com.ok()) { if (!com.ok()) {
LOG("COM initialization failed on render_loop thread.\n"); LOG("COM initialization failed on render_loop thread.\n");
stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_ERROR);
return 0; return 0;
} }
@ -688,14 +678,19 @@ HRESULT get_default_endpoint(IMMDevice ** device)
return ERROR_SUCCESS; return ERROR_SUCCESS;
} }
owned_critical_section g_first_init_lock;
bool g_first_init = false;
} // namespace anonymous } // namespace anonymous
extern "C" { extern "C" {
int wasapi_init(cubeb ** context, char const * context_name) int wasapi_init(cubeb ** context, char const * context_name)
{ {
HRESULT hr; auto_lock lock(&g_first_init_lock);
auto_com com; if (!g_first_init) {
if (!com.ok()) { // Per the MSDN documentation for IAudioClient, the first use *must* be made from an STA thread.
auto_com com(COINIT_APARTMENTTHREADED);
if (FAILED(com.result)) {
return CUBEB_ERROR; return CUBEB_ERROR;
} }
@ -703,12 +698,37 @@ int wasapi_init(cubeb ** context, char const * context_name)
so that this backend is not incorrectly enabled on platforms that don't so that this backend is not incorrectly enabled on platforms that don't
support WASAPI. */ support WASAPI. */
IMMDevice * device; IMMDevice * device;
hr = get_default_endpoint(&device); HRESULT hr = get_default_endpoint(&device);
if (FAILED(hr)) { if (FAILED(hr)) {
LOG("Could not get device.\n"); LOG("Could not get device: %x\n", hr);
return CUBEB_ERROR; return CUBEB_ERROR;
} }
IAudioClient * client;
hr = device->Activate(__uuidof(IAudioClient),
CLSCTX_INPROC_SERVER,
NULL, (void **)&client);
if (SUCCEEDED(hr)) {
WAVEFORMATEX * mix_format;
hr = client->GetMixFormat(&mix_format);
if (SUCCEEDED(hr)) {
hr = client->Initialize(AUDCLNT_SHAREMODE_SHARED,
AUDCLNT_STREAMFLAGS_EVENTCALLBACK |
AUDCLNT_STREAMFLAGS_NOPERSIST,
ms_to_hns(100),
0,
mix_format,
NULL);
CoTaskMemFree(mix_format);
g_first_init = true;
}
SafeRelease(client);
}
SafeRelease(device); SafeRelease(device);
if (FAILED(hr)) {
LOG("Could not initialize IAudioClient: %x\n", hr);
return CUBEB_ERROR;
}
}
cubeb * ctx = (cubeb *)calloc(1, sizeof(cubeb)); cubeb * ctx = (cubeb *)calloc(1, sizeof(cubeb));
@ -784,10 +804,8 @@ wasapi_get_max_channel_count(cubeb * ctx, uint32_t * max_channels)
HRESULT hr; HRESULT hr;
IAudioClient * client; IAudioClient * client;
WAVEFORMATEX * mix_format; WAVEFORMATEX * mix_format;
auto_com com; XASSERT(g_first_init);
if (!com.ok()) { auto_com com; /* don't care what COM mode we're in here */
return CUBEB_ERROR;
}
XASSERT(ctx && max_channels); XASSERT(ctx && max_channels);
@ -825,10 +843,8 @@ wasapi_get_min_latency(cubeb * ctx, cubeb_stream_params params, uint32_t * laten
HRESULT hr; HRESULT hr;
IAudioClient * client; IAudioClient * client;
REFERENCE_TIME default_period; REFERENCE_TIME default_period;
auto_com com; XASSERT(g_first_init);
if (!com.ok()) { auto_com com; /* don't care what COM mode we're in here */
return CUBEB_ERROR;
}
IMMDevice * device; IMMDevice * device;
hr = get_default_endpoint(&device); hr = get_default_endpoint(&device);
@ -872,10 +888,8 @@ wasapi_get_preferred_sample_rate(cubeb * ctx, uint32_t * rate)
HRESULT hr; HRESULT hr;
IAudioClient * client; IAudioClient * client;
WAVEFORMATEX * mix_format; WAVEFORMATEX * mix_format;
auto_com com; XASSERT(g_first_init);
if (!com.ok()) { auto_com com; /* don't care what COM mode we're in here */
return CUBEB_ERROR;
}
IMMDevice * device; IMMDevice * device;
hr = get_default_endpoint(&device); hr = get_default_endpoint(&device);