diff options
Diffstat (limited to 'audio/out/ao_wasapi_utils.c')
-rw-r--r-- | audio/out/ao_wasapi_utils.c | 162 |
1 files changed, 103 insertions, 59 deletions
diff --git a/audio/out/ao_wasapi_utils.c b/audio/out/ao_wasapi_utils.c index a696040dba..2614872b91 100644 --- a/audio/out/ao_wasapi_utils.c +++ b/audio/out/ao_wasapi_utils.c @@ -18,12 +18,15 @@ */ #include <math.h> -#include <wchar.h> + #include <windows.h> -#include <errors.h> +#include <mmsystem.h> +#include <mmreg.h> #include <ksguid.h> #include <ksmedia.h> #include <avrt.h> +#include <propsys.h> +#include <functiondiscoverykeys_devpkey.h> #include "audio/format.h" #include "osdep/timer.h" @@ -31,38 +34,47 @@ #include "osdep/strnlen.h" #include "ao_wasapi.h" -#define MIXER_DEFAULT_LABEL L"mpv - video player" - -DEFINE_PROPERTYKEY(mp_PKEY_Device_FriendlyName, - 0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, - 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 14); -DEFINE_PROPERTYKEY(mp_PKEY_Device_DeviceDesc, - 0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, - 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 2); -// CEA 861 subformats -// should work on vista -DEFINE_GUID(mp_KSDATAFORMAT_SUBTYPE_IEC61937_DTS, - 0x00000008, 0x0000, 0x0010, 0x80, 0x00, +#ifndef KSDATAFORMAT_SUBTYPE_IEC61937_DTS +DEFINE_GUID(KSDATAFORMAT_SUBTYPE_IEC61937_DTS, + WAVE_FORMAT_DTS, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71); -DEFINE_GUID(mp_KSDATAFORMAT_SUBTYPE_IEC61937_DOLBY_DIGITAL, - 0x00000092, 0x0000, 0x0010, 0x80, 0x00, +#endif + +#ifndef KSDATAFORMAT_SUBTYPE_IEC61937_DOLBY_DIGITAL +DEFINE_GUID(KSDATAFORMAT_SUBTYPE_IEC61937_DOLBY_DIGITAL, + WAVE_FORMAT_DOLBY_AC3_SPDIF, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71); -// might require 7+ -DEFINE_GUID(mp_KSDATAFORMAT_SUBTYPE_IEC61937_AAC, +#endif + +#ifndef KSDATAFORMAT_SUBTYPE_IEC61937_AAC +DEFINE_GUID(KSDATAFORMAT_SUBTYPE_IEC61937_AAC, 0x00000006, 0x0cea, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71); -DEFINE_GUID(mp_KSDATAFORMAT_SUBTYPE_IEC61937_MPEG3, - 0x00000004, 0x0cea, 0x0010, 0x80, 0x00, +#endif + +#ifndef KSDATAFORMAT_SUBTYPE_IEC61937_MPEG3 +DEFINE_GUID(KSDATAFORMAT_SUBTYPE_IEC61937_MPEG3, + 0x00000005, 0x0cea, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71); -DEFINE_GUID(mp_KSDATAFORMAT_SUBTYPE_IEC61937_DOLBY_DIGITAL_PLUS, +#endif + +#ifndef KSDATAFORMAT_SUBTYPE_IEC61937_DOLBY_DIGITAL_PLUS +DEFINE_GUID(KSDATAFORMAT_SUBTYPE_IEC61937_DOLBY_DIGITAL_PLUS, 0x0000000a, 0x0cea, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71); -DEFINE_GUID(mp_KSDATAFORMAT_SUBTYPE_IEC61937_DTS_HD, +#endif + +#ifndef KSDATAFORMAT_SUBTYPE_IEC61937_DTS_HD +DEFINE_GUID(KSDATAFORMAT_SUBTYPE_IEC61937_DTS_HD, 0x0000000b, 0x0cea, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71); -DEFINE_GUID(mp_KSDATAFORMAT_SUBTYPE_IEC61937_DOLBY_MLP, +#endif + +#ifndef KSDATAFORMAT_SUBTYPE_IEC61937_DOLBY_MLP +DEFINE_GUID(KSDATAFORMAT_SUBTYPE_IEC61937_DOLBY_MLP, 0x0000000c, 0x0cea, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71); +#endif struct wasapi_sample_fmt { int mp_format; // AF_FORMAT_* @@ -84,13 +96,13 @@ static const struct wasapi_sample_fmt wasapi_formats[] = { // aka S24 (with conversion on output) {AF_FORMAT_S32, 24, 24, &KSDATAFORMAT_SUBTYPE_PCM}, {AF_FORMAT_FLOAT, 32, 32, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT}, - {AF_FORMAT_S_AC3, 16, 16, &mp_KSDATAFORMAT_SUBTYPE_IEC61937_DOLBY_DIGITAL}, - {AF_FORMAT_S_DTS, 16, 16, &mp_KSDATAFORMAT_SUBTYPE_IEC61937_DTS}, - {AF_FORMAT_S_AAC, 16, 16, &mp_KSDATAFORMAT_SUBTYPE_IEC61937_AAC}, - {AF_FORMAT_S_MP3, 16, 16, &mp_KSDATAFORMAT_SUBTYPE_IEC61937_MPEG3}, - {AF_FORMAT_S_TRUEHD, 16, 16, &mp_KSDATAFORMAT_SUBTYPE_IEC61937_DOLBY_MLP}, - {AF_FORMAT_S_EAC3, 16, 16, &mp_KSDATAFORMAT_SUBTYPE_IEC61937_DOLBY_DIGITAL_PLUS}, - {AF_FORMAT_S_DTSHD, 16, 16, &mp_KSDATAFORMAT_SUBTYPE_IEC61937_DTS_HD}, + {AF_FORMAT_S_AC3, 16, 16, &KSDATAFORMAT_SUBTYPE_IEC61937_DOLBY_DIGITAL}, + {AF_FORMAT_S_DTS, 16, 16, &KSDATAFORMAT_SUBTYPE_IEC61937_DTS}, + {AF_FORMAT_S_AAC, 16, 16, &KSDATAFORMAT_SUBTYPE_IEC61937_AAC}, + {AF_FORMAT_S_MP3, 16, 16, &KSDATAFORMAT_SUBTYPE_IEC61937_MPEG3}, + {AF_FORMAT_S_TRUEHD, 16, 16, &KSDATAFORMAT_SUBTYPE_IEC61937_DOLBY_MLP}, + {AF_FORMAT_S_EAC3, 16, 16, &KSDATAFORMAT_SUBTYPE_IEC61937_DOLBY_DIGITAL_PLUS}, + {AF_FORMAT_S_DTSHD, 16, 16, &KSDATAFORMAT_SUBTYPE_IEC61937_DTS_HD}, {0}, }; @@ -145,7 +157,19 @@ static void set_waveformat(WAVEFORMATEXTENSIBLE *wformat, wformat->SubFormat = *format_to_subtype(format->mp_format); wformat->Samples.wValidBitsPerSample = format->used_msb; - wformat->dwChannelMask = mp_chmap_to_waveext(channels); + + uint64_t chans = mp_chmap_to_waveext(channels); + wformat->dwChannelMask = chans; + + if (wformat->Format.nChannels > 8 || wformat->dwChannelMask != chans) { + // IAudioClient::IsFormatSupported tend to fallback to stereo for closest + // format match when there are more channels. Remix to standard layout. + // Also if input channel mask has channels outside 32-bits override it + // and hope for the best... + wformat->dwChannelMask = KSAUDIO_SPEAKER_7POINT1_SURROUND; + wformat->Format.nChannels = 8; + } + update_waveformat_datarate(wformat); } @@ -238,7 +262,8 @@ static char *waveformat_to_str_buf(char *buf, size_t buf_size, WAVEFORMATEX *wf) (unsigned) wf->nSamplesPerSec); return buf; } -#define waveformat_to_str(wf) waveformat_to_str_buf((char[64]){0}, 64, (wf)) +#define waveformat_to_str_(wf, sz) waveformat_to_str_buf((char[sz]){0}, sz, (wf)) +#define waveformat_to_str(wf) waveformat_to_str_(wf, MP_NUM_CHANNELS * 4 + 42) static void waveformat_copy(WAVEFORMATEXTENSIBLE* dst, WAVEFORMATEX* src) { @@ -374,7 +399,7 @@ static bool search_channels(struct ao *ao, WAVEFORMATEXTENSIBLE *wformat) "3.0", "3.0(back)", "quad", "quad(side)", "3.1", "5.0(side)", "4.1", - "5.1(side)", "6.0", "6.0(front)", "hexagonal" + "5.1(side)", "6.0", "6.0(front)", "hexagonal", "6.1(back)", "6.1(front)", "7.0", "7.0(front)", "7.1(wide)", "7.1(wide-side)", "7.1(rear)", "octagonal", NULL}; @@ -545,29 +570,36 @@ exit_label: return hr; } -static void init_session_display(struct wasapi_state *state) { +static void init_session_display(struct wasapi_state *state, const char *name) { HRESULT hr = IAudioClient_GetService(state->pAudioClient, &IID_IAudioSessionControl, (void **)&state->pSessionControl); EXIT_ON_ERROR(hr); - wchar_t path[MAX_PATH] = {0}; - GetModuleFileNameW(NULL, path, MAX_PATH); + wchar_t *path = talloc_array(NULL, wchar_t, MP_PATH_MAX); + GetModuleFileNameW(NULL, path, MP_PATH_MAX); hr = IAudioSessionControl_SetIconPath(state->pSessionControl, path, NULL); + talloc_free(path); if (FAILED(hr)) { // don't goto exit_label here since SetDisplayName might still work MP_WARN(state, "Error setting audio session icon: %s\n", mp_HRESULT_to_str(hr)); } - hr = IAudioSessionControl_SetDisplayName(state->pSessionControl, - MIXER_DEFAULT_LABEL, NULL); + assert(name); + if (!name) + return; + + wchar_t *title = mp_from_utf8(NULL, name); + hr = IAudioSessionControl_SetDisplayName(state->pSessionControl, title, NULL); + talloc_free(title); + EXIT_ON_ERROR(hr); return; exit_label: // if we got here then the session control is useless - release it SAFE_RELEASE(state->pSessionControl); - MP_WARN(state, "Error setting audio session display name: %s\n", + MP_WARN(state, "Error setting audio session name: %s\n", mp_HRESULT_to_str(hr)); return; } @@ -608,16 +640,28 @@ static HRESULT fix_format(struct ao *ao, bool align_hack) struct wasapi_state *state = ao->priv; MP_DBG(state, "IAudioClient::GetDevicePeriod\n"); - REFERENCE_TIME devicePeriod; - HRESULT hr = IAudioClient_GetDevicePeriod(state->pAudioClient,&devicePeriod, - NULL); - MP_VERBOSE(state, "Device period: %.2g ms\n", - (double) devicePeriod / 10000.0 ); - - REFERENCE_TIME bufferDuration = devicePeriod; + REFERENCE_TIME defaultPeriod, minPeriod; + HRESULT hr = IAudioClient_GetDevicePeriod(state->pAudioClient,&defaultPeriod, + &minPeriod); + MP_VERBOSE(state, "Device period: default %lld us, minimum %lld us\n", + defaultPeriod / 10, minPeriod / 10); + + // per Microsoft: + // * hnsBufferDuration = hnsPeriodicity = 0 for shared mode + // * hnsBufferDuration = hnsPeriodicity != 0 for exclusive mode + // https://learn.microsoft.com/en-us/windows/win32/api/audioclient/nf-audioclient-iaudioclient-initialize + REFERENCE_TIME bufferDuration; 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); + bufferDuration = 0; + } else if (state->opt_exclusive_buffer == 0) { + bufferDuration = defaultPeriod; + } else { + if (state->opt_exclusive_buffer > 0 && !align_hack) { + MP_VERBOSE(state, "Requested buffer duration: %d us\n", + state->opt_exclusive_buffer); + } + bufferDuration = MPMAX((REFERENCE_TIME) state->opt_exclusive_buffer * 10, + minPeriod); } // handle unsupported buffer size if AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED was @@ -630,15 +674,15 @@ static HRESULT fix_format(struct ao *ao, bool align_hack) * state->bufferFrameCount)); } - REFERENCE_TIME bufferPeriod = - state->share_mode == AUDCLNT_SHAREMODE_EXCLUSIVE ? bufferDuration : 0; + if (state->share_mode == AUDCLNT_SHAREMODE_EXCLUSIVE) + MP_VERBOSE(state, "Trying buffer duration %lld us\n", bufferDuration / 10); MP_DBG(state, "IAudioClient::Initialize\n"); hr = IAudioClient_Initialize(state->pAudioClient, state->share_mode, AUDCLNT_STREAMFLAGS_EVENTCALLBACK, bufferDuration, - bufferPeriod, + bufferDuration, &(state->format.Format), NULL); EXIT_ON_ERROR(hr); @@ -662,13 +706,13 @@ static HRESULT fix_format(struct ao *ao, bool align_hack) bufferDuration = (REFERENCE_TIME) (0.5 + (10000.0 * 1000 / state->format.Format.nSamplesPerSec * state->bufferFrameCount)); - MP_VERBOSE(state, "Buffer frame count: %"PRIu32" (%.2g ms)\n", - state->bufferFrameCount, (double) bufferDuration / 10000.0 ); + MP_VERBOSE(state, "Buffer frame count: %"PRIu32" (%lld us)\n", + state->bufferFrameCount, bufferDuration / 10); hr = init_clock(state); EXIT_ON_ERROR(hr); - init_session_display(state); + init_session_display(state, ao->client_name); init_volume_control(state); #if !HAVE_UWP @@ -701,7 +745,7 @@ static char* get_device_name(struct mp_log *l, void *talloc_ctx, IMMDevice *pDev HRESULT hr = IMMDevice_OpenPropertyStore(pDevice, STGM_READ, &pProps); EXIT_ON_ERROR(hr); - hr = IPropertyStore_GetValue(pProps, &mp_PKEY_Device_FriendlyName, + hr = IPropertyStore_GetValue(pProps, &PKEY_Device_FriendlyName, &devname); EXIT_ON_ERROR(hr); @@ -928,7 +972,7 @@ bool wasapi_thread_init(struct ao *ao) { struct wasapi_state *state = ao->priv; MP_DBG(ao, "Init wasapi thread\n"); - int64_t retry_wait = 1; + int64_t retry_wait = MP_TIME_US_TO_NS(1); bool align_hack = false; HRESULT hr; @@ -1011,13 +1055,13 @@ retry: goto retry; case AUDCLNT_E_DEVICE_IN_USE: case AUDCLNT_E_DEVICE_INVALIDATED: - if (retry_wait > 8) { + if (retry_wait > MP_TIME_US_TO_NS(8)) { MP_FATAL(ao, "Bad device retry failed\n"); return false; } wasapi_thread_uninit(ao); - MP_WARN(ao, "Retrying in %"PRId64" us\n", retry_wait); - mp_sleep_us(retry_wait); + MP_WARN(ao, "Retrying in %"PRId64" ns\n", retry_wait); + mp_sleep_ns(retry_wait); retry_wait *= 2; goto retry; } |