summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--audio/out/ao_wasapi.c6
-rwxr-xr-xaudio/out/ao_wasapi_utils.c307
2 files changed, 152 insertions, 161 deletions
diff --git a/audio/out/ao_wasapi.c b/audio/out/ao_wasapi.c
index a08364ad29..205a25d03e 100644
--- a/audio/out/ao_wasapi.c
+++ b/audio/out/ao_wasapi.c
@@ -243,12 +243,6 @@ static int init(struct ao *ao)
if(!wasapi_fill_VistaBlob(state))
MP_WARN(ao, "Error loading thread priority functions\n");
- if (state->opt_exclusive) {
- state->share_mode = AUDCLNT_SHAREMODE_EXCLUSIVE;
- } else {
- state->share_mode = AUDCLNT_SHAREMODE_SHARED;
- }
-
state->init_done = CreateEventW(NULL, FALSE, FALSE, NULL);
state->hUninit = CreateEventW(NULL, FALSE, FALSE, NULL);
state->hFeed = CreateEventW(NULL, FALSE, FALSE, NULL); /* for wasapi event mode */
diff --git a/audio/out/ao_wasapi_utils.c b/audio/out/ao_wasapi_utils.c
index e8bf1df92e..c5c4cd0a9a 100755
--- a/audio/out/ao_wasapi_utils.c
+++ b/audio/out/ao_wasapi_utils.c
@@ -42,6 +42,70 @@ DEFINE_PROPERTYKEY(mp_PKEY_Device_FriendlyName,
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,
+ 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
+DEFINE_GUID(mp_KSDATAFORMAT_SUBTYPE_IEC61937_DOLBY_DIGITAL,
+ 0x00000092, 0x0000, 0x0010, 0x80, 0x00,
+ 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
+// might require 7+
+DEFINE_GUID(mp_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,
+ 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
+DEFINE_GUID(mp_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,
+ 0x0000000b, 0x0cea, 0x0010, 0x80, 0x00,
+ 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
+DEFINE_GUID(mp_KSDATAFORMAT_SUBTYPE_IEC61937_DOLBY_MLP,
+ 0x0000000c, 0x0cea, 0x0010, 0x80, 0x00,
+ 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
+
+struct wasapi_fmt_mapping {
+ const GUID *subtype;
+ int format;
+};
+
+const struct wasapi_fmt_mapping wasapi_fmt_table[] = {
+ {&mp_KSDATAFORMAT_SUBTYPE_IEC61937_DOLBY_DIGITAL, AF_FORMAT_S_AC3},
+ {&mp_KSDATAFORMAT_SUBTYPE_IEC61937_DTS, AF_FORMAT_S_DTS},
+ {&mp_KSDATAFORMAT_SUBTYPE_IEC61937_AAC, AF_FORMAT_S_AAC},
+ {&mp_KSDATAFORMAT_SUBTYPE_IEC61937_MPEG3, AF_FORMAT_S_MP3},
+ {&mp_KSDATAFORMAT_SUBTYPE_IEC61937_DOLBY_MLP, AF_FORMAT_S_TRUEHD},
+ {&mp_KSDATAFORMAT_SUBTYPE_IEC61937_DOLBY_DIGITAL_PLUS, AF_FORMAT_S_EAC3},
+ {&mp_KSDATAFORMAT_SUBTYPE_IEC61937_DTS_HD, AF_FORMAT_S_DTSHD},
+ {0}
+};
+
+static const GUID *format_to_subtype(int format)
+{
+ if (AF_FORMAT_IS_SPECIAL(format)) {
+ for (int i = 0; wasapi_fmt_table[i].format; i++) {
+ if (wasapi_fmt_table[i].format == format)
+ return wasapi_fmt_table[i].subtype;
+ }
+ return &KSDATAFORMAT_SPECIFIER_NONE;
+ } else if (af_fmt_is_float(format)) {
+ return &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
+ }
+ return &KSDATAFORMAT_SUBTYPE_PCM;
+}
+
+// "solve" the under-determined inverse of format_to_subtype by
+// assuming the input subtype is "special" (i.e. IEC61937)
+static int special_subtype_to_format(const GUID *subtype) {
+ for (int i = 0; wasapi_fmt_table[i].format; i++) {
+ if (IsEqualGUID(subtype, wasapi_fmt_table[i].subtype))
+ return wasapi_fmt_table[i].format;
+ }
+ return 0;
+}
char *mp_GUID_to_str_buf(char *buf, size_t buf_size, const GUID *guid)
{
@@ -151,7 +215,7 @@ exit_label:
return false;
}
-static void update_waveformat_datarate_pcm(WAVEFORMATEXTENSIBLE *wformat)
+static void update_waveformat_datarate(WAVEFORMATEXTENSIBLE *wformat)
{
WAVEFORMATEX *wf = &wformat->Format;
wf->nBlockAlign = wf->nChannels * wf->wBitsPerSample / 8;
@@ -159,52 +223,48 @@ static void update_waveformat_datarate_pcm(WAVEFORMATEXTENSIBLE *wformat)
}
static void set_waveformat(WAVEFORMATEXTENSIBLE *wformat,
- WORD bits, WORD valid_bits, bool is_float,
+ int format, WORD valid_bits,
DWORD samplerate, struct mp_chmap *channels)
{
wformat->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
wformat->Format.nChannels = channels->num;
wformat->Format.nSamplesPerSec = samplerate;
- wformat->Format.wBitsPerSample = bits;
+ wformat->Format.wBitsPerSample = af_fmt2bits(format);
wformat->Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
- if (is_float) {
- wformat->SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
- } else {
- wformat->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
- }
- wformat->Samples.wValidBitsPerSample = valid_bits;
+
+ wformat->SubFormat = *format_to_subtype(format);
+ wformat->Samples.wValidBitsPerSample = valid_bits ? valid_bits : wformat->Format.wBitsPerSample;
wformat->dwChannelMask = mp_chmap_to_waveext(channels);
- update_waveformat_datarate_pcm(wformat);
+ update_waveformat_datarate(wformat);
}
+// This implicitly transforms all pcm formats to:
+// interleaved / signed (except 8-bit is unsigned) / waveext channel order.
+// "Special" formats should be exempt as they should already
+// satisfy these properties.
static void set_waveformat_with_ao(WAVEFORMATEXTENSIBLE *wformat, struct ao *ao)
{
- // This implicitly transforms all formats to:
- // interleaved / signed (except for 8 bit) / waveext channel order.
- // You must still call set_ao_format() to ensure consistency.
-
struct mp_chmap channels = ao->channels;
mp_chmap_reorder_to_waveext(&channels);
- set_waveformat(wformat, af_fmt2bits(ao->format), af_fmt2bits(ao->format),
- af_fmt_is_float(ao->format), ao->samplerate, &channels);
+ set_waveformat(wformat, ao->format, 0, ao->samplerate, &channels);
}
+// other wformat parameters must already be set with set_waveformat
static void change_waveformat_samplerate(WAVEFORMATEXTENSIBLE *wformat,
DWORD samplerate)
{
- // other wformat parameters must already be set with set_waveformat
wformat->Format.nSamplesPerSec = samplerate;
- update_waveformat_datarate_pcm(wformat);
+ update_waveformat_datarate(wformat);
}
+// other wformat parameters must already be set with set_waveformat
static void change_waveformat_channels(WAVEFORMATEXTENSIBLE *wformat,
struct mp_chmap *channels)
{
- // other wformat parameters must already be set with set_waveformat
wformat->Format.nChannels = channels->num;
- wformat->dwChannelMask = mp_chmap_to_waveext(channels);
- update_waveformat_datarate_pcm(wformat);
+ wformat->dwChannelMask = mp_chmap_to_waveext(channels);
+ update_waveformat_datarate(wformat);
}
static WORD waveformat_valid_bits(const WAVEFORMATEX *wf)
@@ -217,51 +277,37 @@ static WORD waveformat_valid_bits(const WAVEFORMATEX *wf)
}
}
-static bool waveformat_is_float(WAVEFORMATEX *wf)
-{
- switch (wf->wFormatTag) {
- case WAVE_FORMAT_EXTENSIBLE:
- {
- WAVEFORMATEXTENSIBLE *wformat = (WAVEFORMATEXTENSIBLE *)wf;
- return IsEqualGUID(&KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, &wformat->SubFormat);
- }
- case WAVE_FORMAT_IEEE_FLOAT:
- return true;
- default:
- return false;
- }
-}
-
-static bool waveformat_is_pcm_int(WAVEFORMATEX *wf)
+static int format_from_waveformat(WAVEFORMATEX *wf)
{
+ int format;
switch (wf->wFormatTag) {
case WAVE_FORMAT_EXTENSIBLE:
{
WAVEFORMATEXTENSIBLE *wformat = (WAVEFORMATEXTENSIBLE *)wf;
- return IsEqualGUID(&KSDATAFORMAT_SUBTYPE_PCM, &wformat->SubFormat);
+ if (IsEqualGUID(&wformat->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM)) {
+ format = wf->wBitsPerSample == 8 ? AF_FORMAT_U8 : AF_FORMAT_S32;
+ } else if (IsEqualGUID(&wformat->SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT)) {
+ format = AF_FORMAT_FLOAT;
+ } else {
+ format = special_subtype_to_format(&wformat->SubFormat);
+ }
+ break;
}
case WAVE_FORMAT_PCM:
- return true;
- default:
- return false;
- }
-}
-
-static int format_from_waveformat(WAVEFORMATEX *wf)
-{
- int format = 0;
- // it is an undocumented fact that 8-bit pcm in WAVEFORMATEX implies unsigned
- if (waveformat_is_float(wf)) {
- format = AF_FORMAT_FLOAT;
- } else if (waveformat_is_pcm_int(wf)) {
format = wf->wBitsPerSample == 8 ? AF_FORMAT_U8 : AF_FORMAT_S32;
- } else {
+ break;
+ case WAVE_FORMAT_IEEE_FLOAT:
+ format = AF_FORMAT_FLOAT;
+ break;
+ default:
return 0;
}
// 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
// format with the container size. The least significant, "invalid"
// bits will be excess precision ignored by wasapi.
+ // The change_bits operations should be a no-op for properly
+ // configured "special" formats, otherwise it will return 0.
return af_fmt_change_bits(format, wf->wBitsPerSample);
}
@@ -284,26 +330,17 @@ static bool chmap_from_waveformat(struct mp_chmap *channels, const WAVEFORMATEX
static char *waveformat_to_str_buf(char *buf, size_t buf_size, WAVEFORMATEX *wf)
{
- char* type = "?";
- if (waveformat_is_float(wf)) {
- type = "float";
- } else if (waveformat_is_pcm_int(wf)) {
- type = wf->wBitsPerSample == 8 ? "u" : "s";
- }
-
- unsigned valid_bits = waveformat_valid_bits(wf);
struct mp_chmap channels;
chmap_from_waveformat(&channels, wf);
- if (valid_bits == wf->wBitsPerSample) {
- snprintf(buf, buf_size, "%s %s%u @ %uhz",
- mp_chmap_to_str(&channels), type, valid_bits,
- (unsigned) wf->nSamplesPerSec);
- } else {
- snprintf(buf, buf_size, "%s %s%u (in %s%u) @ %uhz",
- mp_chmap_to_str(&channels), type, valid_bits,
- type, (unsigned) wf->wBitsPerSample,
- (unsigned) wf->nSamplesPerSec);
- }
+
+ unsigned valid_bits = waveformat_valid_bits(wf);
+ char validstr[12] = "";
+ if (valid_bits != wf->wBitsPerSample)
+ snprintf(validstr, sizeof(validstr), " (%u valid)", valid_bits);
+
+ snprintf(buf, buf_size, "%s %s%s @ %uhz",
+ mp_chmap_to_str(&channels), af_fmt_to_str(format_from_waveformat(wf)),
+ validstr, (unsigned) wf->nSamplesPerSec);
return buf;
}
#define waveformat_to_str(wf) waveformat_to_str_buf((char[40]){0}, 40, (wf))
@@ -317,36 +354,37 @@ static void waveformat_copy(WAVEFORMATEXTENSIBLE* dst, WAVEFORMATEX* src)
}
}
-static bool set_ao_format(struct ao *ao, WAVEFORMATEX *wf)
+static bool set_ao_format(struct ao *ao, WAVEFORMATEX *wf, AUDCLNT_SHAREMODE share_mode)
{
struct wasapi_state *state = ao->priv;
-
int format = format_from_waveformat(wf);
if (!format) {
MP_ERR(ao, "Unable to construct sample format from WAVEFORMAT %s\n",
waveformat_to_str(wf));
return false;
}
- struct mp_chmap channels;
- if (!chmap_from_waveformat(&channels, wf)) {
- MP_ERR(ao, "Unable to construct channel map from WAVEFORMAT %s\n",
- waveformat_to_str(wf));
- return false;
- }
- ao->samplerate = wf->nSamplesPerSec;
- ao->bps = wf->nAvgBytesPerSec;
- ao->format = format;
- ao->channels = channels;
+ // Do not touch the ao for passthrough, just assume that we set WAVEFORMATEX correctly.
+ if (!AF_FORMAT_IS_SPECIAL(format)) {
+ struct mp_chmap channels;
+ if (!chmap_from_waveformat(&channels, wf)) {
+ MP_ERR(ao, "Unable to construct channel map from WAVEFORMAT %s\n",
+ waveformat_to_str(wf));
+ return false;
+ }
+ ao->samplerate = wf->nSamplesPerSec;
+ ao->format = format;
+ ao->channels = channels;
+ }
waveformat_copy(&state->format, wf);
-
+ state->share_mode = share_mode;
return true;
}
static bool try_format_exclusive(struct ao *ao, WAVEFORMATEXTENSIBLE *wformat)
{
struct wasapi_state *state = ao->priv;
- MP_VERBOSE(ao, "Trying %s\n", waveformat_to_str(&wformat->Format));
+ MP_VERBOSE(ao, "Trying %s (exclusive)\n", waveformat_to_str(&wformat->Format));
HRESULT hr = IAudioClient_IsFormatSupported(state->pAudioClient,
AUDCLNT_SHAREMODE_EXCLUSIVE,
&wformat->Format, NULL);
@@ -362,22 +400,15 @@ exit_label:
static bool search_sample_formats(struct ao *ao, WAVEFORMATEXTENSIBLE *wformat,
int samplerate, struct mp_chmap *channels)
{
- // try float
- int float_bits[] = {64, 32, 0};
- for (int i = 0; float_bits[i]; i++) {
- set_waveformat(wformat, float_bits[i], float_bits[i], true,
- samplerate, channels);
- if (try_format_exclusive(ao, wformat))
- return true;
- }
-
- // try int
// some common bit depths / container sizes (requests welcome)
- int bits[] = {32, 24, 32, 16, 0};
- int valid_bits[] = {32, 24, 24, 16, 0};
- for (int i = 0; bits[i] && valid_bits[i]; i++) {
- set_waveformat(wformat, bits[i], valid_bits[i], false,
- samplerate, channels);
+ int try[] = {AF_FORMAT_DOUBLE, AF_FORMAT_FLOAT, AF_FORMAT_S32,
+ AF_FORMAT_S24 , AF_FORMAT_S32 , AF_FORMAT_S16,
+ AF_FORMAT_U8 , 0};
+ unsigned valid[] = {0 , 0, 0,
+ 0 , 24, 0,
+ 0 };
+ for (int i = 0; try[i]; i++) {
+ set_waveformat(wformat, try[i], valid[i], samplerate, channels);
if (try_format_exclusive(ao, wformat))
return true;
}
@@ -470,20 +501,21 @@ static bool search_channels(struct ao *ao, WAVEFORMATEXTENSIBLE *wformat)
return false;
}
-static bool find_formats_exclusive(struct ao *ao)
+static bool find_formats_exclusive(struct ao *ao, bool do_search)
{
WAVEFORMATEXTENSIBLE wformat;
set_waveformat_with_ao(&wformat, ao);
- // If the format doesn't work as is, we have to manually try
- // all possible formats in search_channels(). Nice API Microsoft.
- if (!try_format_exclusive(ao, &wformat) && !search_channels(ao, &wformat))
+ // Try the requested format as is. If that doesn't work, and the
+ // do_search argument is set, do the pcm format search.
+ if (!try_format_exclusive(ao, &wformat) &&
+ (!do_search || !search_channels(ao, &wformat)))
return false;
- if (!set_ao_format(ao, &wformat.Format))
+ if (!set_ao_format(ao, &wformat.Format, AUDCLNT_SHAREMODE_EXCLUSIVE))
return false;
- MP_VERBOSE(ao, "Accepted as %s %s @ %dhz\n",
+ MP_VERBOSE(ao, "Accepted as %s %s @ %dhz (exclusive)\n",
mp_chmap_to_str(&ao->channels),
af_fmt_to_str(ao->format), ao->samplerate);
return true;
@@ -496,7 +528,7 @@ static bool find_formats_shared(struct ao *ao)
WAVEFORMATEXTENSIBLE wformat;
set_waveformat_with_ao(&wformat, ao);
- MP_VERBOSE(ao, "Trying %s\n", waveformat_to_str(&wformat.Format));
+ MP_VERBOSE(ao, "Trying %s (shared)\n", waveformat_to_str(&wformat.Format));
WAVEFORMATEX *closestMatch;
HRESULT hr = IAudioClient_IsFormatSupported(state->pAudioClient,
AUDCLNT_SHAREMODE_SHARED,
@@ -522,10 +554,10 @@ static bool find_formats_shared(struct ao *ao)
CoTaskMemFree(closestMatch);
}
- if (!set_ao_format(ao, &wformat.Format))
+ if (!set_ao_format(ao, &wformat.Format, AUDCLNT_SHAREMODE_SHARED))
return false;
- MP_VERBOSE(ao, "Accepted as %s %s @ %dhz\n",
+ MP_VERBOSE(ao, "Accepted as %s %s @ %dhz (shared)\n",
mp_chmap_to_str(&ao->channels),
af_fmt_to_str(ao->format), ao->samplerate);
return true;
@@ -534,59 +566,24 @@ exit_label:
return false;
}
-static bool try_passthrough(struct ao *ao)
-{
- // fixme: this will only do SPDIF AC3 and doesn't bother to check
- // that the resulting waveformat is actually consistent with the ao
- // https://msdn.microsoft.com/en-us/library/windows/desktop/dd370811%28v=vs.85%29.aspx
- // https://msdn.microsoft.com/en-us/library/windows/desktop/dd316761(v=vs.85).aspx
- struct wasapi_state *state = ao->priv;
-
- WAVEFORMATEXTENSIBLE wformat = {
- .Format = {
- .wFormatTag = WAVE_FORMAT_EXTENSIBLE,
- .nChannels = ao->channels.num,
- .nSamplesPerSec = ao->samplerate,
- .nAvgBytesPerSec = (ao->samplerate) * (ao->channels.num * 2),
- .nBlockAlign = ao->channels.num * 2,
- .wBitsPerSample = 16,
- .cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX),
- },
- .Samples.wValidBitsPerSample = 16,
- .dwChannelMask = mp_chmap_to_waveext(&ao->channels),
- .SubFormat = KSDATAFORMAT_SUBTYPE_PCM,
- };
- wformat.SubFormat.Data1 = WAVE_FORMAT_DOLBY_AC3_SPDIF; // see INIT_WAVEFORMATEX_GUID macro
-
- MP_VERBOSE(ao, "Trying passthrough for %s...\n", af_fmt_to_str(ao->format));
-
- HRESULT hr = IAudioClient_IsFormatSupported(state->pAudioClient,
- state->share_mode,
- &wformat.Format, NULL);
- if (!FAILED(hr)) {
- state->format = wformat;
- return true;
- }
- MP_ERR(ao, "Couldn't use passthrough\n");
- return false;
-}
-
static bool find_formats(struct ao *ao)
{
struct wasapi_state *state = ao->priv;
if (state->opt_exclusive) {
- // https://msdn.microsoft.com/en-us/library/windows/desktop/dd370811%28v=vs.85%29.aspx
- // "Many audio devices support both PCM and non-PCM stream formats.
- // However, the audio engine can mix only PCM streams. Thus, only
- // exclusive-mode streams can have non-PCM formats.
- if (AF_FORMAT_IS_IEC61937(ao->format)) {
- return try_passthrough(ao);
- } else {
- return find_formats_exclusive(ao);
- }
+ // If exclusive is requested, try the requested format (which
+ // might be passthrough). If that fails, do a pcm format
+ // search.
+ return find_formats_exclusive(ao, true);
+ } else if (AF_FORMAT_IS_SPECIAL(ao->format)) {
+ // If a passthrough format is requested, but exclusive mode
+ // was not explicitly set, try only the requested passthrough
+ // format in exclusive mode. Fall back on shared mode if that
+ // fails without doing the exclusive pcm format search.
+ if (find_formats_exclusive(ao, false))
+ return true;
}
-
+ // Default is to use shared mode
return find_formats_shared(ao);
}