diff --git a/modules/audio_device/audio_device_unittest.cc b/modules/audio_device/audio_device_unittest.cc index b708856efb..709b191b9f 100644 --- a/modules/audio_device/audio_device_unittest.cc +++ b/modules/audio_device/audio_device_unittest.cc @@ -38,8 +38,8 @@ #ifdef WEBRTC_WIN #include "modules/audio_device/include/audio_device_factory.h" #include "modules/audio_device/win/core_audio_utility_win.h" - -#endif +#include "rtc_base/win/scoped_com_initializer.h" +#endif // WEBRTC_WIN using ::testing::_; using ::testing::AtLeast; @@ -596,8 +596,8 @@ class MAYBE_AudioDeviceTest // We must initialize the COM library on a thread before we calling any of // the library functions. All COM functions in the ADM will return // CO_E_NOTINITIALIZED otherwise. - com_initializer_ = std::make_unique( - webrtc_win::ScopedCOMInitializer::kMTA); + com_initializer_ = + std::make_unique(ScopedCOMInitializer::kMTA); EXPECT_TRUE(com_initializer_->Succeeded()); EXPECT_TRUE(webrtc_win::core_audio_utility::IsSupported()); EXPECT_TRUE(webrtc_win::core_audio_utility::IsMMCSSSupported()); @@ -656,7 +656,7 @@ class MAYBE_AudioDeviceTest private: #ifdef WEBRTC_WIN // Windows Core Audio based ADM needs to run on a COM initialized thread. - std::unique_ptr com_initializer_; + std::unique_ptr com_initializer_; #endif AudioDeviceModule::AudioLayer audio_layer_; std::unique_ptr task_queue_factory_; @@ -691,8 +691,7 @@ TEST(MAYBE_AudioDeviceTestWin, ConstructDestructWithFactory) { // CreateWindowsCoreAudioAudioDeviceModule() can be used on Windows and that // it sets the audio layer to kWindowsCoreAudio2 implicitly. Note that, the // new ADM for Windows must be created on a COM thread. - webrtc_win::ScopedCOMInitializer com_initializer( - webrtc_win::ScopedCOMInitializer::kMTA); + ScopedCOMInitializer com_initializer(ScopedCOMInitializer::kMTA); EXPECT_TRUE(com_initializer.Succeeded()); audio_device = CreateWindowsCoreAudioAudioDeviceModule(task_queue_factory.get()); diff --git a/modules/audio_device/include/audio_device_factory.cc b/modules/audio_device/include/audio_device_factory.cc index 909506b9fc..d5b381029e 100644 --- a/modules/audio_device/include/audio_device_factory.cc +++ b/modules/audio_device/include/audio_device_factory.cc @@ -38,7 +38,7 @@ CreateWindowsCoreAudioAudioDeviceModuleForTest( bool automatic_restart) { RTC_DLOG(INFO) << __FUNCTION__; // Returns NULL if Core Audio is not supported or if COM has not been - // initialized correctly using webrtc_win::ScopedCOMInitializer. + // initialized correctly using ScopedCOMInitializer. if (!webrtc_win::core_audio_utility::IsSupported()) { RTC_LOG(LS_ERROR) << "Unable to create ADM since Core Audio is not supported"; diff --git a/modules/audio_device/include/audio_device_factory.h b/modules/audio_device/include/audio_device_factory.h index fb1ac41fad..9c19d6196d 100644 --- a/modules/audio_device/include/audio_device_factory.h +++ b/modules/audio_device/include/audio_device_factory.h @@ -30,8 +30,8 @@ namespace webrtc { // rtc::scoped_refptr CreateAudioDevice() { // task_queue_factory_ = CreateDefaultTaskQueueFactory(); // // Tell COM that this thread shall live in the MTA. -// com_initializer_ = std::make_unique( -// webrtc_win::ScopedCOMInitializer::kMTA); +// com_initializer_ = std::make_unique( +// ScopedCOMInitializer::kMTA); // if (!com_initializer_->Succeeded()) { // return nullptr; // } @@ -42,7 +42,7 @@ namespace webrtc { // } // // private: -// std::unique_ptr com_initializer_; +// std::unique_ptr com_initializer_; // std::unique_ptr task_queue_factory_; // rtc::scoped_refptr CreateWindowsCoreAudioAudioDeviceModule( diff --git a/modules/audio_device/win/audio_device_core_win.cc b/modules/audio_device/win/audio_device_core_win.cc index 771dcfa532..776a16cda4 100644 --- a/modules/audio_device/win/audio_device_core_win.cc +++ b/modules/audio_device/win/audio_device_core_win.cc @@ -38,6 +38,7 @@ #include +#include "rtc_base/checks.h" #include "rtc_base/logging.h" #include "rtc_base/platform_thread.h" #include "rtc_base/string_utils.h" @@ -226,7 +227,7 @@ bool AudioDeviceWindowsCore::CoreAudioIsSupported() { // by a corresponding call to CoUninitialize. // ScopedCOMInitializer comInit(ScopedCOMInitializer::kMTA); - if (!comInit.succeeded()) { + if (!comInit.Succeeded()) { // Things will work even if an STA thread is calling this method but we // want to ensure that MTA is used and therefore return false here. return false; @@ -395,7 +396,7 @@ AudioDeviceWindowsCore::AudioDeviceWindowsCore() _inputDeviceIndex(0), _outputDeviceIndex(0) { RTC_LOG(LS_INFO) << __FUNCTION__ << " created"; - assert(_comInit.succeeded()); + RTC_DCHECK(_comInit.Succeeded()); // Try to load the Avrt DLL if (!_avrtLibrary) { @@ -2364,7 +2365,6 @@ int32_t AudioDeviceWindowsCore::StartRecording() { // Set thread priority to highest possible SetThreadPriority(_hRecThread, THREAD_PRIORITY_TIME_CRITICAL); - } // critScoped DWORD ret = WaitForSingleObject(_hCaptureStartedEvent, 1000); @@ -2652,7 +2652,7 @@ DWORD AudioDeviceWindowsCore::DoRenderThread() { // Initialize COM as MTA in this thread. ScopedCOMInitializer comInit(ScopedCOMInitializer::kMTA); - if (!comInit.succeeded()) { + if (!comInit.Succeeded()) { RTC_LOG(LS_ERROR) << "failed to initialize COM in render thread"; return 1; } @@ -2959,7 +2959,7 @@ DWORD AudioDeviceWindowsCore::DoCaptureThreadPollDMO() { // Initialize COM as MTA in this thread. ScopedCOMInitializer comInit(ScopedCOMInitializer::kMTA); - if (!comInit.succeeded()) { + if (!comInit.Succeeded()) { RTC_LOG(LS_ERROR) << "failed to initialize COM in polling DMO thread"; return 1; } @@ -3091,7 +3091,7 @@ DWORD AudioDeviceWindowsCore::DoCaptureThread() { // Initialize COM as MTA in this thread. ScopedCOMInitializer comInit(ScopedCOMInitializer::kMTA); - if (!comInit.succeeded()) { + if (!comInit.Succeeded()) { RTC_LOG(LS_ERROR) << "failed to initialize COM in capture thread"; return 1; } @@ -3427,7 +3427,7 @@ int AudioDeviceWindowsCore::SetDMOProperties() { // MFPKEY_WMAAECMA_FEATR_AGC - Digital AGC (disabled). // MFPKEY_WMAAECMA_FEATR_CENTER_CLIP - AEC center clipping (enabled). // MFPKEY_WMAAECMA_FEATR_ECHO_LENGTH - Filter length (256 ms). - // TODO(andrew): investigate decresing the length to 128 ms. + // TODO(andrew): investigate decresing the length to 128 ms. // MFPKEY_WMAAECMA_FEATR_FRAME_SIZE - Frame size (0). // 0 is automatic; defaults to 160 samples (or 10 ms frames at the // selected 16 kHz) as long as mic array processing is disabled. diff --git a/modules/audio_device/win/audio_device_core_win.h b/modules/audio_device/win/audio_device_core_win.h index e3826f95d9..7e7ef21157 100644 --- a/modules/audio_device/win/audio_device_core_win.h +++ b/modules/audio_device/win/audio_device_core_win.h @@ -8,8 +8,8 @@ * be found in the AUTHORS file in the root of the source tree. */ -#ifndef AUDIO_DEVICE_AUDIO_DEVICE_CORE_WIN_H_ -#define AUDIO_DEVICE_AUDIO_DEVICE_CORE_WIN_H_ +#ifndef MODULES_AUDIO_DEVICE_WIN_AUDIO_DEVICE_CORE_WIN_H_ +#define MODULES_AUDIO_DEVICE_WIN_AUDIO_DEVICE_CORE_WIN_H_ #if (_MSC_VER >= 1400) // only include for VS 2005 and higher @@ -28,6 +28,7 @@ #include "api/scoped_refptr.h" #include "rtc_base/synchronization/mutex.h" +#include "rtc_base/win/scoped_com_initializer.h" // Use Multimedia Class Scheduler Service (MMCSS) to boost the thread priority #pragma comment(lib, "avrt.lib") @@ -45,37 +46,6 @@ const float MIN_CORE_MICROPHONE_VOLUME = 0.0f; const uint16_t CORE_SPEAKER_VOLUME_STEP_SIZE = 1; const uint16_t CORE_MICROPHONE_VOLUME_STEP_SIZE = 1; -// Utility class which initializes COM in the constructor (STA or MTA), -// and uninitializes COM in the destructor. -class ScopedCOMInitializer { - public: - // Enum value provided to initialize the thread as an MTA instead of STA. - enum SelectMTA { kMTA }; - - // Constructor for STA initialization. - ScopedCOMInitializer() { Initialize(COINIT_APARTMENTTHREADED); } - - // Constructor for MTA initialization. - explicit ScopedCOMInitializer(SelectMTA mta) { - Initialize(COINIT_MULTITHREADED); - } - - ~ScopedCOMInitializer() { - if (SUCCEEDED(hr_)) - CoUninitialize(); - } - - bool succeeded() const { return SUCCEEDED(hr_); } - - private: - void Initialize(COINIT init) { hr_ = CoInitializeEx(NULL, init); } - - HRESULT hr_; - - ScopedCOMInitializer(const ScopedCOMInitializer&); - void operator=(const ScopedCOMInitializer&); -}; - class AudioDeviceWindowsCore : public AudioDeviceGeneric { public: AudioDeviceWindowsCore(); @@ -327,4 +297,4 @@ class AudioDeviceWindowsCore : public AudioDeviceGeneric { } // namespace webrtc -#endif // AUDIO_DEVICE_AUDIO_DEVICE_CORE_WIN_H_ +#endif // MODULES_AUDIO_DEVICE_WIN_AUDIO_DEVICE_CORE_WIN_H_ diff --git a/modules/audio_device/win/core_audio_base_win.cc b/modules/audio_device/win/core_audio_base_win.cc index bf3bf1ab80..672e482478 100644 --- a/modules/audio_device/win/core_audio_base_win.cc +++ b/modules/audio_device/win/core_audio_base_win.cc @@ -19,6 +19,7 @@ #include "rtc_base/logging.h" #include "rtc_base/numerics/safe_conversions.h" #include "rtc_base/time_utils.h" +#include "rtc_base/win/scoped_com_initializer.h" #include "rtc_base/win/windows_version.h" using Microsoft::WRL::ComPtr; @@ -796,7 +797,7 @@ HRESULT CoreAudioBase::QueryInterface(REFIID iid, void** object) { if (iid == IID_IUnknown || iid == __uuidof(IAudioSessionEvents)) { *object = static_cast(this); return S_OK; - }; + } *object = nullptr; return E_NOINTERFACE; } diff --git a/modules/audio_device/win/core_audio_utility_win.h b/modules/audio_device/win/core_audio_utility_win.h index 265b8996d7..79203dc954 100644 --- a/modules/audio_device/win/core_audio_utility_win.h +++ b/modules/audio_device/win/core_audio_utility_win.h @@ -118,71 +118,6 @@ class ScopedMMCSSRegistration { HANDLE mmcss_handle_ = nullptr; }; -// Initializes COM in the constructor (STA or MTA), and uninitializes COM in the -// destructor. Taken from base::win::ScopedCOMInitializer. -// -// WARNING: This should only be used once per thread, ideally scoped to a -// similar lifetime as the thread itself. You should not be using this in -// random utility functions that make COM calls; instead ensure that these -// functions are running on a COM-supporting thread! -// See https://msdn.microsoft.com/en-us/library/ms809971.aspx for details. -class ScopedCOMInitializer { - public: - // Enum value provided to initialize the thread as an MTA instead of STA. - // There are two types of apartments, Single Threaded Apartments (STAs) - // and Multi Threaded Apartments (MTAs). Within a given process there can - // be multiple STA’s but there is only one MTA. STA is typically used by - // "GUI applications" and MTA by "worker threads" with no UI message loop. - enum SelectMTA { kMTA }; - - // Constructor for STA initialization. - ScopedCOMInitializer() { - RTC_DLOG(INFO) << "Single-Threaded Apartment (STA) COM thread"; - Initialize(COINIT_APARTMENTTHREADED); - } - - // Constructor for MTA initialization. - explicit ScopedCOMInitializer(SelectMTA mta) { - RTC_DLOG(INFO) << "Multi-Threaded Apartment (MTA) COM thread"; - Initialize(COINIT_MULTITHREADED); - } - - ~ScopedCOMInitializer() { - if (Succeeded()) { - CoUninitialize(); - } - } - - ScopedCOMInitializer(const ScopedCOMInitializer&) = delete; - ScopedCOMInitializer& operator=(const ScopedCOMInitializer&) = delete; - - bool Succeeded() { return SUCCEEDED(hr_); } - - private: - void Initialize(COINIT init) { - // Initializes the COM library for use by the calling thread, sets the - // thread's concurrency model, and creates a new apartment for the thread - // if one is required. CoInitializeEx must be called at least once, and is - // usually called only once, for each thread that uses the COM library. - hr_ = CoInitializeEx(NULL, init); - RTC_CHECK_NE(RPC_E_CHANGED_MODE, hr_) - << "Invalid COM thread model change (MTA->STA)"; - // Multiple calls to CoInitializeEx by the same thread are allowed as long - // as they pass the same concurrency flag, but subsequent valid calls - // return S_FALSE. To close the COM library gracefully on a thread, each - // successful call to CoInitializeEx, including any call that returns - // S_FALSE, must be balanced by a corresponding call to CoUninitialize. - if (hr_ == S_OK) { - RTC_DLOG(INFO) - << "The COM library was initialized successfully on this thread"; - } else if (hr_ == S_FALSE) { - RTC_DLOG(WARNING) - << "The COM library is already initialized on this thread"; - } - } - HRESULT hr_; -}; - // A PROPVARIANT that is automatically initialized and cleared upon respective // construction and destruction of this class. class ScopedPropVariant { @@ -323,7 +258,7 @@ class ScopedHandle { // Always ensure that Core Audio is supported before using these methods. // Use webrtc_win::core_audio_utility::IsSupported() for this purpose. // Also, all methods must be called on a valid COM thread. This can be done -// by using the webrtc_win::ScopedCOMInitializer helper class. +// by using the ScopedCOMInitializer helper class. // These methods are based on media::CoreAudioUtil in Chrome. namespace core_audio_utility { diff --git a/modules/audio_device/win/core_audio_utility_win_unittest.cc b/modules/audio_device/win/core_audio_utility_win_unittest.cc index 67040464b9..9f1ce5e75e 100644 --- a/modules/audio_device/win/core_audio_utility_win_unittest.cc +++ b/modules/audio_device/win/core_audio_utility_win_unittest.cc @@ -11,6 +11,7 @@ #include "modules/audio_device/win/core_audio_utility_win.h" #include "rtc_base/arraysize.h" #include "rtc_base/logging.h" +#include "rtc_base/win/scoped_com_initializer.h" #include "rtc_base/win/windows_version.h" #include "test/gtest.h" @@ -54,8 +55,7 @@ bool ShouldAbortTest(bool requirements_satisfied, // CoreAudioUtilityWinTest test fixture. class CoreAudioUtilityWinTest : public ::testing::Test { protected: - CoreAudioUtilityWinTest() - : com_init_(webrtc_win::ScopedCOMInitializer::kMTA) { + CoreAudioUtilityWinTest() : com_init_(ScopedCOMInitializer::kMTA) { // We must initialize the COM library on a thread before we calling any of // the library functions. All COM functions will return CO_E_NOTINITIALIZED // otherwise. diff --git a/rtc_base/BUILD.gn b/rtc_base/BUILD.gn index 66da6e03aa..a4d209eef0 100644 --- a/rtc_base/BUILD.gn +++ b/rtc_base/BUILD.gn @@ -124,6 +124,12 @@ rtc_library("rtc_base_approved") { if (is_win) { sources += [ + "win/get_activation_factory.cc", + "win/get_activation_factory.h", + "win/hstring.cc", + "win/hstring.h", + "win/scoped_com_initializer.cc", + "win/scoped_com_initializer.h", "win/windows_version.cc", "win/windows_version.h", ] diff --git a/rtc_base/win/get_activation_factory.cc b/rtc_base/win/get_activation_factory.cc new file mode 100644 index 0000000000..b3be9abfa7 --- /dev/null +++ b/rtc_base/win/get_activation_factory.cc @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2020 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "rtc_base/win/get_activation_factory.h" + +#include +#include + +namespace { + +FARPROC LoadComBaseFunction(const char* function_name) { + static HMODULE const handle = + ::LoadLibraryExW(L"combase.dll", nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32); + return handle ? ::GetProcAddress(handle, function_name) : nullptr; +} + +decltype(&::RoGetActivationFactory) GetRoGetActivationFactoryFunction() { + static decltype(&::RoGetActivationFactory) const function = + reinterpret_cast( + LoadComBaseFunction("RoGetActivationFactory")); + return function; +} + +} // namespace + +namespace webrtc { + +bool ResolveCoreWinRTDelayload() { + return GetRoGetActivationFactoryFunction() && + ResolveCoreWinRTStringDelayload(); +} + +HRESULT RoGetActivationFactoryProxy(HSTRING class_id, + const IID& iid, + void** out_factory) { + auto get_factory_func = GetRoGetActivationFactoryFunction(); + if (!get_factory_func) + return E_FAIL; + return get_factory_func(class_id, iid, out_factory); +} + +} // namespace webrtc diff --git a/rtc_base/win/get_activation_factory.h b/rtc_base/win/get_activation_factory.h new file mode 100644 index 0000000000..801f39d313 --- /dev/null +++ b/rtc_base/win/get_activation_factory.h @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2020 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef RTC_BASE_WIN_GET_ACTIVATION_FACTORY_H_ +#define RTC_BASE_WIN_GET_ACTIVATION_FACTORY_H_ + +#include + +#include "rtc_base/win/hstring.h" + +namespace webrtc { + +// Provides access to Core WinRT functions which may not be available on +// Windows 7. Loads functions dynamically at runtime to prevent library +// dependencies. + +// Callers must check the return value of ResolveCoreWinRTDelayLoad() before +// using these functions. + +bool ResolveCoreWinRTDelayload(); + +HRESULT RoGetActivationFactoryProxy(HSTRING class_id, + const IID& iid, + void** out_factory); + +// Retrieves an activation factory for the type specified. +template +HRESULT GetActivationFactory(InterfaceType** factory) { + HSTRING class_id_hstring; + HRESULT hr = CreateHstring(runtime_class_id, wcslen(runtime_class_id), + &class_id_hstring); + if (FAILED(hr)) + return hr; + + hr = RoGetActivationFactoryProxy(class_id_hstring, IID_PPV_ARGS(factory)); + if (FAILED(hr)) + return hr; + + return DeleteHstring(class_id_hstring); +} + +} // namespace webrtc + +#endif // RTC_BASE_WIN_GET_ACTIVATION_FACTORY_H_ diff --git a/rtc_base/win/hstring.cc b/rtc_base/win/hstring.cc new file mode 100644 index 0000000000..5a362a97c9 --- /dev/null +++ b/rtc_base/win/hstring.cc @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2020 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "rtc_base/win/hstring.h" + +#include +#include + +namespace { + +FARPROC LoadComBaseFunction(const char* function_name) { + static HMODULE const handle = + ::LoadLibraryExW(L"combase.dll", nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32); + return handle ? ::GetProcAddress(handle, function_name) : nullptr; +} + +decltype(&::WindowsCreateString) GetWindowsCreateString() { + static decltype(&::WindowsCreateString) const function = + reinterpret_cast( + LoadComBaseFunction("WindowsCreateString")); + return function; +} + +decltype(&::WindowsDeleteString) GetWindowsDeleteString() { + static decltype(&::WindowsDeleteString) const function = + reinterpret_cast( + LoadComBaseFunction("WindowsDeleteString")); + return function; +} + +} // namespace + +namespace webrtc { + +bool ResolveCoreWinRTStringDelayload() { + return GetWindowsDeleteString() && GetWindowsCreateString(); +} + +HRESULT CreateHstring(const wchar_t* src, uint32_t len, HSTRING* out_hstr) { + decltype(&::WindowsCreateString) create_string_func = + GetWindowsCreateString(); + if (!create_string_func) + return E_FAIL; + return create_string_func(src, len, out_hstr); +} + +HRESULT DeleteHstring(HSTRING hstr) { + decltype(&::WindowsDeleteString) delete_string_func = + GetWindowsDeleteString(); + if (!delete_string_func) + return E_FAIL; + return delete_string_func(hstr); +} + +} // namespace webrtc diff --git a/rtc_base/win/hstring.h b/rtc_base/win/hstring.h new file mode 100644 index 0000000000..8fb119a9e6 --- /dev/null +++ b/rtc_base/win/hstring.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2020 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef RTC_BASE_WIN_HSTRING_H_ +#define RTC_BASE_WIN_HSTRING_H_ + +#include +#include +#include + +namespace webrtc { + +// Callers must check the return value of ResolveCoreWinRTStringDelayLoad() +// before using these functions. +bool ResolveCoreWinRTStringDelayload(); + +HRESULT CreateHstring(const wchar_t* src, uint32_t len, HSTRING* out_hstr); + +HRESULT DeleteHstring(HSTRING hstr); + +} // namespace webrtc + +#endif // RTC_BASE_WIN_HSTRING_H_ diff --git a/rtc_base/win/scoped_com_initializer.cc b/rtc_base/win/scoped_com_initializer.cc new file mode 100644 index 0000000000..b83ad32a67 --- /dev/null +++ b/rtc_base/win/scoped_com_initializer.cc @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2020 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "rtc_base/win/scoped_com_initializer.h" + +namespace webrtc { + +ScopedCOMInitializer::ScopedCOMInitializer() { + RTC_DLOG(INFO) << "Single-Threaded Apartment (STA) COM thread"; + Initialize(COINIT_APARTMENTTHREADED); +} + +// Constructor for MTA initialization. +ScopedCOMInitializer::ScopedCOMInitializer(SelectMTA mta) { + RTC_DLOG(INFO) << "Multi-Threaded Apartment (MTA) COM thread"; + Initialize(COINIT_MULTITHREADED); +} + +ScopedCOMInitializer::~ScopedCOMInitializer() { + if (Succeeded()) { + CoUninitialize(); + } +} + +void ScopedCOMInitializer::Initialize(COINIT init) { + // Initializes the COM library for use by the calling thread, sets the + // thread's concurrency model, and creates a new apartment for the thread + // if one is required. CoInitializeEx must be called at least once, and is + // usually called only once, for each thread that uses the COM library. + hr_ = CoInitializeEx(NULL, init); + RTC_CHECK_NE(RPC_E_CHANGED_MODE, hr_) + << "Invalid COM thread model change (MTA->STA)"; + // Multiple calls to CoInitializeEx by the same thread are allowed as long + // as they pass the same concurrency flag, but subsequent valid calls + // return S_FALSE. To close the COM library gracefully on a thread, each + // successful call to CoInitializeEx, including any call that returns + // S_FALSE, must be balanced by a corresponding call to CoUninitialize. + if (hr_ == S_OK) { + RTC_DLOG(INFO) + << "The COM library was initialized successfully on this thread"; + } else if (hr_ == S_FALSE) { + RTC_DLOG(WARNING) + << "The COM library is already initialized on this thread"; + } +} + +} // namespace webrtc diff --git a/rtc_base/win/scoped_com_initializer.h b/rtc_base/win/scoped_com_initializer.h new file mode 100644 index 0000000000..918812fc72 --- /dev/null +++ b/rtc_base/win/scoped_com_initializer.h @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2020 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef RTC_BASE_WIN_SCOPED_COM_INITIALIZER_H_ +#define RTC_BASE_WIN_SCOPED_COM_INITIALIZER_H_ + +#include + +#include "rtc_base/logging.h" + +namespace webrtc { + +// Initializes COM in the constructor (STA or MTA), and uninitializes COM in the +// destructor. Taken from base::win::ScopedCOMInitializer. +// +// WARNING: This should only be used once per thread, ideally scoped to a +// similar lifetime as the thread itself. You should not be using this in +// random utility functions that make COM calls; instead ensure that these +// functions are running on a COM-supporting thread! +// See https://msdn.microsoft.com/en-us/library/ms809971.aspx for details. +class ScopedCOMInitializer { + public: + // Enum value provided to initialize the thread as an MTA instead of STA. + // There are two types of apartments, Single Threaded Apartments (STAs) + // and Multi Threaded Apartments (MTAs). Within a given process there can + // be multiple STA’s but there is only one MTA. STA is typically used by + // "GUI applications" and MTA by "worker threads" with no UI message loop. + enum SelectMTA { kMTA }; + + // Constructor for STA initialization. + ScopedCOMInitializer(); + + // Constructor for MTA initialization. + explicit ScopedCOMInitializer(SelectMTA mta); + + ~ScopedCOMInitializer(); + + ScopedCOMInitializer(const ScopedCOMInitializer&) = delete; + ScopedCOMInitializer& operator=(const ScopedCOMInitializer&) = delete; + + bool Succeeded() { return SUCCEEDED(hr_); } + + private: + void Initialize(COINIT init); + + HRESULT hr_; +}; + +} // namespace webrtc + +#endif // RTC_BASE_WIN_SCOPED_COM_INITIALIZER_H_ diff --git a/video/video_quality_test.cc b/video/video_quality_test.cc index c0393875e4..a58aa1f33f 100644 --- a/video/video_quality_test.cc +++ b/video/video_quality_test.cc @@ -1348,8 +1348,8 @@ rtc::scoped_refptr VideoQualityTest::CreateAudioDevice() { // CO_E_NOTINITIALIZED otherwise. The legacy ADM for Windows used internal // COM initialization but the new ADM requires COM to be initialized // externally. - com_initializer_ = std::make_unique( - webrtc_win::ScopedCOMInitializer::kMTA); + com_initializer_ = + std::make_unique(ScopedCOMInitializer::kMTA); RTC_CHECK(com_initializer_->Succeeded()); RTC_CHECK(webrtc_win::core_audio_utility::IsSupported()); RTC_CHECK(webrtc_win::core_audio_utility::IsMMCSSSupported()); diff --git a/video/video_quality_test.h b/video/video_quality_test.h index 2177830794..f49ce385b6 100644 --- a/video/video_quality_test.h +++ b/video/video_quality_test.h @@ -30,6 +30,7 @@ #include "video/video_analyzer.h" #ifdef WEBRTC_WIN #include "modules/audio_device/win/core_audio_utility_win.h" +#include "rtc_base/win/scoped_com_initializer.h" #endif namespace webrtc { @@ -137,7 +138,7 @@ class VideoQualityTest : public test::CallTest, #ifdef WEBRTC_WIN // Windows Core Audio based ADM needs to run on a COM initialized thread. // Only referenced in combination with --audio --use_real_adm flags. - std::unique_ptr com_initializer_; + std::unique_ptr com_initializer_; #endif };