VirtualBox

source: vbox/trunk/src/VBox/Devices/Audio/VBoxMMNotificationClient.cpp@ 83239

Last change on this file since 83239 was 83239, checked in by vboxsync, 5 years ago

Audio/Win: Added ability to register/unregister the IMMNotificationClient interface at runtime.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 6.7 KB
Line 
1/* $Id: VBoxMMNotificationClient.cpp 83239 2020-03-10 10:13:59Z vboxsync $ */
2/** @file
3 * VBoxMMNotificationClient.cpp - Implementation of the IMMNotificationClient interface
4 * to detect audio endpoint changes.
5 */
6
7/*
8 * Copyright (C) 2017-2020 Oracle Corporation
9 *
10 * This file is part of VirtualBox Open Source Edition (OSE), as
11 * available from http://www.215389.xyz. This file is free software;
12 * you can redistribute it and/or modify it under the terms of the GNU
13 * General Public License (GPL) as published by the Free Software
14 * Foundation, in version 2 as it comes in the "COPYING" file of the
15 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
16 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
17 */
18
19#include "VBoxMMNotificationClient.h"
20
21#include <iprt/win/windows.h>
22
23#pragma warning(push)
24#pragma warning(disable: 4201)
25#include <mmdeviceapi.h>
26#include <endpointvolume.h>
27#pragma warning(pop)
28
29#ifdef LOG_GROUP
30# undef LOG_GROUP
31#endif
32#define LOG_GROUP LOG_GROUP_DRV_HOST_AUDIO
33#include <VBox/log.h>
34
35VBoxMMNotificationClient::VBoxMMNotificationClient(void)
36 : m_fRegisteredClient(false)
37 , m_cRef(1)
38{
39}
40
41VBoxMMNotificationClient::~VBoxMMNotificationClient(void)
42{
43}
44
45/**
46 * Registers the mulitmedia notification client implementation.
47 */
48HRESULT VBoxMMNotificationClient::Register(void)
49{
50 HRESULT hr = m_pEnum->RegisterEndpointNotificationCallback(this);
51 if (SUCCEEDED(hr))
52 {
53 m_fRegisteredClient = true;
54
55 hr = AttachToDefaultEndpoint();
56 }
57
58 return hr;
59}
60
61/**
62 * Unregisters the mulitmedia notification client implementation.
63 */
64void VBoxMMNotificationClient::Unregister(void)
65{
66 DetachFromEndpoint();
67
68 if (m_fRegisteredClient)
69 {
70 m_pEnum->UnregisterEndpointNotificationCallback(this);
71
72 m_fRegisteredClient = false;
73 }
74}
75
76/**
77 * Initializes the mulitmedia notification client implementation.
78 *
79 * @return HRESULT
80 */
81HRESULT VBoxMMNotificationClient::Initialize(void)
82{
83 HRESULT hr = CoCreateInstance(__uuidof(MMDeviceEnumerator), 0, CLSCTX_ALL, __uuidof(IMMDeviceEnumerator),
84 (void **)&m_pEnum);
85
86 LogFunc(("Returning %Rhrc\n", hr));
87 return hr;
88}
89
90/**
91 * Registration callback implementation for storing our (required) contexts.
92 *
93 * @return IPRT status code.
94 * @param pDrvIns Driver instance to register the notification client to.
95 * @param pfnCallback Audio callback to call by the notification client in case of new events.
96 */
97int VBoxMMNotificationClient::RegisterCallback(PPDMDRVINS pDrvIns, PFNPDMHOSTAUDIOCALLBACK pfnCallback)
98{
99 this->m_pDrvIns = pDrvIns;
100 this->m_pfnCallback = pfnCallback;
101
102 return VINF_SUCCESS;
103}
104
105/**
106 * Unregistration callback implementation for cleaning up our mess when we're done handling
107 * with notifications.
108 */
109void VBoxMMNotificationClient::UnregisterCallback(void)
110{
111 this->m_pDrvIns = NULL;
112 this->m_pfnCallback = NULL;
113}
114
115/**
116 * Stub being called when attaching to the default audio endpoint.
117 * Does nothing at the moment.
118 */
119HRESULT VBoxMMNotificationClient::AttachToDefaultEndpoint(void)
120{
121 return S_OK;
122}
123
124/**
125 * Stub being called when detaching from the default audio endpoint.
126 * Does nothing at the moment.
127 */
128void VBoxMMNotificationClient::DetachFromEndpoint(void)
129{
130
131}
132
133/**
134 * Helper function for invoking the audio connector callback (if any).
135 */
136void VBoxMMNotificationClient::doCallback(void)
137{
138#ifdef VBOX_WITH_AUDIO_CALLBACKS
139 AssertPtr(this->m_pDrvIns);
140 AssertPtr(this->m_pfnCallback);
141
142 if (this->m_pfnCallback)
143 /* Ignore rc */ this->m_pfnCallback(this->m_pDrvIns, PDMAUDIOBACKENDCBTYPE_DEVICES_CHANGED, NULL, 0);
144#endif
145}
146
147/**
148 * Handler implementation which is called when an audio device state
149 * has been changed.
150 *
151 * @return HRESULT
152 * @param pwstrDeviceId Device ID the state is announced for.
153 * @param dwNewState New state the device is now in.
154 */
155STDMETHODIMP VBoxMMNotificationClient::OnDeviceStateChanged(LPCWSTR pwstrDeviceId, DWORD dwNewState)
156{
157 char *pszState = "unknown";
158
159 switch (dwNewState)
160 {
161 case DEVICE_STATE_ACTIVE:
162 pszState = "active";
163 break;
164 case DEVICE_STATE_DISABLED:
165 pszState = "disabled";
166 break;
167 case DEVICE_STATE_NOTPRESENT:
168 pszState = "not present";
169 break;
170 case DEVICE_STATE_UNPLUGGED:
171 pszState = "unplugged";
172 break;
173 default:
174 break;
175 }
176
177 LogRel(("Audio: Device '%ls' has changed state to '%s'\n", pwstrDeviceId, pszState));
178
179 doCallback();
180
181 return S_OK;
182}
183
184/**
185 * Handler implementation which is called when a new audio device has been added.
186 *
187 * @return HRESULT
188 * @param pwstrDeviceId Device ID which has been added.
189 */
190STDMETHODIMP VBoxMMNotificationClient::OnDeviceAdded(LPCWSTR pwstrDeviceId)
191{
192 LogRel(("Audio: Device '%ls' has been added\n", pwstrDeviceId));
193
194 return S_OK;
195}
196
197/**
198 * Handler implementation which is called when an audio device has been removed.
199 *
200 * @return HRESULT
201 * @param pwstrDeviceId Device ID which has been removed.
202 */
203STDMETHODIMP VBoxMMNotificationClient::OnDeviceRemoved(LPCWSTR pwstrDeviceId)
204{
205 LogRel(("Audio: Device '%ls' has been removed\n", pwstrDeviceId));
206
207 return S_OK;
208}
209
210/**
211 * Handler implementation which is called when the device audio device has been
212 * changed.
213 *
214 * @return HRESULT
215 * @param eFlow Flow direction of the new default device.
216 * @param eRole Role of the new default device.
217 * @param pwstrDefaultDeviceId ID of the new default device.
218 */
219STDMETHODIMP VBoxMMNotificationClient::OnDefaultDeviceChanged(EDataFlow eFlow, ERole eRole, LPCWSTR pwstrDefaultDeviceId)
220{
221 RT_NOREF(eRole);
222
223 char *pszRole = "unknown";
224
225 if (eFlow == eRender)
226 pszRole = "output";
227 else if (eFlow == eCapture)
228 pszRole = "input";
229
230 LogRel(("Audio: Default %s device has been changed to '%ls'\n", pszRole, pwstrDefaultDeviceId));
231
232 doCallback();
233
234 return S_OK;
235}
236
237STDMETHODIMP VBoxMMNotificationClient::QueryInterface(REFIID interfaceID, void **ppvInterface)
238{
239 const IID IID_IMMNotificationClient = __uuidof(IMMNotificationClient);
240
241 if ( IsEqualIID(interfaceID, IID_IUnknown)
242 || IsEqualIID(interfaceID, IID_IMMNotificationClient))
243 {
244 *ppvInterface = static_cast<IMMNotificationClient*>(this);
245 AddRef();
246 return S_OK;
247 }
248
249 *ppvInterface = NULL;
250 return E_NOINTERFACE;
251}
252
253STDMETHODIMP_(ULONG) VBoxMMNotificationClient::AddRef(void)
254{
255 return InterlockedIncrement(&m_cRef);
256}
257
258STDMETHODIMP_(ULONG) VBoxMMNotificationClient::Release(void)
259{
260 long lRef = InterlockedDecrement(&m_cRef);
261 if (lRef == 0)
262 delete this;
263
264 return lRef;
265}
266
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette