Skip to content
This repository was archived by the owner on Oct 25, 2024. It is now read-only.

Commit 918e3b9

Browse files
authored
Enable automatic audio playback device switching (#93)
1 parent 2700fba commit 918e3b9

File tree

5 files changed

+127
-6
lines changed

5 files changed

+127
-6
lines changed

modules/audio_device/win/audio_device_core_win.cc

Lines changed: 48 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -168,10 +168,40 @@ class MediaBufferImpl final : public IMediaBuffer {
168168
// Static Methods
169169
// ============================================================================
170170

171+
HRESULT __stdcall AudioDeviceWindowsCore::OnDefaultDeviceChanged(
172+
EDataFlow flow,
173+
ERole role,
174+
LPCWSTR pwstrDefaultDeviceId) {
175+
if (flow != eRender || role != eCommunications)
176+
SetEvent(_hDeviceRestartEvent);
177+
return S_OK;
178+
}
179+
180+
ULONG AudioDeviceWindowsCore::AddRef() {
181+
ULONG new_ref = InterlockedIncrement(&ref_count_);
182+
return new_ref;
183+
}
184+
185+
ULONG AudioDeviceWindowsCore::Release() {
186+
ULONG new_ref = InterlockedDecrement(&ref_count_);
187+
return new_ref;
188+
}
189+
190+
HRESULT AudioDeviceWindowsCore::QueryInterface(REFIID iid, void** object) {
191+
if (object == nullptr) {
192+
return E_POINTER;
193+
}
194+
if (iid == IID_IUnknown || iid == __uuidof(IMMNotificationClient)) {
195+
*object = static_cast<IMMNotificationClient*>(this);
196+
return S_OK;
197+
};
198+
*object = nullptr;
199+
return E_NOINTERFACE;
200+
}
201+
171202
// ----------------------------------------------------------------------------
172203
// CoreAudioIsSupported
173204
// ----------------------------------------------------------------------------
174-
175205
bool AudioDeviceWindowsCore::CoreAudioIsSupported() {
176206
RTC_LOG(LS_VERBOSE) << __FUNCTION__;
177207

@@ -369,6 +399,7 @@ AudioDeviceWindowsCore::AudioDeviceWindowsCore()
369399
_hRecThread(NULL),
370400
_hCaptureStartedEvent(NULL),
371401
_hShutdownCaptureEvent(NULL),
402+
_hDeviceRestartEvent(NULL),
372403
_hMmTask(NULL),
373404
_playAudioFrameSize(0),
374405
_playSampleRate(0),
@@ -444,6 +475,7 @@ AudioDeviceWindowsCore::AudioDeviceWindowsCore()
444475
_hShutdownCaptureEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
445476
_hRenderStartedEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
446477
_hCaptureStartedEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
478+
_hDeviceRestartEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
447479

448480
_perfCounterFreq.QuadPart = 1;
449481
_perfCounterFactor = 0.0;
@@ -470,6 +502,7 @@ AudioDeviceWindowsCore::AudioDeviceWindowsCore()
470502
reinterpret_cast<void**>(&_ptrEnumerator));
471503
assert(NULL != _ptrEnumerator);
472504

505+
_ptrEnumerator->RegisterEndpointNotificationCallback(this);
473506
// DMO initialization for built-in WASAPI AEC.
474507
{
475508
IMediaObject* ptrDMO = NULL;
@@ -497,6 +530,9 @@ AudioDeviceWindowsCore::~AudioDeviceWindowsCore() {
497530

498531
// The IMMDeviceEnumerator is created during construction. Must release
499532
// it here and not in Terminate() since we don't recreate it in Init().
533+
if (_ptrEnumerator) {
534+
_ptrEnumerator->UnregisterEndpointNotificationCallback(this);
535+
}
500536
SAFE_RELEASE(_ptrEnumerator);
501537

502538
_ptrAudioBuffer = NULL;
@@ -531,6 +567,11 @@ AudioDeviceWindowsCore::~AudioDeviceWindowsCore() {
531567
_hShutdownCaptureEvent = NULL;
532568
}
533569

570+
if (NULL != _hDeviceRestartEvent) {
571+
CloseHandle(_hDeviceRestartEvent);
572+
_hDeviceRestartEvent = NULL;
573+
}
574+
534575
if (_avrtLibrary) {
535576
BOOL freeOK = FreeLibrary(_avrtLibrary);
536577
if (!freeOK) {
@@ -2635,7 +2676,7 @@ DWORD WINAPI AudioDeviceWindowsCore::WSAPICaptureThreadPollDMO(LPVOID context) {
26352676

26362677
DWORD AudioDeviceWindowsCore::DoRenderThread() {
26372678
bool keepPlaying = true;
2638-
HANDLE waitArray[2] = {_hShutdownRenderEvent, _hRenderSamplesReadyEvent};
2679+
HANDLE waitArray[3] = {_hShutdownRenderEvent, _hRenderSamplesReadyEvent, _hDeviceRestartEvent};
26392680
HRESULT hr = S_OK;
26402681
HANDLE hMmTask = NULL;
26412682

@@ -2752,13 +2793,17 @@ DWORD AudioDeviceWindowsCore::DoRenderThread() {
27522793

27532794
while (keepPlaying) {
27542795
// Wait for a render notification event or a shutdown event
2755-
DWORD waitResult = WaitForMultipleObjects(2, waitArray, FALSE, 500);
2796+
DWORD waitResult = WaitForMultipleObjects(3, waitArray, FALSE, 500);
27562797
switch (waitResult) {
27572798
case WAIT_OBJECT_0 + 0: // _hShutdownRenderEvent
27582799
keepPlaying = false;
27592800
break;
27602801
case WAIT_OBJECT_0 + 1: // _hRenderSamplesReadyEvent
27612802
break;
2803+
case WAIT_OBJECT_0 + 2: // _hDeviceRestartEvent
2804+
// TODO: if we're going to switch back to this platform specific impl,
2805+
// We should restart the playout session here.
2806+
break;
27622807
case WAIT_TIMEOUT: // timeout notification
27632808
RTC_LOG(LS_WARNING) << "render event timed out after 0.5 seconds";
27642809
goto Exit;

modules/audio_device/win/audio_device_core_win.h

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,13 +76,19 @@ class ScopedCOMInitializer {
7676
void operator=(const ScopedCOMInitializer&);
7777
};
7878

79-
class AudioDeviceWindowsCore : public AudioDeviceGeneric {
79+
class AudioDeviceWindowsCore : public AudioDeviceGeneric,
80+
public IMMNotificationClient {
8081
public:
8182
AudioDeviceWindowsCore();
8283
~AudioDeviceWindowsCore();
8384

8485
static bool CoreAudioIsSupported();
8586

87+
// IUnknown (required by IMMNotificationClient).
88+
ULONG __stdcall AddRef() override;
89+
ULONG __stdcall Release() override;
90+
HRESULT __stdcall QueryInterface(REFIID iid, void** object) override;
91+
8692
// Retrieve the currently utilized audio layer
8793
virtual int32_t ActiveAudioLayer(
8894
AudioDeviceModule::AudioLayer& audioLayer) const;
@@ -262,6 +268,7 @@ class AudioDeviceWindowsCore : public AudioDeviceGeneric {
262268
HANDLE _hPlayThread;
263269
HANDLE _hRenderStartedEvent;
264270
HANDLE _hShutdownRenderEvent;
271+
HANDLE _hDeviceRestartEvent;
265272

266273
HANDLE _hCaptureSamplesReadyEvent;
267274
HANDLE _hRecThread;
@@ -292,6 +299,30 @@ class AudioDeviceWindowsCore : public AudioDeviceGeneric {
292299
double _perfCounterFactor;
293300

294301
private:
302+
// IMMNotificationClient implementation. At present we
303+
// only handle OnDefaultDeviceChanged event.
304+
HRESULT __stdcall OnDeviceStateChanged(LPCWSTR pwstrDeviceId,
305+
DWORD dwNewState) override {
306+
return S_OK;
307+
}
308+
309+
HRESULT __stdcall OnDeviceAdded(LPCWSTR pwstrDeviceId) override {
310+
return S_OK;
311+
}
312+
313+
HRESULT __stdcall OnDeviceRemoved(LPCWSTR pwstrDeviceId) override {
314+
return S_OK;
315+
}
316+
317+
HRESULT __stdcall OnDefaultDeviceChanged(
318+
EDataFlow flow,
319+
ERole role,
320+
LPCWSTR pwstrDefaultDeviceId) override;
321+
322+
HRESULT __stdcall OnPropertyValueChanged(LPCWSTR pwstrDeviceId,
323+
const PROPERTYKEY key) override {
324+
return S_OK;
325+
}
295326
bool _initialized;
296327
bool _recording;
297328
bool _playing;
@@ -306,6 +337,7 @@ class AudioDeviceWindowsCore : public AudioDeviceGeneric {
306337
AudioDeviceModule::WindowsDeviceType _outputDevice;
307338
uint16_t _inputDeviceIndex;
308339
uint16_t _outputDeviceIndex;
340+
LONG ref_count_ = 1;
309341
};
310342

311343
#endif // #if (_MSC_VER >= 1400)

modules/audio_device/win/core_audio_base_win.cc

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,9 +181,15 @@ CoreAudioBase::CoreAudioBase(Direction direction,
181181
// invalidated or the stream format has changed.
182182
restart_event_.Set(CreateEvent(nullptr, false, false, nullptr));
183183
RTC_DCHECK(restart_event_.IsValid());
184+
185+
enumerator_ = core_audio_utility::CreateDeviceEnumerator();
186+
enumerator_->RegisterEndpointNotificationCallback(this);
187+
RTC_LOG(INFO) << __FUNCTION__
188+
<< ":Registered endpoint notification callback.";
184189
}
185190

186191
CoreAudioBase::~CoreAudioBase() {
192+
enumerator_->UnregisterEndpointNotificationCallback(this);
187193
RTC_DLOG(INFO) << __FUNCTION__;
188194
RTC_DCHECK_EQ(ref_count_, 1);
189195
}
@@ -875,6 +881,18 @@ HRESULT CoreAudioBase::OnGroupingParamChanged(LPCGUID new_grouping_param,
875881
return S_OK;
876882
}
877883

884+
// IMMNotificationClient::OnDefaultDeviceChanged
885+
HRESULT __stdcall CoreAudioBase::OnDefaultDeviceChanged(EDataFlow flow,
886+
ERole role,
887+
LPCWSTR pwstrDefaultDeviceId) {
888+
// We only handle the output device.
889+
RTC_LOG(LS_ERROR) << "OnDefaultDeviceChanged invoked.";
890+
if (flow != eRender || role != eCommunications)
891+
return S_OK;
892+
Restart();
893+
return S_OK;
894+
}
895+
878896
void CoreAudioBase::ThreadRun() {
879897
if (!core_audio_utility::IsMMCSSSupported()) {
880898
RTC_LOG(LS_ERROR) << "MMCSS is not supported";

modules/audio_device/win/core_audio_base_win.h

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ namespace webrtc_win {
3939
// a separate thread owned and controlled by the manager.
4040
// TODO(henrika): investigate if CoreAudioBase should implement
4141
// IMMNotificationClient as well (might improve support for device changes).
42-
class CoreAudioBase : public IAudioSessionEvents {
42+
class CoreAudioBase : public IAudioSessionEvents, public IMMNotificationClient {
4343
public:
4444
enum class Direction {
4545
kInput,
@@ -160,6 +160,7 @@ class CoreAudioBase : public IAudioSessionEvents {
160160
std::atomic<bool> is_restarting_;
161161
std::unique_ptr<rtc::PlatformThread> audio_thread_;
162162
Microsoft::WRL::ComPtr<IAudioSessionControl> audio_session_control_;
163+
Microsoft::WRL::ComPtr<IMMDeviceEnumerator> enumerator_;
163164

164165
void StopThread();
165166
AudioSessionState GetAudioSessionState() const;
@@ -194,6 +195,31 @@ class CoreAudioBase : public IAudioSessionEvents {
194195
LPCGUID event_context) override;
195196
HRESULT __stdcall OnGroupingParamChanged(LPCGUID new_grouping_param,
196197
LPCGUID event_context) override;
198+
199+
// IMMNotificationClient implementation. At present we
200+
// only handle OnDefaultDeviceChanged event.
201+
HRESULT __stdcall OnDeviceStateChanged(LPCWSTR pwstrDeviceId,
202+
DWORD dwNewState) override {
203+
return S_OK;
204+
}
205+
206+
HRESULT __stdcall OnDeviceAdded(LPCWSTR pwstrDeviceId) override {
207+
return S_OK;
208+
}
209+
210+
HRESULT __stdcall OnDeviceRemoved(LPCWSTR pwstrDeviceId) override {
211+
return S_OK;
212+
}
213+
214+
HRESULT __stdcall OnDefaultDeviceChanged(
215+
EDataFlow flow,
216+
ERole role,
217+
LPCWSTR pwstrDefaultDeviceId) override;
218+
219+
HRESULT __stdcall OnPropertyValueChanged(LPCWSTR pwstrDeviceId,
220+
const PROPERTYKEY key) override {
221+
return S_OK;
222+
}
197223
};
198224

199225
} // namespace webrtc_win

modules/audio_device/win/core_audio_output_win.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -410,10 +410,10 @@ bool CoreAudioOutput::HandleStreamDisconnected() {
410410
if (InitPlayout() != 0) {
411411
return false;
412412
}
413+
413414
if (StartPlayout() != 0) {
414415
return false;
415416
}
416-
417417
RTC_DLOG(INFO) << __FUNCTION__ << " --->>>";
418418
return true;
419419
}

0 commit comments

Comments
 (0)