summaryrefslogtreecommitdiffstats
path: root/audio
diff options
context:
space:
mode:
authorKevin Mitchell <kevmitch@gmail.com>2015-03-31 01:56:17 -0700
committerKevin Mitchell <kevmitch@gmail.com>2015-03-31 02:02:54 -0700
commita6bf38bcadafd3f7b08b63d8ea1642a9f351f6e6 (patch)
treee7fb5b2f4d13d452499ac45022444f63c17a336a /audio
parent1091353d476b7187c8cfd350dbb98d2d1ad6d07c (diff)
downloadmpv-a6bf38bcadafd3f7b08b63d8ea1642a9f351f6e6.tar.bz2
mpv-a6bf38bcadafd3f7b08b63d8ea1642a9f351f6e6.tar.xz
ao/wasapi: add ao hotplug
Create a second copy of the change_notify structure for the hotplug ao. change_notify->is_hotplug distinguishes the hotplug version from the regular one monitoring the currently playing ao. Also make the change notification less verbose now that there might be two of them around.
Diffstat (limited to 'audio')
-rw-r--r--audio/out/ao_wasapi.c20
-rwxr-xr-xaudio/out/ao_wasapi.h4
-rwxr-xr-xaudio/out/ao_wasapi_changenotify.c129
-rwxr-xr-xaudio/out/ao_wasapi_utils.c26
-rwxr-xr-xaudio/out/ao_wasapi_utils.h3
5 files changed, 118 insertions, 64 deletions
diff --git a/audio/out/ao_wasapi.c b/audio/out/ao_wasapi.c
index fb045d1858..8a8d910469 100644
--- a/audio/out/ao_wasapi.c
+++ b/audio/out/ao_wasapi.c
@@ -391,6 +391,24 @@ static void audio_resume(struct ao *ao)
SetEvent(state->hResume);
}
+static void hotplug_uninit(struct ao *ao)
+{
+ MP_DBG(ao, "Hotplug uninit\n");
+ wasapi_hotplug_uninit(ao);
+ CoUninitialize();
+}
+
+static int hotplug_init(struct ao *ao)
+{
+ MP_DBG(ao, "Hotplug init\n");
+ CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
+ if (wasapi_hotplug_init(ao) != S_OK) {
+ hotplug_uninit(ao);
+ return -1;
+ }
+ return 0;
+}
+
static void list_devs(struct ao *ao, struct ao_device_list *list)
{
CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
@@ -410,6 +428,8 @@ const struct ao_driver audio_out_wasapi = {
.reset = audio_reset,
.resume = audio_resume,
.list_devs = list_devs,
+ .hotplug_init = hotplug_init,
+ .hotplug_uninit = hotplug_uninit,
.priv_size = sizeof(wasapi_state),
.options = (const struct m_option[]) {
OPT_FLAG("exclusive", opt_exclusive, 0),
diff --git a/audio/out/ao_wasapi.h b/audio/out/ao_wasapi.h
index 2fd266eab0..3ae0816a8d 100755
--- a/audio/out/ao_wasapi.h
+++ b/audio/out/ao_wasapi.h
@@ -20,6 +20,7 @@
#ifndef MP_AO_WASAPI_H_
#define MP_AO_WASAPI_H_
+#include <stdbool.h>
#include <audioclient.h>
#include <audiopolicy.h>
#include <mmdeviceapi.h>
@@ -30,10 +31,11 @@
typedef struct change_notify {
IMMNotificationClient client; /* this must be first in the structure! */
LPWSTR monitored; /* Monitored device */
+ bool is_hotplug;
struct ao *ao;
} change_notify;
-HRESULT wasapi_change_init(struct ao* ao);
+HRESULT wasapi_change_init(struct ao* ao, bool is_hotplug);
void wasapi_change_uninit(struct ao* ao);
#define EXIT_ON_ERROR(hres) \
diff --git a/audio/out/ao_wasapi_changenotify.c b/audio/out/ao_wasapi_changenotify.c
index 6f2263a3ec..26c3c7ed0d 100755
--- a/audio/out/ao_wasapi_changenotify.c
+++ b/audio/out/ao_wasapi_changenotify.c
@@ -27,26 +27,6 @@
#include "ao_wasapi.h"
#include "ao_wasapi_utils.h"
-static char* ERole_to_str(ERole role)
-{
- switch (role) {
- case eConsole: return "console";
- case eMultimedia: return "multimedia";
- case eCommunications: return "communications";
- default: return "<Unknown>";
- }
-}
-
-static char* EDataFlow_to_str(EDataFlow flow)
-{
- switch (flow) {
- case eRender: return "render";
- case eCapture: return "capture";
- case eAll: return "all";
- default: return "<Unknown>";
- }
-}
-
static HRESULT STDMETHODCALLTYPE sIMMNotificationClient_QueryInterface(
IMMNotificationClient* This, REFIID riid, void **ppvObject)
{
@@ -84,19 +64,23 @@ static HRESULT STDMETHODCALLTYPE sIMMNotificationClient_OnDeviceStateChanged(
change_notify *change = (change_notify *)This;
struct ao *ao = change->ao;
- if (pwstrDeviceId && !wcscmp(change->monitored, pwstrDeviceId)) {
+ if (change->is_hotplug) {
+ MP_VERBOSE(ao, "OnDeviceStateChanged triggered: sending hotplug event\n");
+ ao_hotplug_event(ao);
+ } else if (pwstrDeviceId && !wcscmp(pwstrDeviceId, change->monitored)) {
switch (dwNewState) {
case DEVICE_STATE_DISABLED:
case DEVICE_STATE_NOTPRESENT:
case DEVICE_STATE_UNPLUGGED:
- MP_VERBOSE(ao,
- "OnDeviceStateChange triggered - requesting ao reload\n");
+ MP_VERBOSE(ao, "OnDeviceStateChanged triggered on device %S: "
+ "requesting ao reload\n", pwstrDeviceId);
ao_request_reload(ao);
+ break;
case DEVICE_STATE_ACTIVE:
- default:
- return S_OK;
+ break;
}
}
+
return S_OK;
}
@@ -107,9 +91,11 @@ static HRESULT STDMETHODCALLTYPE sIMMNotificationClient_OnDeviceAdded(
change_notify *change = (change_notify *)This;
struct ao *ao = change->ao;
- MP_VERBOSE(ao, "OnDeviceAdded triggered\n");
- if(pwstrDeviceId)
- MP_VERBOSE(ao, "New device %S\n",pwstrDeviceId);
+ if (change->is_hotplug) {
+ MP_VERBOSE(ao, "OnDeviceAdded triggered: sending hotplug event\n");
+ ao_hotplug_event(ao);
+ }
+
return S_OK;
}
@@ -121,10 +107,15 @@ static HRESULT STDMETHODCALLTYPE sIMMNotificationClient_OnDeviceRemoved(
change_notify *change = (change_notify *)This;
struct ao *ao = change->ao;
- if (pwstrDeviceId && !wcscmp(change->monitored, pwstrDeviceId)) {
- MP_VERBOSE(ao, "OnDeviceRemoved triggered - requesting ao reload\n");
+ if (change->is_hotplug) {
+ MP_VERBOSE(ao, "OnDeviceRemoved triggered: sending hotplug event\n");
+ ao_hotplug_event(ao);
+ } else if (pwstrDeviceId && !wcscmp(pwstrDeviceId, change->monitored)) {
+ MP_VERBOSE(ao, "OnDeviceRemoved triggered for device %S: "
+ "requesting ao reload\n", pwstrDeviceId);
ao_request_reload(ao);
}
+
return S_OK;
}
@@ -138,29 +129,32 @@ static HRESULT STDMETHODCALLTYPE sIMMNotificationClient_OnDefaultDeviceChanged(
struct ao *ao = change->ao;
struct wasapi_state *state = (struct wasapi_state *)ao->priv;
- MP_VERBOSE(ao, "OnDefaultDeviceChanged triggered for role:%s flow:%s\n",
- ERole_to_str(role), EDataFlow_to_str(flow));
- if(pwstrDeviceId)
- MP_VERBOSE(ao, "New default device %S\n", pwstrDeviceId);
-
/* don't care about "eCapture" or non-"eMultimedia" roles */
if (flow == eCapture || role != eMultimedia) return S_OK;
- /* stay on the device the user specified */
- if (state->opt_device) {
- MP_VERBOSE(ao, "Staying on specified device \"%s\"", state->opt_device);
- return S_OK;
- }
+ if (change->is_hotplug) {
+ MP_VERBOSE(ao, "OnDefaultDeviceChanged triggered: sending hotplug event\n");
+ ao_hotplug_event(ao);
+ } else {
+ /* stay on the device the user specified */
+ if (state->opt_device) {
+ MP_VERBOSE(ao, "OnDefaultDeviceChanged triggered: "
+ "staying on specified device %s\n", state->opt_device);
+ return S_OK;
+ }
- /* don't reload if already on the new default */
- if (pwstrDeviceId && !wcscmp(change->monitored, pwstrDeviceId)) {
- MP_VERBOSE(ao, "Already using default device, no reload required\n");
- return S_OK;
+ /* don't reload if already on the new default */
+ if (pwstrDeviceId && !wcscmp(pwstrDeviceId, change->monitored)) {
+ MP_VERBOSE(ao, "OnDefaultDeviceChanged triggered: "
+ "already using default device, no reload required\n");
+ return S_OK;
+ }
+
+ /* if we got here, we need to reload */
+ MP_VERBOSE(ao, "OnDefaultDeviceChanged triggered: requesting ao reload\n");
+ ao_request_reload(ao);
}
- /* if we got here, we need to reload */
- ao_request_reload(ao);
- MP_VERBOSE(ao, "Requesting ao reload\n");
return S_OK;
}
@@ -172,17 +166,21 @@ static HRESULT STDMETHODCALLTYPE sIMMNotificationClient_OnPropertyValueChanged(
change_notify *change = (change_notify *)This;
struct ao *ao = change->ao;
- if (pwstrDeviceId && !wcscmp(change->monitored, pwstrDeviceId)) {
- MP_VERBOSE(ao, "OnPropertyValueChanged triggered\n");
- MP_VERBOSE(ao, "Changed property: ");
+ if (!change->is_hotplug && pwstrDeviceId &&
+ !wcscmp(pwstrDeviceId, change->monitored))
+ {
+ MP_VERBOSE(ao, "OnPropertyValueChanged triggered on device %S\n",
+ pwstrDeviceId);
if (IsEqualPropertyKey(PKEY_AudioEngine_DeviceFormat, key)) {
MP_VERBOSE(change->ao,
- "PKEY_AudioEngine_DeviceFormat - requesting ao reload\n");
+ "Changed property: PKEY_AudioEngine_DeviceFormat "
+ "- requesting ao reload\n");
ao_request_reload(change->ao);
} else {
- MP_VERBOSE(ao, "%s\n", mp_PKEY_to_str(&key));
+ MP_VERBOSE(ao, "Changed property: %s\n", mp_PKEY_to_str(&key));
}
}
+
return S_OK;
}
@@ -198,27 +196,34 @@ static CONST_VTBL IMMNotificationClientVtbl sIMMDeviceEnumeratorVtbl_vtbl = {
};
-HRESULT wasapi_change_init(struct ao *ao)
+HRESULT wasapi_change_init(struct ao *ao, bool is_hotplug)
{
- MP_DBG(ao, "Setting up monitoring on playback device\n");
struct wasapi_state *state = (struct wasapi_state *)ao->priv;
struct change_notify *change = &state->change;
+ HRESULT hr;
/* COM voodoo to emulate c++ class */
change->client.lpVtbl = &sIMMDeviceEnumeratorVtbl_vtbl;
- /* so the callbacks can access the ao */
- change->ao = ao;
-
- /* get the device string to compare with the pwstrDeviceId argument in callbacks */
- HRESULT hr = IMMDevice_GetId(state->pDevice, &change->monitored);
- EXIT_ON_ERROR(hr);
-
/* register the change notification client */
hr = IMMDeviceEnumerator_RegisterEndpointNotificationCallback(
state->pEnumerator, (IMMNotificationClient *)change);
EXIT_ON_ERROR(hr);
- MP_VERBOSE(state, "Monitoring changes in device: %S\n", state->change.monitored);
+ /* so the callbacks can access the ao */
+ change->ao = ao;
+
+ /* whether or not this is the hotplug instance */
+ change->is_hotplug = is_hotplug;
+
+ if (is_hotplug) {
+ MP_DBG(ao, "Monitoring for hotplug events\n");
+ } else {
+ /* Get the device string to compare with the pwstrDeviceId */
+ hr = IMMDevice_GetId(state->pDevice, &change->monitored);
+ EXIT_ON_ERROR(hr);
+ MP_VERBOSE(ao, "Monitoring changes in device %S\n", change->monitored);
+ }
+
return hr;
exit_label:
MP_ERR(state, "Error setting up device change monitoring: %s\n",
diff --git a/audio/out/ao_wasapi_utils.c b/audio/out/ao_wasapi_utils.c
index 9e2ffe0dff..6ecc6c82a2 100755
--- a/audio/out/ao_wasapi_utils.c
+++ b/audio/out/ao_wasapi_utils.c
@@ -1211,7 +1211,7 @@ retry:
}
state->previous_volume = state->initial_volume;
- wasapi_change_init(ao);
+ wasapi_change_init(ao, false);
MP_DBG(ao, "Init wasapi thread done\n");
return S_OK;
@@ -1221,6 +1221,30 @@ exit_label:
return hr;
}
+HRESULT wasapi_hotplug_init(struct ao *ao)
+{
+ struct wasapi_state *state = (struct wasapi_state *)ao->priv;
+ HRESULT hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL,
+ &IID_IMMDeviceEnumerator, (void **)&state->pEnumerator);
+ EXIT_ON_ERROR(hr);
+
+ hr = wasapi_change_init(ao, true);
+ EXIT_ON_ERROR(hr);
+
+ return S_OK;
+exit_label:
+ MP_ERR(state, "Error setting up audio hotplug: %s (0x%"PRIx32")\n",
+ wasapi_explain_err(hr), (uint32_t) hr);
+ return hr;
+}
+
+void wasapi_hotplug_uninit(struct ao *ao)
+{
+ struct wasapi_state *state = (struct wasapi_state *)ao->priv;
+ wasapi_change_uninit(ao);
+ SAFE_RELEASE(state->pEnumerator, IMMDeviceEnumerator_Release(state->pEnumerator));
+}
+
void wasapi_thread_uninit(struct ao *ao)
{
struct wasapi_state *state = (struct wasapi_state *)ao->priv;
diff --git a/audio/out/ao_wasapi_utils.h b/audio/out/ao_wasapi_utils.h
index a44cd4d45a..7327d2f350 100755
--- a/audio/out/ao_wasapi_utils.h
+++ b/audio/out/ao_wasapi_utils.h
@@ -47,6 +47,9 @@ void wasapi_dispatch(void);
HRESULT wasapi_thread_init(struct ao *ao);
void wasapi_thread_uninit(struct ao *ao);
+HRESULT wasapi_hotplug_init(struct ao *ao);
+void wasapi_hotplug_uninit(struct ao *ao);
+
HRESULT wasapi_setup_proxies(wasapi_state *state);
void wasapi_release_proxies(wasapi_state *state);