diff options
Diffstat (limited to 'audio/out/ao_wasapi_utils.c')
-rw-r--r-- | audio/out/ao_wasapi_utils.c | 157 |
1 files changed, 40 insertions, 117 deletions
diff --git a/audio/out/ao_wasapi_utils.c b/audio/out/ao_wasapi_utils.c index d29c6bed7d..8d0ea30bd7 100644 --- a/audio/out/ao_wasapi_utils.c +++ b/audio/out/ao_wasapi_utils.c @@ -3,18 +3,18 @@ * * Original author: Jonathan Yong <10walls@gmail.com> * - * mpv is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. + * mpv is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. * * mpv is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * GNU Lesser General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with mpv. If not, see <http://www.gnu.org/licenses/>. + * You should have received a copy of the GNU Lesser General Public + * License along with mpv. If not, see <http://www.gnu.org/licenses/>. */ #include <math.h> @@ -29,6 +29,7 @@ #include "audio/format.h" #include "osdep/timer.h" #include "osdep/io.h" +#include "osdep/strnlen.h" #include "ao_wasapi.h" #define MIXER_DEFAULT_LABEL L"mpv - video player" @@ -297,7 +298,7 @@ static bool try_format_exclusive(struct ao *ao, WAVEFORMATEXTENSIBLE *wformat) if (hr != AUDCLNT_E_UNSUPPORTED_FORMAT) EXIT_ON_ERROR(hr); - return hr == S_OK; + return SUCCEEDED(hr); exit_label: MP_ERR(state, "Error testing exclusive format: %s\n", mp_HRESULT_to_str(hr)); return false; @@ -606,18 +607,20 @@ static HRESULT fix_format(struct ao *ao) MP_VERBOSE(state, "Device period: %.2g ms\n", (double) devicePeriod / 10000.0 ); - // integer multiple of device period close to 50ms - bufferPeriod = bufferDuration = - ceil(50.0 * 10000.0 / devicePeriod) * devicePeriod; + if (state->share_mode == AUDCLNT_SHAREMODE_SHARED) { + // for shared mode, use integer multiple of device period close to 50ms + bufferDuration = devicePeriod * ceil(50.0 * 10000.0 / devicePeriod); + bufferPeriod = 0; + } else { + // in exclusive mode, these should all be the same + bufferPeriod = bufferDuration = devicePeriod; + } // handle unsupported buffer size hopefully this shouldn't happen because of // the above integer device period // http://msdn.microsoft.com/en-us/library/windows/desktop/dd370875%28v=vs.85%29.aspx int retries=0; reinit: - if (state->share_mode == AUDCLNT_SHAREMODE_SHARED) - bufferPeriod = 0; - MP_DBG(state, "IAudioClient::Initialize\n"); hr = IAudioClient_Initialize(state->pAudioClient, state->share_mode, @@ -639,9 +642,12 @@ reinit: IAudioClient_GetBufferSize(state->pAudioClient, &state->bufferFrameCount); - bufferPeriod = bufferDuration = (REFERENCE_TIME) (0.5 + + bufferDuration = (REFERENCE_TIME) (0.5 + (10000.0 * 1000 / state->format.Format.nSamplesPerSec * state->bufferFrameCount)); + if (state->share_mode == AUDCLNT_SHAREMODE_EXCLUSIVE) + bufferPeriod = bufferDuration; + IAudioClient_Release(state->pAudioClient); state->pAudioClient = NULL; @@ -749,7 +755,7 @@ struct enumerator { struct mp_log *log; IMMDeviceEnumerator *pEnumerator; IMMDeviceCollection *pDevices; - int count; + UINT count; }; static void destroy_enumerator(struct enumerator *e) @@ -784,7 +790,7 @@ exit_label: return NULL; } -static struct device_desc *device_desc_for_num(struct enumerator *e, int i) +static struct device_desc *device_desc_for_num(struct enumerator *e, UINT i) { IMMDevice *pDevice = NULL; HRESULT hr = IMMDeviceCollection_Item(e->pDevices, i, &pDevice); @@ -818,7 +824,7 @@ void wasapi_list_devs(struct ao *ao, struct ao_device_list *list) if (!enumerator) return; - for (int i = 0; i < enumerator->count; i++) { + for (UINT i = 0; i < enumerator->count; i++) { struct device_desc *d = device_desc_for_num(enumerator, i); if (!d) goto exit_label; @@ -858,14 +864,19 @@ static LPWSTR select_device(struct mp_log *l, struct device_desc *d) (wcslen(d->deviceID) + 1) * sizeof(wchar_t)); } -LPWSTR find_deviceID(struct ao *ao) +bstr wasapi_get_specified_device_string(struct ao *ao) { - LPWSTR deviceID = NULL; struct wasapi_state *state = ao->priv; bstr device = bstr_strip(bstr0(state->opt_device)); if (!device.len) device = bstr_strip(bstr0(ao->device)); + return device; +} +LPWSTR wasapi_find_deviceID(struct ao *ao) +{ + LPWSTR deviceID = NULL; + bstr device = wasapi_get_specified_device_string(ao); MP_DBG(ao, "Find device \'%.*s\'\n", BSTR_P(device)); struct device_desc *d = NULL; @@ -873,6 +884,11 @@ LPWSTR find_deviceID(struct ao *ao) if (!enumerator) goto exit_label; + if (!enumerator->count) { + MP_ERR(ao, "There are no playback devices available\n"); + goto exit_label; + } + if (!device.len) { MP_VERBOSE(ao, "No device specified. Selecting default.\n"); d = default_device_desc(enumerator); @@ -883,7 +899,7 @@ LPWSTR find_deviceID(struct ao *ao) // try selecting by number bstr rest; long long devno = bstrtoll(device, &rest, 10); - if (!rest.len && 0 <= devno && devno < enumerator->count) { + if (!rest.len && 0 <= devno && devno < (long long)enumerator->count) { MP_VERBOSE(ao, "Selecting device by number: #%lld\n", devno); d = device_desc_for_num(enumerator, devno); deviceID = select_device(ao->log, d); @@ -892,7 +908,7 @@ LPWSTR find_deviceID(struct ao *ao) // select by id or name bstr_eatstart0(&device, "{0.0.0.00000000}."); - for (int i = 0; i < enumerator->count; i++) { + for (UINT i = 0; i < enumerator->count; i++) { d = device_desc_for_num(enumerator, i); if (!d) goto exit_label; @@ -904,7 +920,7 @@ LPWSTR find_deviceID(struct ao *ao) } if (bstrcmp(device, bstr_strip(bstr0(d->name))) == 0) { - if (!state->deviceID) { + if (!deviceID) { MP_VERBOSE(ao, "Selecting device by name: \'%.*s\'\n", BSTR_P(device)); deviceID = select_device(ao->log, d); } else { @@ -925,93 +941,6 @@ exit_label: return deviceID; } -static void *unmarshal(struct wasapi_state *state, REFIID type, IStream **from) -{ - if (!*from) - return NULL; - void *to_proxy = NULL; - HRESULT hr = CoGetInterfaceAndReleaseStream(*from, type, &to_proxy); - *from = NULL; // the stream is released even on failure - EXIT_ON_ERROR(hr); - return to_proxy; -exit_label: - MP_WARN(state, "Error reading COM proxy: %s\n", mp_HRESULT_to_str(hr)); - return to_proxy; -} - -void wasapi_receive_proxies(struct wasapi_state *state) { - state->pAudioVolumeProxy = unmarshal(state, &IID_ISimpleAudioVolume, - &state->sAudioVolume); - state->pEndpointVolumeProxy = unmarshal(state, &IID_IAudioEndpointVolume, - &state->sEndpointVolume); - state->pSessionControlProxy = unmarshal(state, &IID_IAudioSessionControl, - &state->sSessionControl); -} - -void wasapi_release_proxies(wasapi_state *state) { - SAFE_RELEASE(state->pAudioVolumeProxy, - ISimpleAudioVolume_Release(state->pAudioVolumeProxy)); - SAFE_RELEASE(state->pEndpointVolumeProxy, - IAudioEndpointVolume_Release(state->pEndpointVolumeProxy)); - SAFE_RELEASE(state->pSessionControlProxy, - IAudioSessionControl_Release(state->pSessionControlProxy)); -} - -// Must call CoReleaseMarshalData to decrement marshalled object's reference -// count. -#define SAFE_RELEASE_INTERFACE_STREAM(stream) do { \ - if ((stream) != NULL) { \ - CoReleaseMarshalData((stream)); \ - IStream_Release((stream)); \ - (stream) = NULL; \ - } \ - } while(0) - -static IStream *marshal(struct wasapi_state *state, - REFIID type, void *from_obj) -{ - if (!from_obj) - return NULL; - IStream *to; - HRESULT hr = CreateStreamOnHGlobal(NULL, TRUE, &to); - EXIT_ON_ERROR(hr); - hr = CoMarshalInterThreadInterfaceInStream(type, (IUnknown *)from_obj, &to); - EXIT_ON_ERROR(hr); - return to; -exit_label: - SAFE_RELEASE_INTERFACE_STREAM(to); - MP_WARN(state, "Error creating COM proxy stream: %s\n", - mp_HRESULT_to_str(hr)); - return to; -} - -static void create_proxy_streams(struct wasapi_state *state) { - state->sAudioVolume = marshal(state, &IID_ISimpleAudioVolume, - state->pAudioVolume); - state->sEndpointVolume = marshal(state, &IID_IAudioEndpointVolume, - state->pEndpointVolume); - state->sSessionControl = marshal(state, &IID_IAudioSessionControl, - state->pSessionControl); -} - -static void destroy_proxy_streams(struct wasapi_state *state) { - // This is only to handle error conditions. - // During normal operation, these will already have been released by - // unmarshaling. - SAFE_RELEASE_INTERFACE_STREAM(state->sAudioVolume); - SAFE_RELEASE_INTERFACE_STREAM(state->sEndpointVolume); - SAFE_RELEASE_INTERFACE_STREAM(state->sSessionControl); -} - -void wasapi_dispatch(struct ao *ao) -{ - MP_DBG(ao, "Dispatch\n"); - // dispatch any possible pending messages - MSG msg; - while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) - DispatchMessage(&msg); -} - HRESULT wasapi_thread_init(struct ao *ao) { struct wasapi_state *state = ao->priv; @@ -1046,13 +975,10 @@ retry: ; } EXIT_ON_ERROR(hr); - MP_DBG(ao, "Creating proxies\n"); - create_proxy_streams(state); - MP_DBG(ao, "Init wasapi thread done\n"); return S_OK; exit_label: - MP_ERR(state, "Error setting up audio thread: %s\n", mp_HRESULT_to_str(hr)); + MP_FATAL(state, "Error setting up audio thread: %s\n", mp_HRESULT_to_str(hr)); return hr; } @@ -1060,13 +986,10 @@ void wasapi_thread_uninit(struct ao *ao) { struct wasapi_state *state = ao->priv; MP_DBG(ao, "Thread shutdown\n"); - wasapi_dispatch(ao); if (state->pAudioClient) IAudioClient_Stop(state->pAudioClient); - destroy_proxy_streams(state); - SAFE_RELEASE(state->pRenderClient, IAudioRenderClient_Release(state->pRenderClient)); SAFE_RELEASE(state->pAudioClock, IAudioClock_Release(state->pAudioClock)); SAFE_RELEASE(state->pAudioVolume, ISimpleAudioVolume_Release(state->pAudioVolume)); |