1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim:set ts=2 sw=2 sts=2 et cindent: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "AudioNotificationSender.h"
8 #include "mozilla/ClearOnShutdown.h" // for ClearOnShutdown
9 #include "mozilla/dom/ContentParent.h" // for ContentParent
10 #include "mozilla/Logging.h" // for LazyLogModule
11 #include "mozilla/RefPtr.h" // for RefPtr
12 #include "mozilla/StaticPtr.h" // for StaticAutoPtr
13 #include "nsAppRunner.h" // for XRE_IsParentProcess
14 #include "nsTArray.h" // for nsTArray
15 #include <mmdeviceapi.h> // for IMMNotificationClient interface
17 static mozilla::LazyLogModule
sLogger("AudioNotificationSender");
20 #define ANS_LOG(...) MOZ_LOG(sLogger, mozilla::LogLevel::Debug, (__VA_ARGS__))
22 #define ANS_LOGW(...) \
23 MOZ_LOG(sLogger, mozilla::LogLevel::Warning, (__VA_ARGS__))
29 * A runnable task to notify the audio device-changed event.
31 class AudioDeviceChangedRunnable final
: public Runnable
{
33 explicit AudioDeviceChangedRunnable()
34 : Runnable("AudioDeviceChangedRunnable") {}
36 NS_IMETHOD
Run() override
{
37 MOZ_ASSERT(NS_IsMainThread());
39 nsTArray
<dom::ContentParent
*> parents
;
40 dom::ContentParent::GetAll(parents
);
41 for (dom::ContentParent
* p
: parents
) {
42 Unused
<< p
->SendAudioDefaultDeviceChange();
46 }; // class AudioDeviceChangedRunnable
49 * An observer for receiving audio device events from Windows.
51 typedef void (*DefaultDeviceChangedCallback
)();
52 class AudioNotification final
: public IMMNotificationClient
{
54 explicit AudioNotification(DefaultDeviceChangedCallback aCallback
)
55 : mCallback(aCallback
), mRefCt(0), mIsRegistered(false) {
56 MOZ_COUNT_CTOR(AudioNotification
);
57 MOZ_ASSERT(mCallback
);
58 const CLSID CLSID_MMDeviceEnumerator
= __uuidof(MMDeviceEnumerator
);
59 const IID IID_IMMDeviceEnumerator
= __uuidof(IMMDeviceEnumerator
);
60 HRESULT hr
= CoCreateInstance(CLSID_MMDeviceEnumerator
, nullptr,
61 CLSCTX_INPROC_SERVER
, IID_IMMDeviceEnumerator
,
62 getter_AddRefs(mDeviceEnumerator
));
65 ANS_LOGW("Cannot create an IMMDeviceEnumerator instance.");
69 hr
= mDeviceEnumerator
->RegisterEndpointNotificationCallback(this);
71 ANS_LOGW("Cannot register notification callback.");
75 ANS_LOG("Register notification callback successfully.");
79 ~AudioNotification() {
80 MOZ_COUNT_DTOR(AudioNotification
);
81 // Assert mIsRegistered is true when we have mDeviceEnumerator.
82 // Don't care mIsRegistered if there is no mDeviceEnumerator.
83 MOZ_ASSERT(!mDeviceEnumerator
|| mIsRegistered
);
84 if (!mDeviceEnumerator
) {
85 ANS_LOG("No device enumerator in use.");
90 mDeviceEnumerator
->UnregisterEndpointNotificationCallback(this);
92 // We can't really do anything here, so we just add a log for debugging.
93 ANS_LOGW("Unregister notification failed.");
95 ANS_LOG("Unregister notification callback successfully.");
98 mIsRegistered
= false;
101 // True whenever the notification server is set to report events to this
103 bool IsRegistered() const { return mIsRegistered
; }
105 // IMMNotificationClient Implementation
106 HRESULT STDMETHODCALLTYPE
OnDefaultDeviceChanged(EDataFlow aFlow
, ERole aRole
,
107 LPCWSTR aDeviceId
) override
{
108 ANS_LOG("Default device has changed: flow %d, role: %d\n", aFlow
, aRole
);
113 // The remaining methods are not implemented. they simply log when called
114 // (if log is enabled), for debugging.
115 HRESULT STDMETHODCALLTYPE
OnDeviceAdded(LPCWSTR aDeviceId
) override
{
116 ANS_LOG("Audio device added.");
120 HRESULT STDMETHODCALLTYPE
OnDeviceRemoved(LPCWSTR aDeviceId
) override
{
121 ANS_LOG("Audio device removed.");
125 HRESULT STDMETHODCALLTYPE
OnDeviceStateChanged(LPCWSTR aDeviceId
,
126 DWORD aNewState
) override
{
127 ANS_LOG("Audio device state changed.");
131 HRESULT STDMETHODCALLTYPE
132 OnPropertyValueChanged(LPCWSTR aDeviceId
, const PROPERTYKEY aKey
) override
{
133 ANS_LOG("Audio device property value changed.");
137 // IUnknown Implementation
138 ULONG STDMETHODCALLTYPE
AddRef() override
{
139 return InterlockedIncrement(&mRefCt
);
142 ULONG STDMETHODCALLTYPE
Release() override
{
143 ULONG ulRef
= InterlockedDecrement(&mRefCt
);
150 HRESULT STDMETHODCALLTYPE
QueryInterface(REFIID riid
,
151 VOID
** ppvInterface
) override
{
152 if (__uuidof(IUnknown
) == riid
) {
154 *ppvInterface
= static_cast<IUnknown
*>(this);
155 } else if (__uuidof(IMMNotificationClient
) == riid
) {
157 *ppvInterface
= static_cast<IMMNotificationClient
*>(this);
159 *ppvInterface
= NULL
;
160 return E_NOINTERFACE
;
166 RefPtr
<IMMDeviceEnumerator
> mDeviceEnumerator
;
167 DefaultDeviceChangedCallback mCallback
;
170 }; // class AudioNotification
173 * A singleton observer for audio device changed events.
175 static StaticAutoPtr
<AudioNotification
> sAudioNotification
;
178 * AudioNotificationSender Implementation
181 nsresult
AudioNotificationSender::Init() {
182 MOZ_ASSERT(XRE_IsParentProcess());
183 MOZ_ASSERT(NS_IsMainThread());
185 if (!sAudioNotification
) {
186 sAudioNotification
= new AudioNotification(NotifyDefaultDeviceChanged
);
187 ClearOnShutdown(&sAudioNotification
);
189 if (!sAudioNotification
->IsRegistered()) {
190 ANS_LOGW("The notification sender cannot be initialized.");
191 return NS_ERROR_FAILURE
;
193 ANS_LOG("The notification sender is initailized successfully.");
200 void AudioNotificationSender::NotifyDefaultDeviceChanged() {
201 // This is running on the callback thread (from OnDefaultDeviceChanged).
202 MOZ_ASSERT(XRE_IsParentProcess());
203 ANS_LOG("Notify the default device-changed event.");
205 RefPtr
<AudioDeviceChangedRunnable
> runnable
=
206 new AudioDeviceChangedRunnable();
207 NS_DispatchToMainThread(runnable
);
211 } // namespace mozilla