1
0
mirror of https://github.com/mpv-player/mpv synced 2025-01-11 09:29:29 +00:00

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.
This commit is contained in:
Kevin Mitchell 2015-03-31 01:56:17 -07:00
parent 1091353d47
commit a6bf38bcad
5 changed files with 119 additions and 65 deletions

View File

@ -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),

View File

@ -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) \

View File

@ -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(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);
}
/* 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;
}
/* 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",

View File

@ -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;

View File

@ -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);