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

Commit af17b52

Browse files
committed
Enable automatic audio playback device switching (#93)
1 parent c03437d commit af17b52

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
@@ -169,10 +169,40 @@ class MediaBufferImpl final : public IMediaBuffer {
169169
// Static Methods
170170
// ============================================================================
171171

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

@@ -370,6 +400,7 @@ AudioDeviceWindowsCore::AudioDeviceWindowsCore()
370400
_hRecThread(NULL),
371401
_hCaptureStartedEvent(NULL),
372402
_hShutdownCaptureEvent(NULL),
403+
_hDeviceRestartEvent(NULL),
373404
_hMmTask(NULL),
374405
_playAudioFrameSize(0),
375406
_playSampleRate(0),
@@ -445,6 +476,7 @@ AudioDeviceWindowsCore::AudioDeviceWindowsCore()
445476
_hShutdownCaptureEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
446477
_hRenderStartedEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
447478
_hCaptureStartedEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
479+
_hDeviceRestartEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
448480

449481
_perfCounterFreq.QuadPart = 1;
450482
_perfCounterFactor = 0.0;
@@ -471,6 +503,7 @@ AudioDeviceWindowsCore::AudioDeviceWindowsCore()
471503
reinterpret_cast<void**>(&_ptrEnumerator));
472504
assert(NULL != _ptrEnumerator);
473505

506+
_ptrEnumerator->RegisterEndpointNotificationCallback(this);
474507
// DMO initialization for built-in WASAPI AEC.
475508
{
476509
IMediaObject* ptrDMO = NULL;
@@ -498,6 +531,9 @@ AudioDeviceWindowsCore::~AudioDeviceWindowsCore() {
498531

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

503539
_ptrAudioBuffer = NULL;
@@ -532,6 +568,11 @@ AudioDeviceWindowsCore::~AudioDeviceWindowsCore() {
532568
_hShutdownCaptureEvent = NULL;
533569
}
534570

571+
if (NULL != _hDeviceRestartEvent) {
572+
CloseHandle(_hDeviceRestartEvent);
573+
_hDeviceRestartEvent = NULL;
574+
}
575+
535576
if (_avrtLibrary) {
536577
BOOL freeOK = FreeLibrary(_avrtLibrary);
537578
if (!freeOK) {
@@ -2646,7 +2687,7 @@ DWORD WINAPI AudioDeviceWindowsCore::WSAPICaptureThreadPollDMO(LPVOID context) {
26462687

26472688
DWORD AudioDeviceWindowsCore::DoRenderThread() {
26482689
bool keepPlaying = true;
2649-
HANDLE waitArray[2] = {_hShutdownRenderEvent, _hRenderSamplesReadyEvent};
2690+
HANDLE waitArray[3] = {_hShutdownRenderEvent, _hRenderSamplesReadyEvent, _hDeviceRestartEvent};
26502691
HRESULT hr = S_OK;
26512692
HANDLE hMmTask = NULL;
26522693

@@ -2763,13 +2804,17 @@ DWORD AudioDeviceWindowsCore::DoRenderThread() {
27632804

27642805
while (keepPlaying) {
27652806
// Wait for a render notification event or a shutdown event
2766-
DWORD waitResult = WaitForMultipleObjects(2, waitArray, FALSE, 500);
2807+
DWORD waitResult = WaitForMultipleObjects(3, waitArray, FALSE, 500);
27672808
switch (waitResult) {
27682809
case WAIT_OBJECT_0 + 0: // _hShutdownRenderEvent
27692810
keepPlaying = false;
27702811
break;
27712812
case WAIT_OBJECT_0 + 1: // _hRenderSamplesReadyEvent
27722813
break;
2814+
case WAIT_OBJECT_0 + 2: // _hDeviceRestartEvent
2815+
// TODO: if we're going to switch back to this platform specific impl,
2816+
// We should restart the playout session here.
2817+
break;
27732818
case WAIT_TIMEOUT: // timeout notification
27742819
RTC_LOG(LS_WARNING) << "render event timed out after 0.5 seconds";
27752820
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
@@ -46,13 +46,19 @@ const float MIN_CORE_MICROPHONE_VOLUME = 0.0f;
4646
const uint16_t CORE_SPEAKER_VOLUME_STEP_SIZE = 1;
4747
const uint16_t CORE_MICROPHONE_VOLUME_STEP_SIZE = 1;
4848

49-
class AudioDeviceWindowsCore : public AudioDeviceGeneric {
49+
class AudioDeviceWindowsCore : public AudioDeviceGeneric,
50+
public IMMNotificationClient {
5051
public:
5152
AudioDeviceWindowsCore();
5253
~AudioDeviceWindowsCore();
5354

5455
static bool CoreAudioIsSupported();
5556

57+
// IUnknown (required by IMMNotificationClient).
58+
ULONG __stdcall AddRef() override;
59+
ULONG __stdcall Release() override;
60+
HRESULT __stdcall QueryInterface(REFIID iid, void** object) override;
61+
5662
// Retrieve the currently utilized audio layer
5763
virtual int32_t ActiveAudioLayer(
5864
AudioDeviceModule::AudioLayer& audioLayer) const;
@@ -247,6 +253,7 @@ class AudioDeviceWindowsCore : public AudioDeviceGeneric {
247253
HANDLE _hPlayThread;
248254
HANDLE _hRenderStartedEvent;
249255
HANDLE _hShutdownRenderEvent;
256+
HANDLE _hDeviceRestartEvent;
250257

251258
HANDLE _hCaptureSamplesReadyEvent;
252259
HANDLE _hRecThread;
@@ -277,6 +284,30 @@ class AudioDeviceWindowsCore : public AudioDeviceGeneric {
277284
double _perfCounterFactor;
278285

279286
private:
287+
// IMMNotificationClient implementation. At present we
288+
// only handle OnDefaultDeviceChanged event.
289+
HRESULT __stdcall OnDeviceStateChanged(LPCWSTR pwstrDeviceId,
290+
DWORD dwNewState) override {
291+
return S_OK;
292+
}
293+
294+
HRESULT __stdcall OnDeviceAdded(LPCWSTR pwstrDeviceId) override {
295+
return S_OK;
296+
}
297+
298+
HRESULT __stdcall OnDeviceRemoved(LPCWSTR pwstrDeviceId) override {
299+
return S_OK;
300+
}
301+
302+
HRESULT __stdcall OnDefaultDeviceChanged(
303+
EDataFlow flow,
304+
ERole role,
305+
LPCWSTR pwstrDefaultDeviceId) override;
306+
307+
HRESULT __stdcall OnPropertyValueChanged(LPCWSTR pwstrDeviceId,
308+
const PROPERTYKEY key) override {
309+
return S_OK;
310+
}
280311
bool _initialized;
281312
bool _recording;
282313
bool _playing;
@@ -291,6 +322,7 @@ class AudioDeviceWindowsCore : public AudioDeviceGeneric {
291322
AudioDeviceModule::WindowsDeviceType _outputDevice;
292323
uint16_t _inputDeviceIndex;
293324
uint16_t _outputDeviceIndex;
325+
LONG ref_count_ = 1;
294326
};
295327

296328
#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
@@ -182,9 +182,15 @@ CoreAudioBase::CoreAudioBase(Direction direction,
182182
// invalidated or the stream format has changed.
183183
restart_event_.Set(CreateEvent(nullptr, false, false, nullptr));
184184
RTC_DCHECK(restart_event_.IsValid());
185+
186+
enumerator_ = core_audio_utility::CreateDeviceEnumerator();
187+
enumerator_->RegisterEndpointNotificationCallback(this);
188+
RTC_LOG(INFO) << __FUNCTION__
189+
<< ":Registered endpoint notification callback.";
185190
}
186191

187192
CoreAudioBase::~CoreAudioBase() {
193+
enumerator_->UnregisterEndpointNotificationCallback(this);
188194
RTC_DLOG(INFO) << __FUNCTION__;
189195
RTC_DCHECK_EQ(ref_count_, 1);
190196
}
@@ -876,6 +882,18 @@ HRESULT CoreAudioBase::OnGroupingParamChanged(LPCGUID new_grouping_param,
876882
return S_OK;
877883
}
878884

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