mirror of
https://github.com/mpv-player/mpv
synced 2025-04-11 04:01:31 +00:00
ao_wasapi: wrap long lines and use only c99 comment style
also remove a log message in AOCONTROL_UPDATE_STREAM_TITLE since none of the other controls have one.
This commit is contained in:
parent
c188240ab9
commit
3ae726e8dd
@ -39,8 +39,9 @@ static HRESULT get_device_delay(struct wasapi_state *state, double *delay) {
|
|||||||
HRESULT hr;
|
HRESULT hr;
|
||||||
|
|
||||||
hr = IAudioClock_GetPosition(state->pAudioClock, &position, &qpc_position);
|
hr = IAudioClock_GetPosition(state->pAudioClock, &position, &qpc_position);
|
||||||
/* GetPosition succeeded, but the result may be inaccurate due to the length of the call */
|
// GetPosition succeeded, but the result may be
|
||||||
/* http://msdn.microsoft.com/en-us/library/windows/desktop/dd370889%28v=vs.85%29.aspx */
|
// inaccurate due to the length of the call
|
||||||
|
// http://msdn.microsoft.com/en-us/library/windows/desktop/dd370889%28v=vs.85%29.aspx
|
||||||
if (hr == S_FALSE) {
|
if (hr == S_FALSE) {
|
||||||
MP_DBG(state, "Possibly inaccurate device position.\n");
|
MP_DBG(state, "Possibly inaccurate device position.\n");
|
||||||
hr = S_OK;
|
hr = S_OK;
|
||||||
@ -49,12 +50,14 @@ static HRESULT get_device_delay(struct wasapi_state *state, double *delay) {
|
|||||||
|
|
||||||
LARGE_INTEGER qpc_count;
|
LARGE_INTEGER qpc_count;
|
||||||
QueryPerformanceCounter(&qpc_count);
|
QueryPerformanceCounter(&qpc_count);
|
||||||
double qpc_diff = (qpc_count.QuadPart * 1e7 / state->qpc_frequency.QuadPart) - qpc_position;
|
double qpc_diff = (qpc_count.QuadPart * 1e7 / state->qpc_frequency.QuadPart)
|
||||||
|
- qpc_position;
|
||||||
|
|
||||||
position += state->clock_frequency * (uint64_t) (qpc_diff / 1e7);
|
position += state->clock_frequency * (uint64_t) (qpc_diff / 1e7);
|
||||||
|
|
||||||
/* convert position to the same base as sample_count */
|
// convert position to the same base as sample_count
|
||||||
position = position * state->format.Format.nSamplesPerSec / state->clock_frequency;
|
position = position * state->format.Format.nSamplesPerSec
|
||||||
|
/ state->clock_frequency;
|
||||||
|
|
||||||
double diff = sample_count - position;
|
double diff = sample_count - position;
|
||||||
*delay = diff / state->format.Format.nSamplesPerSec;
|
*delay = diff / state->format.Format.nSamplesPerSec;
|
||||||
@ -80,7 +83,8 @@ static void thread_feed(struct ao *ao)
|
|||||||
EXIT_ON_ERROR(hr);
|
EXIT_ON_ERROR(hr);
|
||||||
|
|
||||||
frame_count -= padding;
|
frame_count -= padding;
|
||||||
MP_TRACE(ao, "Frame to fill: %"PRIu32". Padding: %"PRIu32"\n", frame_count, padding);
|
MP_TRACE(ao, "Frame to fill: %"PRIu32". Padding: %"PRIu32"\n",
|
||||||
|
frame_count, padding);
|
||||||
}
|
}
|
||||||
double delay;
|
double delay;
|
||||||
hr = get_device_delay(state, &delay);
|
hr = get_device_delay(state, &delay);
|
||||||
@ -124,17 +128,21 @@ static void thread_resume(struct ao *ao)
|
|||||||
mp_HRESULT_to_str(hr));
|
mp_HRESULT_to_str(hr));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Fill the buffer before starting, but only if there is no audio queued to play. */
|
// Fill the buffer before starting, but only if there is no audio queued to
|
||||||
/* This prevents overfilling the buffer, which leads to problems in exclusive mode */
|
// play. This prevents overfilling the buffer, which leads to problems in
|
||||||
|
// exclusive mode
|
||||||
if (padding < (UINT32) state->bufferFrameCount)
|
if (padding < (UINT32) state->bufferFrameCount)
|
||||||
thread_feed(ao);
|
thread_feed(ao);
|
||||||
|
|
||||||
// start feeding next wakeup if something else hasn't been requested
|
// start feeding next wakeup if something else hasn't been requested
|
||||||
int expected = WASAPI_THREAD_RESUME;
|
int expected = WASAPI_THREAD_RESUME;
|
||||||
atomic_compare_exchange_strong(&state->thread_state, &expected, WASAPI_THREAD_FEED);
|
atomic_compare_exchange_strong(&state->thread_state, &expected,
|
||||||
|
WASAPI_THREAD_FEED);
|
||||||
hr = IAudioClient_Start(state->pAudioClient);
|
hr = IAudioClient_Start(state->pAudioClient);
|
||||||
if (hr != S_OK)
|
if (hr != S_OK) {
|
||||||
MP_ERR(state, "IAudioClient_Start returned %s\n", mp_HRESULT_to_str(hr));
|
MP_ERR(state, "IAudioClient_Start returned %s\n",
|
||||||
|
mp_HRESULT_to_str(hr));
|
||||||
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -145,11 +153,11 @@ static void thread_reset(struct ao *ao)
|
|||||||
HRESULT hr;
|
HRESULT hr;
|
||||||
MP_DBG(state, "Thread Reset\n");
|
MP_DBG(state, "Thread Reset\n");
|
||||||
hr = IAudioClient_Stop(state->pAudioClient);
|
hr = IAudioClient_Stop(state->pAudioClient);
|
||||||
/* we may get S_FALSE if the stream is already stopped */
|
// we may get S_FALSE if the stream is already stopped
|
||||||
if (hr != S_OK && hr != S_FALSE)
|
if (hr != S_OK && hr != S_FALSE)
|
||||||
MP_ERR(state, "IAudioClient_Stop returned: %s\n", mp_HRESULT_to_str(hr));
|
MP_ERR(state, "IAudioClient_Stop returned: %s\n", mp_HRESULT_to_str(hr));
|
||||||
|
|
||||||
/* we may get S_FALSE if the stream is already reset */
|
// we may get S_FALSE if the stream is already reset
|
||||||
hr = IAudioClient_Reset(state->pAudioClient);
|
hr = IAudioClient_Reset(state->pAudioClient);
|
||||||
if (hr != S_OK && hr != S_FALSE)
|
if (hr != S_OK && hr != S_FALSE)
|
||||||
MP_ERR(state, "IAudioClient_Reset returned: %s\n", mp_HRESULT_to_str(hr));
|
MP_ERR(state, "IAudioClient_Reset returned: %s\n", mp_HRESULT_to_str(hr));
|
||||||
@ -157,7 +165,8 @@ static void thread_reset(struct ao *ao)
|
|||||||
atomic_store(&state->sample_count, 0);
|
atomic_store(&state->sample_count, 0);
|
||||||
// start feeding next wakeup if something else hasn't been requested
|
// start feeding next wakeup if something else hasn't been requested
|
||||||
int expected = WASAPI_THREAD_RESET;
|
int expected = WASAPI_THREAD_RESET;
|
||||||
atomic_compare_exchange_strong(&state->thread_state, &expected, WASAPI_THREAD_FEED);
|
atomic_compare_exchange_strong(&state->thread_state, &expected,
|
||||||
|
WASAPI_THREAD_FEED);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -173,11 +182,12 @@ static DWORD __stdcall AudioThread(void *lpParameter)
|
|||||||
goto exit_label;
|
goto exit_label;
|
||||||
|
|
||||||
MP_DBG(ao, "Entering dispatch loop\n");
|
MP_DBG(ao, "Entering dispatch loop\n");
|
||||||
while (true) { /* watch events */
|
while (true) { // watch events
|
||||||
HANDLE events[] = {state->hWake};
|
HANDLE events[] = {state->hWake};
|
||||||
switch (MsgWaitForMultipleObjects(MP_ARRAY_SIZE(events), events, FALSE, INFINITE,
|
switch (MsgWaitForMultipleObjects(MP_ARRAY_SIZE(events), events,
|
||||||
|
FALSE, INFINITE,
|
||||||
QS_POSTMESSAGE | QS_SENDMESSAGE)) {
|
QS_POSTMESSAGE | QS_SENDMESSAGE)) {
|
||||||
/* AudioThread wakeup */
|
// AudioThread wakeup
|
||||||
case WAIT_OBJECT_0:
|
case WAIT_OBJECT_0:
|
||||||
switch (atomic_load(&state->thread_state)) {
|
switch (atomic_load(&state->thread_state)) {
|
||||||
case WASAPI_THREAD_FEED:
|
case WASAPI_THREAD_FEED:
|
||||||
@ -198,7 +208,7 @@ static DWORD __stdcall AudioThread(void *lpParameter)
|
|||||||
goto exit_label;
|
goto exit_label;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
/* messages to dispatch (COM marshalling) */
|
// messages to dispatch (COM marshalling)
|
||||||
case (WAIT_OBJECT_0 + MP_ARRAY_SIZE(events)):
|
case (WAIT_OBJECT_0 + MP_ARRAY_SIZE(events)):
|
||||||
wasapi_dispatch(ao);
|
wasapi_dispatch(ao);
|
||||||
break;
|
break;
|
||||||
@ -215,7 +225,8 @@ exit_label:
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void set_thread_state(struct ao *ao, enum wasapi_thread_state thread_state)
|
static void set_thread_state(struct ao *ao,
|
||||||
|
enum wasapi_thread_state thread_state)
|
||||||
{
|
{
|
||||||
struct wasapi_state *state = ao->priv;
|
struct wasapi_state *state = ao->priv;
|
||||||
atomic_store(&state->thread_state, thread_state);
|
atomic_store(&state->thread_state, thread_state);
|
||||||
@ -230,7 +241,7 @@ static void uninit(struct ao *ao)
|
|||||||
if (state->hWake)
|
if (state->hWake)
|
||||||
set_thread_state(ao, WASAPI_THREAD_SHUTDOWN);
|
set_thread_state(ao, WASAPI_THREAD_SHUTDOWN);
|
||||||
|
|
||||||
/* wait up to 10 seconds */
|
// wait up to 10 seconds
|
||||||
if (state->hAudioThread &&
|
if (state->hAudioThread &&
|
||||||
WaitForSingleObject(state->hAudioThread, 10000) == WAIT_TIMEOUT)
|
WaitForSingleObject(state->hAudioThread, 10000) == WAIT_TIMEOUT)
|
||||||
{
|
{
|
||||||
@ -270,7 +281,7 @@ static int init(struct ao *ao)
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
WaitForSingleObject(state->hInitDone, INFINITE); /* wait on init complete */
|
WaitForSingleObject(state->hInitDone, INFINITE); // wait on init complete
|
||||||
SAFE_RELEASE(state->hInitDone,CloseHandle(state->hInitDone));
|
SAFE_RELEASE(state->hInitDone,CloseHandle(state->hInitDone));
|
||||||
if (state->init_ret != S_OK) {
|
if (state->init_ret != S_OK) {
|
||||||
if (!ao->probing)
|
if (!ao->probing)
|
||||||
@ -388,23 +399,21 @@ static int control(struct ao *ao, enum aocontrol cmd, void *arg)
|
|||||||
if (!state->pSessionControlProxy)
|
if (!state->pSessionControlProxy)
|
||||||
return CONTROL_FALSE;
|
return CONTROL_FALSE;
|
||||||
|
|
||||||
MP_VERBOSE(state, "Updating stream title to \"%s\"\n", (char*)arg);
|
|
||||||
wchar_t *title = mp_from_utf8(NULL, (char*)arg);
|
wchar_t *title = mp_from_utf8(NULL, (char*)arg);
|
||||||
|
|
||||||
wchar_t *tmp = NULL;
|
wchar_t *tmp = NULL;
|
||||||
|
// There is a weird race condition in the IAudioSessionControl itself --
|
||||||
/* There is a weird race condition in the IAudioSessionControl itself --
|
// it seems that *sometimes* the SetDisplayName does not take effect and
|
||||||
it seems that *sometimes* the SetDisplayName does not take effect and it still shows
|
// it still shows the old title. Use this loop to insist until it works.
|
||||||
the old title. Use this loop to insist until it works. */
|
|
||||||
do {
|
do {
|
||||||
IAudioSessionControl_SetDisplayName(state->pSessionControlProxy, title, NULL);
|
IAudioSessionControl_SetDisplayName(state->pSessionControlProxy,
|
||||||
|
title, NULL);
|
||||||
|
|
||||||
SAFE_RELEASE(tmp, CoTaskMemFree(tmp));
|
SAFE_RELEASE(tmp, CoTaskMemFree(tmp));
|
||||||
IAudioSessionControl_GetDisplayName(state->pSessionControlProxy, &tmp);
|
IAudioSessionControl_GetDisplayName(state->pSessionControlProxy,
|
||||||
|
&tmp);
|
||||||
} while (lstrcmpW(title, tmp));
|
} while (lstrcmpW(title, tmp));
|
||||||
SAFE_RELEASE(tmp, CoTaskMemFree(tmp));
|
SAFE_RELEASE(tmp, CoTaskMemFree(tmp));
|
||||||
talloc_free(title);
|
talloc_free(title);
|
||||||
|
|
||||||
return CONTROL_OK;
|
return CONTROL_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -427,7 +436,8 @@ static void hotplug_uninit(struct ao *ao)
|
|||||||
MP_DBG(ao, "Hotplug uninit\n");
|
MP_DBG(ao, "Hotplug uninit\n");
|
||||||
struct wasapi_state *state = ao->priv;
|
struct wasapi_state *state = ao->priv;
|
||||||
wasapi_change_uninit(ao);
|
wasapi_change_uninit(ao);
|
||||||
SAFE_RELEASE(state->pEnumerator, IMMDeviceEnumerator_Release(state->pEnumerator));
|
SAFE_RELEASE(state->pEnumerator,
|
||||||
|
IMMDeviceEnumerator_Release(state->pEnumerator));
|
||||||
CoUninitialize();
|
CoUninitialize();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -438,7 +448,8 @@ static int hotplug_init(struct ao *ao)
|
|||||||
state->log = ao->log;
|
state->log = ao->log;
|
||||||
CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
|
CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
|
||||||
HRESULT hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL,
|
HRESULT hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL,
|
||||||
&IID_IMMDeviceEnumerator, (void **)&state->pEnumerator);
|
&IID_IMMDeviceEnumerator,
|
||||||
|
(void **)&state->pEnumerator);
|
||||||
EXIT_ON_ERROR(hr);
|
EXIT_ON_ERROR(hr);
|
||||||
hr = wasapi_change_init(ao, true);
|
hr = wasapi_change_init(ao, true);
|
||||||
EXIT_ON_ERROR(hr);
|
EXIT_ON_ERROR(hr);
|
||||||
|
@ -29,8 +29,8 @@
|
|||||||
#include "osdep/atomics.h"
|
#include "osdep/atomics.h"
|
||||||
|
|
||||||
typedef struct change_notify {
|
typedef struct change_notify {
|
||||||
IMMNotificationClient client; /* this must be first in the structure! */
|
IMMNotificationClient client; // this must be first in the structure!
|
||||||
LPWSTR monitored; /* Monitored device */
|
LPWSTR monitored; // Monitored device
|
||||||
bool is_hotplug;
|
bool is_hotplug;
|
||||||
struct ao *ao;
|
struct ao *ao;
|
||||||
} change_notify;
|
} change_notify;
|
||||||
|
@ -30,7 +30,7 @@
|
|||||||
static HRESULT STDMETHODCALLTYPE sIMMNotificationClient_QueryInterface(
|
static HRESULT STDMETHODCALLTYPE sIMMNotificationClient_QueryInterface(
|
||||||
IMMNotificationClient* This, REFIID riid, void **ppvObject)
|
IMMNotificationClient* This, REFIID riid, void **ppvObject)
|
||||||
{
|
{
|
||||||
/* Compatible with IMMNotificationClient and IUnknown */
|
// Compatible with IMMNotificationClient and IUnknown
|
||||||
if (IsEqualGUID(&IID_IMMNotificationClient, riid) ||
|
if (IsEqualGUID(&IID_IMMNotificationClient, riid) ||
|
||||||
IsEqualGUID(&IID_IUnknown, riid))
|
IsEqualGUID(&IID_IUnknown, riid))
|
||||||
{
|
{
|
||||||
@ -42,14 +42,14 @@ static HRESULT STDMETHODCALLTYPE sIMMNotificationClient_QueryInterface(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* these are required, but not actually used */
|
// these are required, but not actually used
|
||||||
static ULONG STDMETHODCALLTYPE sIMMNotificationClient_AddRef(
|
static ULONG STDMETHODCALLTYPE sIMMNotificationClient_AddRef(
|
||||||
IMMNotificationClient *This)
|
IMMNotificationClient *This)
|
||||||
{
|
{
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* MSDN says it should free itself, but we're static */
|
// MSDN says it should free itself, but we're static
|
||||||
static ULONG STDMETHODCALLTYPE sIMMNotificationClient_Release(
|
static ULONG STDMETHODCALLTYPE sIMMNotificationClient_Release(
|
||||||
IMMNotificationClient *This)
|
IMMNotificationClient *This)
|
||||||
{
|
{
|
||||||
@ -65,7 +65,8 @@ static HRESULT STDMETHODCALLTYPE sIMMNotificationClient_OnDeviceStateChanged(
|
|||||||
struct ao *ao = change->ao;
|
struct ao *ao = change->ao;
|
||||||
|
|
||||||
if (change->is_hotplug) {
|
if (change->is_hotplug) {
|
||||||
MP_VERBOSE(ao, "OnDeviceStateChanged triggered: sending hotplug event\n");
|
MP_VERBOSE(ao,
|
||||||
|
"OnDeviceStateChanged triggered: sending hotplug event\n");
|
||||||
ao_hotplug_event(ao);
|
ao_hotplug_event(ao);
|
||||||
} else if (pwstrDeviceId && !wcscmp(pwstrDeviceId, change->monitored)) {
|
} else if (pwstrDeviceId && !wcscmp(pwstrDeviceId, change->monitored)) {
|
||||||
switch (dwNewState) {
|
switch (dwNewState) {
|
||||||
@ -99,7 +100,7 @@ static HRESULT STDMETHODCALLTYPE sIMMNotificationClient_OnDeviceAdded(
|
|||||||
return S_OK;
|
return S_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* maybe MPV can go over to the prefered device once it is plugged in? */
|
// maybe MPV can go over to the prefered device once it is plugged in?
|
||||||
static HRESULT STDMETHODCALLTYPE sIMMNotificationClient_OnDeviceRemoved(
|
static HRESULT STDMETHODCALLTYPE sIMMNotificationClient_OnDeviceRemoved(
|
||||||
IMMNotificationClient *This,
|
IMMNotificationClient *This,
|
||||||
LPCWSTR pwstrDeviceId)
|
LPCWSTR pwstrDeviceId)
|
||||||
@ -129,29 +130,31 @@ static HRESULT STDMETHODCALLTYPE sIMMNotificationClient_OnDefaultDeviceChanged(
|
|||||||
struct ao *ao = change->ao;
|
struct ao *ao = change->ao;
|
||||||
struct wasapi_state *state = ao->priv;
|
struct wasapi_state *state = ao->priv;
|
||||||
|
|
||||||
/* don't care about "eCapture" or non-"eMultimedia" roles */
|
// don't care about "eCapture" or non-"eMultimedia" roles
|
||||||
if (flow == eCapture || role != eMultimedia) return S_OK;
|
if (flow == eCapture || role != eMultimedia) return S_OK;
|
||||||
|
|
||||||
if (change->is_hotplug) {
|
if (change->is_hotplug) {
|
||||||
MP_VERBOSE(ao, "OnDefaultDeviceChanged triggered: sending hotplug event\n");
|
MP_VERBOSE(ao,
|
||||||
|
"OnDefaultDeviceChanged triggered: sending hotplug event\n");
|
||||||
ao_hotplug_event(ao);
|
ao_hotplug_event(ao);
|
||||||
} else {
|
} else {
|
||||||
/* stay on the device the user specified */
|
// stay on the device the user specified
|
||||||
if (state->opt_device) {
|
if (state->opt_device) {
|
||||||
MP_VERBOSE(ao, "OnDefaultDeviceChanged triggered: "
|
MP_VERBOSE(ao, "OnDefaultDeviceChanged triggered: "
|
||||||
"staying on specified device %s\n", state->opt_device);
|
"staying on specified device %s\n", state->opt_device);
|
||||||
return S_OK;
|
return S_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* don't reload if already on the new default */
|
// don't reload if already on the new default
|
||||||
if (pwstrDeviceId && !wcscmp(pwstrDeviceId, change->monitored)) {
|
if (pwstrDeviceId && !wcscmp(pwstrDeviceId, change->monitored)) {
|
||||||
MP_VERBOSE(ao, "OnDefaultDeviceChanged triggered: "
|
MP_VERBOSE(ao, "OnDefaultDeviceChanged triggered: "
|
||||||
"already using default device, no reload required\n");
|
"already using default device, no reload required\n");
|
||||||
return S_OK;
|
return S_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* if we got here, we need to reload */
|
// if we got here, we need to reload
|
||||||
MP_VERBOSE(ao, "OnDefaultDeviceChanged triggered: requesting ao reload\n");
|
MP_VERBOSE(ao,
|
||||||
|
"OnDefaultDeviceChanged triggered: requesting ao reload\n");
|
||||||
ao_request_reload(ao);
|
ao_request_reload(ao);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -201,24 +204,24 @@ HRESULT wasapi_change_init(struct ao *ao, bool is_hotplug)
|
|||||||
struct wasapi_state *state = ao->priv;
|
struct wasapi_state *state = ao->priv;
|
||||||
struct change_notify *change = &state->change;
|
struct change_notify *change = &state->change;
|
||||||
HRESULT hr;
|
HRESULT hr;
|
||||||
/* COM voodoo to emulate c++ class */
|
// COM voodoo to emulate c++ class
|
||||||
change->client.lpVtbl = &sIMMDeviceEnumeratorVtbl_vtbl;
|
change->client.lpVtbl = &sIMMDeviceEnumeratorVtbl_vtbl;
|
||||||
|
|
||||||
/* register the change notification client */
|
// register the change notification client
|
||||||
hr = IMMDeviceEnumerator_RegisterEndpointNotificationCallback(
|
hr = IMMDeviceEnumerator_RegisterEndpointNotificationCallback(
|
||||||
state->pEnumerator, (IMMNotificationClient *)change);
|
state->pEnumerator, (IMMNotificationClient *)change);
|
||||||
EXIT_ON_ERROR(hr);
|
EXIT_ON_ERROR(hr);
|
||||||
|
|
||||||
/* so the callbacks can access the ao */
|
// so the callbacks can access the ao
|
||||||
change->ao = ao;
|
change->ao = ao;
|
||||||
|
|
||||||
/* whether or not this is the hotplug instance */
|
// whether or not this is the hotplug instance
|
||||||
change->is_hotplug = is_hotplug;
|
change->is_hotplug = is_hotplug;
|
||||||
|
|
||||||
if (is_hotplug) {
|
if (is_hotplug) {
|
||||||
MP_DBG(ao, "Monitoring for hotplug events\n");
|
MP_DBG(ao, "Monitoring for hotplug events\n");
|
||||||
} else {
|
} else {
|
||||||
/* Get the device string to compare with the pwstrDeviceId */
|
// Get the device string to compare with the pwstrDeviceId
|
||||||
hr = IMMDevice_GetId(state->pDevice, &change->monitored);
|
hr = IMMDevice_GetId(state->pDevice, &change->monitored);
|
||||||
EXIT_ON_ERROR(hr);
|
EXIT_ON_ERROR(hr);
|
||||||
MP_VERBOSE(ao, "Monitoring changes in device %S\n", change->monitored);
|
MP_VERBOSE(ao, "Monitoring changes in device %S\n", change->monitored);
|
||||||
|
@ -97,8 +97,8 @@ static const GUID *format_to_subtype(int format)
|
|||||||
return &KSDATAFORMAT_SUBTYPE_PCM;
|
return &KSDATAFORMAT_SUBTYPE_PCM;
|
||||||
}
|
}
|
||||||
|
|
||||||
// "solve" the under-determined inverse of format_to_subtype by
|
// "solve" the under-determined inverse of format_to_subtype by assuming the
|
||||||
// assuming the input subtype is "special" (i.e. IEC61937)
|
// input subtype is "special" (i.e. IEC61937)
|
||||||
static int special_subtype_to_format(const GUID *subtype) {
|
static int special_subtype_to_format(const GUID *subtype) {
|
||||||
for (int i = 0; wasapi_fmt_table[i].format; i++) {
|
for (int i = 0; wasapi_fmt_table[i].format; i++) {
|
||||||
if (IsEqualGUID(subtype, wasapi_fmt_table[i].subtype))
|
if (IsEqualGUID(subtype, wasapi_fmt_table[i].subtype))
|
||||||
@ -123,7 +123,8 @@ char *mp_PKEY_to_str_buf(char *buf, size_t buf_size, const PROPERTYKEY *pkey)
|
|||||||
{
|
{
|
||||||
buf = mp_GUID_to_str_buf(buf, buf_size, &pkey->fmtid);
|
buf = mp_GUID_to_str_buf(buf, buf_size, &pkey->fmtid);
|
||||||
size_t guid_len = strnlen(buf, buf_size);
|
size_t guid_len = strnlen(buf, buf_size);
|
||||||
snprintf(buf + guid_len, buf_size - guid_len, ",%"PRIu32, (uint32_t) pkey->pid);
|
snprintf(buf + guid_len, buf_size - guid_len, ",%"PRIu32,
|
||||||
|
(uint32_t) pkey->pid);
|
||||||
return buf;
|
return buf;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -204,15 +205,15 @@ static void set_waveformat(WAVEFORMATEXTENSIBLE *wformat,
|
|||||||
wformat->Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
|
wformat->Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
|
||||||
|
|
||||||
wformat->SubFormat = *format_to_subtype(format);
|
wformat->SubFormat = *format_to_subtype(format);
|
||||||
wformat->Samples.wValidBitsPerSample = valid_bits ? valid_bits : wformat->Format.wBitsPerSample;
|
wformat->Samples.wValidBitsPerSample =
|
||||||
|
valid_bits ? valid_bits : wformat->Format.wBitsPerSample;
|
||||||
wformat->dwChannelMask = mp_chmap_to_waveext(channels);
|
wformat->dwChannelMask = mp_chmap_to_waveext(channels);
|
||||||
update_waveformat_datarate(wformat);
|
update_waveformat_datarate(wformat);
|
||||||
}
|
}
|
||||||
|
|
||||||
// This implicitly transforms all pcm formats to:
|
// This implicitly transforms all pcm formats to: interleaved / signed (except
|
||||||
// interleaved / signed (except 8-bit is unsigned) / waveext channel order.
|
// 8-bit is unsigned) / waveext channel order. "Special" formats should be
|
||||||
// "Special" formats should be exempt as they should already
|
// exempt as they should already satisfy these properties.
|
||||||
// satisfy these properties.
|
|
||||||
static void set_waveformat_with_ao(WAVEFORMATEXTENSIBLE *wformat, struct ao *ao)
|
static void set_waveformat_with_ao(WAVEFORMATEXTENSIBLE *wformat, struct ao *ao)
|
||||||
{
|
{
|
||||||
struct mp_chmap channels = ao->channels;
|
struct mp_chmap channels = ao->channels;
|
||||||
@ -257,7 +258,8 @@ static int format_from_waveformat(WAVEFORMATEX *wf)
|
|||||||
WAVEFORMATEXTENSIBLE *wformat = (WAVEFORMATEXTENSIBLE *)wf;
|
WAVEFORMATEXTENSIBLE *wformat = (WAVEFORMATEXTENSIBLE *)wf;
|
||||||
if (IsEqualGUID(&wformat->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM)) {
|
if (IsEqualGUID(&wformat->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM)) {
|
||||||
format = wf->wBitsPerSample == 8 ? AF_FORMAT_U8 : AF_FORMAT_S32;
|
format = wf->wBitsPerSample == 8 ? AF_FORMAT_U8 : AF_FORMAT_S32;
|
||||||
} else if (IsEqualGUID(&wformat->SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT)) {
|
} else if (IsEqualGUID(&wformat->SubFormat,
|
||||||
|
&KSDATAFORMAT_SUBTYPE_IEEE_FLOAT)) {
|
||||||
format = AF_FORMAT_FLOAT;
|
format = AF_FORMAT_FLOAT;
|
||||||
} else {
|
} else {
|
||||||
format = special_subtype_to_format(&wformat->SubFormat);
|
format = special_subtype_to_format(&wformat->SubFormat);
|
||||||
@ -275,16 +277,17 @@ static int format_from_waveformat(WAVEFORMATEX *wf)
|
|||||||
}
|
}
|
||||||
// https://msdn.microsoft.com/en-us/library/windows/hardware/ff538802%28v=vs.85%29.aspx:
|
// https://msdn.microsoft.com/en-us/library/windows/hardware/ff538802%28v=vs.85%29.aspx:
|
||||||
// Since mpv doesn't have the notion of "valid bits", we just specify a
|
// Since mpv doesn't have the notion of "valid bits", we just specify a
|
||||||
// format with the container size. The least significant, "invalid"
|
// format with the container size. The least significant, "invalid" bits
|
||||||
// bits will be excess precision ignored by wasapi.
|
// will be excess precision ignored by wasapi. The change_bytes operations
|
||||||
// The change_bytes operations should be a no-op for properly
|
// should be a no-op for properly configured "special" formats, otherwise it
|
||||||
// configured "special" formats, otherwise it will return 0.
|
// will return 0.
|
||||||
if (wf->wBitsPerSample % 8)
|
if (wf->wBitsPerSample % 8)
|
||||||
return 0;
|
return 0;
|
||||||
return af_fmt_change_bytes(format, wf->wBitsPerSample / 8);
|
return af_fmt_change_bytes(format, wf->wBitsPerSample / 8);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool chmap_from_waveformat(struct mp_chmap *channels, const WAVEFORMATEX *wf)
|
static bool chmap_from_waveformat(struct mp_chmap *channels,
|
||||||
|
const WAVEFORMATEX *wf)
|
||||||
{
|
{
|
||||||
if (wf->wFormatTag == WAVE_FORMAT_EXTENSIBLE) {
|
if (wf->wFormatTag == WAVE_FORMAT_EXTENSIBLE) {
|
||||||
WAVEFORMATEXTENSIBLE *wformat = (WAVEFORMATEXTENSIBLE *)wf;
|
WAVEFORMATEXTENSIBLE *wformat = (WAVEFORMATEXTENSIBLE *)wf;
|
||||||
@ -312,7 +315,8 @@ static char *waveformat_to_str_buf(char *buf, size_t buf_size, WAVEFORMATEX *wf)
|
|||||||
snprintf(validstr, sizeof(validstr), " (%u valid)", valid_bits);
|
snprintf(validstr, sizeof(validstr), " (%u valid)", valid_bits);
|
||||||
|
|
||||||
snprintf(buf, buf_size, "%s %s%s @ %uhz",
|
snprintf(buf, buf_size, "%s %s%s @ %uhz",
|
||||||
mp_chmap_to_str(&channels), af_fmt_to_str(format_from_waveformat(wf)),
|
mp_chmap_to_str(&channels),
|
||||||
|
af_fmt_to_str(format_from_waveformat(wf)),
|
||||||
validstr, (unsigned) wf->nSamplesPerSec);
|
validstr, (unsigned) wf->nSamplesPerSec);
|
||||||
return buf;
|
return buf;
|
||||||
}
|
}
|
||||||
@ -327,7 +331,8 @@ static void waveformat_copy(WAVEFORMATEXTENSIBLE* dst, WAVEFORMATEX* src)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool set_ao_format(struct ao *ao, WAVEFORMATEX *wf, AUDCLNT_SHAREMODE share_mode)
|
static bool set_ao_format(struct ao *ao, WAVEFORMATEX *wf,
|
||||||
|
AUDCLNT_SHAREMODE share_mode)
|
||||||
{
|
{
|
||||||
struct wasapi_state *state = ao->priv;
|
struct wasapi_state *state = ao->priv;
|
||||||
int format = format_from_waveformat(wf);
|
int format = format_from_waveformat(wf);
|
||||||
@ -337,7 +342,8 @@ static bool set_ao_format(struct ao *ao, WAVEFORMATEX *wf, AUDCLNT_SHAREMODE sha
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Do not touch the ao for passthrough, just assume that we set WAVEFORMATEX correctly.
|
// Do not touch the ao for passthrough, just assume that we set WAVEFORMATEX
|
||||||
|
// correctly.
|
||||||
if (af_fmt_is_pcm(format)) {
|
if (af_fmt_is_pcm(format)) {
|
||||||
struct mp_chmap channels;
|
struct mp_chmap channels;
|
||||||
if (!chmap_from_waveformat(&channels, wf)) {
|
if (!chmap_from_waveformat(&channels, wf)) {
|
||||||
@ -357,7 +363,8 @@ static bool set_ao_format(struct ao *ao, WAVEFORMATEX *wf, AUDCLNT_SHAREMODE sha
|
|||||||
static bool try_format_exclusive(struct ao *ao, WAVEFORMATEXTENSIBLE *wformat)
|
static bool try_format_exclusive(struct ao *ao, WAVEFORMATEXTENSIBLE *wformat)
|
||||||
{
|
{
|
||||||
struct wasapi_state *state = ao->priv;
|
struct wasapi_state *state = ao->priv;
|
||||||
MP_VERBOSE(ao, "Trying %s (exclusive)\n", waveformat_to_str(&wformat->Format));
|
MP_VERBOSE(ao, "Trying %s (exclusive)\n",
|
||||||
|
waveformat_to_str(&wformat->Format));
|
||||||
HRESULT hr = IAudioClient_IsFormatSupported(state->pAudioClient,
|
HRESULT hr = IAudioClient_IsFormatSupported(state->pAudioClient,
|
||||||
AUDCLNT_SHAREMODE_EXCLUSIVE,
|
AUDCLNT_SHAREMODE_EXCLUSIVE,
|
||||||
&wformat->Format, NULL);
|
&wformat->Format, NULL);
|
||||||
@ -470,7 +477,8 @@ static bool search_channels(struct ao *ao, WAVEFORMATEXTENSIBLE *wformat)
|
|||||||
if (!wformat->Format.nSamplesPerSec) {
|
if (!wformat->Format.nSamplesPerSec) {
|
||||||
if (search_samplerates(ao, wformat, &entry)) {
|
if (search_samplerates(ao, wformat, &entry)) {
|
||||||
mp_chmap_sel_add_map(&chmap_sel, &entry);
|
mp_chmap_sel_add_map(&chmap_sel, &entry);
|
||||||
MP_VERBOSE(ao, "%s is supported\n", waveformat_to_str(&wformat->Format));
|
MP_VERBOSE(ao, "%s is supported\n",
|
||||||
|
waveformat_to_str(&wformat->Format));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
change_waveformat_channels(wformat, &entry);
|
change_waveformat_channels(wformat, &entry);
|
||||||
@ -494,8 +502,8 @@ static bool find_formats_exclusive(struct ao *ao, bool do_search)
|
|||||||
WAVEFORMATEXTENSIBLE wformat;
|
WAVEFORMATEXTENSIBLE wformat;
|
||||||
set_waveformat_with_ao(&wformat, ao);
|
set_waveformat_with_ao(&wformat, ao);
|
||||||
|
|
||||||
// Try the requested format as is. If that doesn't work, and the
|
// Try the requested format as is. If that doesn't work, and the do_search
|
||||||
// do_search argument is set, do the pcm format search.
|
// argument is set, do the pcm format search.
|
||||||
if (!try_format_exclusive_with_spdif_fallback(ao, &wformat) &&
|
if (!try_format_exclusive_with_spdif_fallback(ao, &wformat) &&
|
||||||
(!do_search || !search_channels(ao, &wformat)))
|
(!do_search || !search_channels(ao, &wformat)))
|
||||||
return false;
|
return false;
|
||||||
@ -550,7 +558,8 @@ static bool find_formats_shared(struct ao *ao)
|
|||||||
af_fmt_to_str(ao->format), ao->samplerate);
|
af_fmt_to_str(ao->format), ao->samplerate);
|
||||||
return true;
|
return true;
|
||||||
exit_label:
|
exit_label:
|
||||||
MP_ERR(state, "Error finding shared mode format: %s\n", mp_HRESULT_to_str(hr));
|
MP_ERR(state, "Error finding shared mode format: %s\n",
|
||||||
|
mp_HRESULT_to_str(hr));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -587,7 +596,8 @@ static HRESULT init_clock(struct wasapi_state *state) {
|
|||||||
|
|
||||||
atomic_store(&state->sample_count, 0);
|
atomic_store(&state->sample_count, 0);
|
||||||
|
|
||||||
MP_VERBOSE(state, "IAudioClock::GetFrequency gave a frequency of %"PRIu64".\n",
|
MP_VERBOSE(state,
|
||||||
|
"IAudioClock::GetFrequency gave a frequency of %"PRIu64".\n",
|
||||||
(uint64_t) state->clock_frequency);
|
(uint64_t) state->clock_frequency);
|
||||||
|
|
||||||
return S_OK;
|
return S_OK;
|
||||||
@ -608,7 +618,8 @@ static HRESULT init_session_display(struct wasapi_state *state) {
|
|||||||
GetModuleFileNameW(NULL, path, MAX_PATH);
|
GetModuleFileNameW(NULL, path, MAX_PATH);
|
||||||
lstrcatW(path, L",-IDI_ICON1");
|
lstrcatW(path, L",-IDI_ICON1");
|
||||||
|
|
||||||
hr = IAudioSessionControl_SetDisplayName(state->pSessionControl, MIXER_DEFAULT_LABEL, NULL);
|
hr = IAudioSessionControl_SetDisplayName(state->pSessionControl,
|
||||||
|
MIXER_DEFAULT_LABEL, NULL);
|
||||||
EXIT_ON_ERROR(hr);
|
EXIT_ON_ERROR(hr);
|
||||||
hr = IAudioSessionControl_SetIconPath(state->pSessionControl, path, NULL);
|
hr = IAudioSessionControl_SetIconPath(state->pSessionControl, path, NULL);
|
||||||
EXIT_ON_ERROR(hr);
|
EXIT_ON_ERROR(hr);
|
||||||
@ -626,15 +637,18 @@ static HRESULT fix_format(struct ao *ao)
|
|||||||
|
|
||||||
REFERENCE_TIME devicePeriod, bufferDuration, bufferPeriod;
|
REFERENCE_TIME devicePeriod, bufferDuration, bufferPeriod;
|
||||||
MP_DBG(state, "IAudioClient::GetDevicePeriod\n");
|
MP_DBG(state, "IAudioClient::GetDevicePeriod\n");
|
||||||
HRESULT hr = IAudioClient_GetDevicePeriod(state->pAudioClient,&devicePeriod, NULL);
|
HRESULT hr = IAudioClient_GetDevicePeriod(state->pAudioClient,&devicePeriod,
|
||||||
MP_VERBOSE(state, "Device period: %.2g ms\n", (double) devicePeriod / 10000.0 );
|
NULL);
|
||||||
|
MP_VERBOSE(state, "Device period: %.2g ms\n",
|
||||||
|
(double) devicePeriod / 10000.0 );
|
||||||
|
|
||||||
/* integer multiple of device period close to 50ms */
|
// integer multiple of device period close to 50ms
|
||||||
bufferPeriod = bufferDuration = ceil(50.0 * 10000.0 / devicePeriod) * devicePeriod;
|
bufferPeriod = bufferDuration =
|
||||||
|
ceil(50.0 * 10000.0 / devicePeriod) * devicePeriod;
|
||||||
|
|
||||||
/* handle unsupported buffer size */
|
// handle unsupported buffer size hopefully this shouldn't happen because of
|
||||||
/* hopefully this shouldn't happen because of the above integer device period */
|
// the above integer device period
|
||||||
/* http://msdn.microsoft.com/en-us/library/windows/desktop/dd370875%28v=vs.85%29.aspx */
|
// http://msdn.microsoft.com/en-us/library/windows/desktop/dd370875%28v=vs.85%29.aspx
|
||||||
int retries=0;
|
int retries=0;
|
||||||
reinit:
|
reinit:
|
||||||
if (state->share_mode == AUDCLNT_SHAREMODE_SHARED)
|
if (state->share_mode == AUDCLNT_SHAREMODE_SHARED)
|
||||||
@ -648,20 +662,22 @@ reinit:
|
|||||||
bufferPeriod,
|
bufferPeriod,
|
||||||
&(state->format.Format),
|
&(state->format.Format),
|
||||||
NULL);
|
NULL);
|
||||||
/* something about buffer sizes on Win7 */
|
// something about buffer sizes on Win7
|
||||||
if (hr == AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED) {
|
if (hr == AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED) {
|
||||||
if (retries > 0) {
|
if (retries > 0) {
|
||||||
EXIT_ON_ERROR(hr);
|
EXIT_ON_ERROR(hr);
|
||||||
} else {
|
} else {
|
||||||
retries ++;
|
retries ++;
|
||||||
}
|
}
|
||||||
MP_VERBOSE(state, "IAudioClient::Initialize negotiation failed with %s, used %lld * 100ns\n",
|
MP_VERBOSE(state, "IAudioClient::Initialize negotiation failed with %s,"
|
||||||
|
"used %lld * 100ns\n",
|
||||||
mp_HRESULT_to_str(hr), bufferDuration);
|
mp_HRESULT_to_str(hr), bufferDuration);
|
||||||
|
|
||||||
IAudioClient_GetBufferSize(state->pAudioClient, &state->bufferFrameCount);
|
IAudioClient_GetBufferSize(state->pAudioClient,
|
||||||
bufferPeriod = bufferDuration =
|
&state->bufferFrameCount);
|
||||||
(REFERENCE_TIME) ((10000.0 * 1000 / state->format.Format.nSamplesPerSec *
|
bufferPeriod = bufferDuration = (REFERENCE_TIME) (0.5 +
|
||||||
state->bufferFrameCount) + 0.5);
|
(10000.0 * 1000 / state->format.Format.nSamplesPerSec
|
||||||
|
* state->bufferFrameCount));
|
||||||
|
|
||||||
IAudioClient_Release(state->pAudioClient);
|
IAudioClient_Release(state->pAudioClient);
|
||||||
state->pAudioClient = NULL;
|
state->pAudioClient = NULL;
|
||||||
@ -694,9 +710,9 @@ reinit:
|
|||||||
EXIT_ON_ERROR(hr);
|
EXIT_ON_ERROR(hr);
|
||||||
|
|
||||||
ao->device_buffer = state->bufferFrameCount;
|
ao->device_buffer = state->bufferFrameCount;
|
||||||
bufferDuration =
|
bufferDuration = (REFERENCE_TIME) (0.5 +
|
||||||
(REFERENCE_TIME) ((10000.0 * 1000 / state->format.Format.nSamplesPerSec *
|
(10000.0 * 1000 / state->format.Format.nSamplesPerSec
|
||||||
state->bufferFrameCount) + 0.5);
|
* state->bufferFrameCount));
|
||||||
MP_VERBOSE(state, "Buffer frame count: %"PRIu32" (%.2g ms)\n",
|
MP_VERBOSE(state, "Buffer frame count: %"PRIu32" (%.2g ms)\n",
|
||||||
state->bufferFrameCount, (double) bufferDuration / 10000.0 );
|
state->bufferFrameCount, (double) bufferDuration / 10000.0 );
|
||||||
|
|
||||||
@ -731,7 +747,8 @@ static char* get_device_id(IMMDevice *pDevice) {
|
|||||||
idstr = mp_to_utf8(NULL, devid);
|
idstr = mp_to_utf8(NULL, devid);
|
||||||
|
|
||||||
if (strstr(idstr, "{0.0.0.00000000}.")) {
|
if (strstr(idstr, "{0.0.0.00000000}.")) {
|
||||||
char *stripped = talloc_strdup(NULL, idstr + strlen("{0.0.0.00000000}."));
|
char *stripped =
|
||||||
|
talloc_strdup(NULL, idstr + strlen("{0.0.0.00000000}."));
|
||||||
talloc_free(idstr);
|
talloc_free(idstr);
|
||||||
idstr = stripped;
|
idstr = stripped;
|
||||||
}
|
}
|
||||||
@ -754,7 +771,8 @@ static char* get_device_name(IMMDevice *pDevice) {
|
|||||||
PROPVARIANT devname;
|
PROPVARIANT devname;
|
||||||
PropVariantInit(&devname);
|
PropVariantInit(&devname);
|
||||||
|
|
||||||
hr = IPropertyStore_GetValue(pProps, &mp_PKEY_Device_FriendlyName, &devname);
|
hr = IPropertyStore_GetValue(pProps, &mp_PKEY_Device_FriendlyName,
|
||||||
|
&devname);
|
||||||
EXIT_ON_ERROR(hr);
|
EXIT_ON_ERROR(hr);
|
||||||
|
|
||||||
namestr = mp_to_utf8(NULL, devname.pwszVal);
|
namestr = mp_to_utf8(NULL, devname.pwszVal);
|
||||||
@ -816,7 +834,8 @@ void wasapi_list_devs(struct ao *ao, struct ao_device_list *list)
|
|||||||
IMMDevice *pDevice = NULL;
|
IMMDevice *pDevice = NULL;
|
||||||
char *name = NULL, *id = NULL;
|
char *name = NULL, *id = NULL;
|
||||||
|
|
||||||
HRESULT hr = IMMDeviceEnumerator_EnumAudioEndpoints(state->pEnumerator, eRender,
|
HRESULT hr =
|
||||||
|
IMMDeviceEnumerator_EnumAudioEndpoints(state->pEnumerator, eRender,
|
||||||
DEVICE_STATE_ACTIVE, &pDevices);
|
DEVICE_STATE_ACTIVE, &pDevices);
|
||||||
EXIT_ON_ERROR(hr);
|
EXIT_ON_ERROR(hr);
|
||||||
|
|
||||||
@ -856,10 +875,12 @@ exit_label:
|
|||||||
SAFE_RELEASE(pDevices, IMMDeviceCollection_Release(pDevices));
|
SAFE_RELEASE(pDevices, IMMDeviceCollection_Release(pDevices));
|
||||||
}
|
}
|
||||||
|
|
||||||
static HRESULT load_default_device(struct ao *ao, IMMDeviceEnumerator* pEnumerator,
|
static HRESULT load_default_device(struct ao *ao,
|
||||||
|
IMMDeviceEnumerator* pEnumerator,
|
||||||
IMMDevice **ppDevice)
|
IMMDevice **ppDevice)
|
||||||
{
|
{
|
||||||
HRESULT hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint(pEnumerator,
|
HRESULT hr =
|
||||||
|
IMMDeviceEnumerator_GetDefaultAudioEndpoint(pEnumerator,
|
||||||
eRender, eMultimedia,
|
eRender, eMultimedia,
|
||||||
ppDevice);
|
ppDevice);
|
||||||
EXIT_ON_ERROR(hr);
|
EXIT_ON_ERROR(hr);
|
||||||
@ -874,7 +895,8 @@ exit_label:
|
|||||||
return hr;
|
return hr;
|
||||||
}
|
}
|
||||||
|
|
||||||
static HRESULT find_and_load_device(struct ao *ao, IMMDeviceEnumerator* pEnumerator,
|
static HRESULT find_and_load_device(struct ao *ao,
|
||||||
|
IMMDeviceEnumerator* pEnumerator,
|
||||||
IMMDevice **ppDevice, char *search)
|
IMMDevice **ppDevice, char *search)
|
||||||
{
|
{
|
||||||
HRESULT hr;
|
HRESULT hr;
|
||||||
@ -893,7 +915,8 @@ static HRESULT find_and_load_device(struct ao *ao, IMMDeviceEnumerator* pEnumera
|
|||||||
|
|
||||||
if (devid == NULL) {
|
if (devid == NULL) {
|
||||||
hr = IMMDeviceEnumerator_EnumAudioEndpoints(pEnumerator, eRender,
|
hr = IMMDeviceEnumerator_EnumAudioEndpoints(pEnumerator, eRender,
|
||||||
DEVICE_STATE_ACTIVE, &pDevices);
|
DEVICE_STATE_ACTIVE,
|
||||||
|
&pDevices);
|
||||||
EXIT_ON_ERROR(hr);
|
EXIT_ON_ERROR(hr);
|
||||||
|
|
||||||
int count;
|
int count;
|
||||||
@ -913,7 +936,8 @@ static HRESULT find_and_load_device(struct ao *ao, IMMDeviceEnumerator* pEnumera
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
hr = IMMDeviceEnumerator_EnumAudioEndpoints(pEnumerator, eRender,
|
hr = IMMDeviceEnumerator_EnumAudioEndpoints(pEnumerator, eRender,
|
||||||
DEVICE_STATE_ACTIVE|DEVICE_STATE_UNPLUGGED,
|
DEVICE_STATE_ACTIVE
|
||||||
|
| DEVICE_STATE_UNPLUGGED,
|
||||||
&pDevices);
|
&pDevices);
|
||||||
EXIT_ON_ERROR(hr);
|
EXIT_ON_ERROR(hr);
|
||||||
|
|
||||||
@ -991,9 +1015,12 @@ HRESULT wasapi_setup_proxies(struct wasapi_state *state) {
|
|||||||
EXIT_ON_ERROR(hr); \
|
EXIT_ON_ERROR(hr); \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
UNMARSHAL(IID_ISimpleAudioVolume, state->pAudioVolumeProxy, state->sAudioVolume);
|
UNMARSHAL(IID_ISimpleAudioVolume, state->pAudioVolumeProxy,
|
||||||
UNMARSHAL(IID_IAudioEndpointVolume, state->pEndpointVolumeProxy, state->sEndpointVolume);
|
state->sAudioVolume);
|
||||||
UNMARSHAL(IID_IAudioSessionControl, state->pSessionControlProxy, state->sSessionControl);
|
UNMARSHAL(IID_IAudioEndpointVolume, state->pEndpointVolumeProxy,
|
||||||
|
state->sEndpointVolume);
|
||||||
|
UNMARSHAL(IID_IAudioSessionControl, state->pSessionControlProxy,
|
||||||
|
state->sSessionControl);
|
||||||
|
|
||||||
#undef UNMARSHAL
|
#undef UNMARSHAL
|
||||||
|
|
||||||
@ -1004,9 +1031,12 @@ exit_label:
|
|||||||
}
|
}
|
||||||
|
|
||||||
void wasapi_release_proxies(wasapi_state *state) {
|
void wasapi_release_proxies(wasapi_state *state) {
|
||||||
SAFE_RELEASE(state->pAudioVolumeProxy, ISimpleAudioVolume_Release(state->pAudioVolumeProxy));
|
SAFE_RELEASE(state->pAudioVolumeProxy,
|
||||||
SAFE_RELEASE(state->pEndpointVolumeProxy, IAudioEndpointVolume_Release(state->pEndpointVolumeProxy));
|
ISimpleAudioVolume_Release(state->pAudioVolumeProxy));
|
||||||
SAFE_RELEASE(state->pSessionControlProxy, IAudioSessionControl_Release(state->pSessionControlProxy));
|
SAFE_RELEASE(state->pEndpointVolumeProxy,
|
||||||
|
IAudioEndpointVolume_Release(state->pEndpointVolumeProxy));
|
||||||
|
SAFE_RELEASE(state->pSessionControlProxy,
|
||||||
|
IAudioSessionControl_Release(state->pSessionControlProxy));
|
||||||
}
|
}
|
||||||
|
|
||||||
static HRESULT create_proxies(struct wasapi_state *state) {
|
static HRESULT create_proxies(struct wasapi_state *state) {
|
||||||
@ -1021,9 +1051,12 @@ static HRESULT create_proxies(struct wasapi_state *state) {
|
|||||||
EXIT_ON_ERROR(hr); \
|
EXIT_ON_ERROR(hr); \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
MARSHAL(IID_ISimpleAudioVolume, state->sAudioVolume, state->pAudioVolume);
|
MARSHAL(IID_ISimpleAudioVolume, state->sAudioVolume,
|
||||||
MARSHAL(IID_IAudioEndpointVolume, state->sEndpointVolume, state->pEndpointVolume);
|
state->pAudioVolume);
|
||||||
MARSHAL(IID_IAudioSessionControl, state->sSessionControl, state->pSessionControl);
|
MARSHAL(IID_IAudioEndpointVolume, state->sEndpointVolume,
|
||||||
|
state->pEndpointVolume);
|
||||||
|
MARSHAL(IID_IAudioSessionControl, state->sSessionControl,
|
||||||
|
state->pSessionControl);
|
||||||
|
|
||||||
return S_OK;
|
return S_OK;
|
||||||
exit_label:
|
exit_label:
|
||||||
@ -1032,15 +1065,18 @@ exit_label:
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void destroy_proxies(struct wasapi_state *state) {
|
static void destroy_proxies(struct wasapi_state *state) {
|
||||||
SAFE_RELEASE(state->sAudioVolume, IStream_Release(state->sAudioVolume));
|
SAFE_RELEASE(state->sAudioVolume,
|
||||||
SAFE_RELEASE(state->sEndpointVolume, IStream_Release(state->sEndpointVolume));
|
IStream_Release(state->sAudioVolume));
|
||||||
SAFE_RELEASE(state->sSessionControl, IStream_Release(state->sSessionControl));
|
SAFE_RELEASE(state->sEndpointVolume,
|
||||||
|
IStream_Release(state->sEndpointVolume));
|
||||||
|
SAFE_RELEASE(state->sSessionControl,
|
||||||
|
IStream_Release(state->sSessionControl));
|
||||||
}
|
}
|
||||||
|
|
||||||
void wasapi_dispatch(struct ao *ao)
|
void wasapi_dispatch(struct ao *ao)
|
||||||
{
|
{
|
||||||
MP_DBG(ao, "Dispatch\n");
|
MP_DBG(ao, "Dispatch\n");
|
||||||
/* dispatch any possible pending messages */
|
// dispatch any possible pending messages
|
||||||
MSG msg;
|
MSG msg;
|
||||||
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
|
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
|
||||||
DispatchMessage(&msg);
|
DispatchMessage(&msg);
|
||||||
@ -1054,7 +1090,8 @@ HRESULT wasapi_thread_init(struct ao *ao)
|
|||||||
retry: ;
|
retry: ;
|
||||||
|
|
||||||
HRESULT hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL,
|
HRESULT hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL,
|
||||||
&IID_IMMDeviceEnumerator, (void **)&state->pEnumerator);
|
&IID_IMMDeviceEnumerator,
|
||||||
|
(void **)&state->pEnumerator);
|
||||||
EXIT_ON_ERROR(hr);
|
EXIT_ON_ERROR(hr);
|
||||||
|
|
||||||
char *device = state->opt_device;
|
char *device = state->opt_device;
|
||||||
@ -1065,7 +1102,8 @@ retry: ;
|
|||||||
if (!device || !device[0]) {
|
if (!device || !device[0]) {
|
||||||
hr = load_default_device(ao, state->pEnumerator, &state->pDevice);
|
hr = load_default_device(ao, state->pEnumerator, &state->pDevice);
|
||||||
} else {
|
} else {
|
||||||
hr = find_and_load_device(ao, state->pEnumerator, &state->pDevice, device);
|
hr = find_and_load_device(ao, state->pEnumerator, &state->pDevice,
|
||||||
|
device);
|
||||||
}
|
}
|
||||||
EXIT_ON_ERROR(hr);
|
EXIT_ON_ERROR(hr);
|
||||||
|
|
||||||
@ -1075,7 +1113,8 @@ retry: ;
|
|||||||
|
|
||||||
MP_DBG(ao, "Activating pAudioClient interface\n");
|
MP_DBG(ao, "Activating pAudioClient interface\n");
|
||||||
hr = IMMDeviceActivator_Activate(state->pDevice, &IID_IAudioClient,
|
hr = IMMDeviceActivator_Activate(state->pDevice, &IID_IAudioClient,
|
||||||
CLSCTX_ALL, NULL, (void **)&state->pAudioClient);
|
CLSCTX_ALL, NULL,
|
||||||
|
(void **)&state->pAudioClient);
|
||||||
EXIT_ON_ERROR(hr);
|
EXIT_ON_ERROR(hr);
|
||||||
|
|
||||||
MP_DBG(ao, "Activating pEndpointVolume interface\n");
|
MP_DBG(ao, "Activating pEndpointVolume interface\n");
|
||||||
|
Loading…
Reference in New Issue
Block a user