summaryrefslogtreecommitdiffstats
path: root/audio/out/ao_wasapi_utils.c
diff options
context:
space:
mode:
Diffstat (limited to 'audio/out/ao_wasapi_utils.c')
-rw-r--r--audio/out/ao_wasapi_utils.c162
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;
}