1
0
mirror of https://github.com/mpv-player/mpv synced 2025-02-19 14:26:57 +00:00

ao_wasapi: drop use of AF_FORMAT_S24

Do conversion directly, using the infrastructure that was added before.

This also rewrites part of format negotation, I guess.

I couldn't test the format that was used for S24 - my hardware does not
report support for it. So I commented it, as it could be buggy. Testing
this with the wasapi_formats[] entry for 24/24 uncommented would be
appreciated.
This commit is contained in:
wm4 2017-07-07 17:41:16 +02:00
parent 4cb5e53ada
commit 951c1a4907
3 changed files with 133 additions and 117 deletions

View File

@ -129,8 +129,9 @@ static bool thread_feed(struct ao *ao)
BYTE *data[1] = {pData};
ao_read_data(ao, (void **)data, frame_count,
mp_time_us() + (int64_t)llrint(delay_us));
ao_read_data_converted(ao, &state->convert_format,
(void **)data, frame_count,
mp_time_us() + (int64_t)llrint(delay_us));
// note, we can't use ao_read_data return value here since we already
// committed to frame_count above in the GetBuffer call

View File

@ -101,6 +101,7 @@ typedef struct wasapi_state {
WAVEFORMATEXTENSIBLE format;
AUDCLNT_SHAREMODE share_mode; // AUDCLNT_SHAREMODE_EXCLUSIVE / SHARED
UINT32 bufferFrameCount; // number of frames in buffer
struct ao_convert_fmt convert_format;
change_notify change;
} wasapi_state;

View File

@ -64,44 +64,53 @@ DEFINE_GUID(mp_KSDATAFORMAT_SUBTYPE_IEC61937_DOLBY_MLP,
0x0000000c, 0x0cea, 0x0010, 0x80, 0x00,
0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
struct wasapi_fmt_mapping {
struct wasapi_sample_fmt {
int mp_format; // AF_FORMAT_*
int bits; // aka wBitsPerSample
int used_msb; // aka wValidBitsPerSample
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}
// some common bit depths / container sizes (requests welcome)
// Entries that have the same mp_format must be:
// 1. consecutive
// 2. sorted by preferred format (worst comes last)
static const struct wasapi_sample_fmt wasapi_formats[] = {
{AF_FORMAT_U8, 8, 8, &KSDATAFORMAT_SUBTYPE_PCM},
{AF_FORMAT_S16, 16, 16, &KSDATAFORMAT_SUBTYPE_PCM},
{AF_FORMAT_S32, 32, 32, &KSDATAFORMAT_SUBTYPE_PCM},
// compatible, assume LSBs are ignored
{AF_FORMAT_S32, 32, 24, &KSDATAFORMAT_SUBTYPE_PCM},
// aka S24 (with conversion) - untested, thus commented.
//{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},
{0},
};
static const GUID *format_to_subtype(int format)
{
if (af_fmt_is_spdif(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 (format == AF_FORMAT_FLOAT) {
return &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
} else if (af_fmt_is_int(format)) {
return &KSDATAFORMAT_SUBTYPE_PCM;
for (int i = 0; wasapi_formats[i].mp_format; i++) {
if (format == wasapi_formats[i].mp_format)
return wasapi_formats[i].subtype;
}
return NULL;
return &KSDATAFORMAT_SPECIFIER_NONE;
}
// "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;
static int special_subtype_to_format(const GUID *subtype)
{
for (int i = 0; wasapi_formats[i].mp_format; i++) {
if (IsEqualGUID(subtype, wasapi_formats[i].subtype) &&
af_fmt_is_spdif(wasapi_formats[i].mp_format))
return wasapi_formats[i].mp_format;
}
return 0;
}
@ -123,32 +132,17 @@ static void update_waveformat_datarate(WAVEFORMATEXTENSIBLE *wformat)
}
static void set_waveformat(WAVEFORMATEXTENSIBLE *wformat,
int format, WORD valid_bits,
struct wasapi_sample_fmt format,
DWORD samplerate, struct mp_chmap *channels)
{
// First find a format that is actually representable.
// (Notably excludes AF_FORMAT_DOUBLE.)
const GUID *sub_format = &KSDATAFORMAT_SPECIFIER_NONE;
int alt_formats[AF_FORMAT_COUNT];
af_get_best_sample_formats(format, alt_formats);
for (int n = 0; alt_formats[n]; n++) {
const GUID *guid = format_to_subtype(alt_formats[n]);
if (guid) {
format = alt_formats[n];
sub_format = guid;
break;
}
}
wformat->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
wformat->Format.nChannels = channels->num;
wformat->Format.nSamplesPerSec = samplerate;
wformat->Format.wBitsPerSample = af_fmt_to_bytes(format) * 8;
wformat->Format.wBitsPerSample = format.bits;
wformat->Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
wformat->SubFormat = *sub_format;
wformat->Samples.wValidBitsPerSample =
valid_bits ? valid_bits : wformat->Format.wBitsPerSample;
wformat->SubFormat = *format_to_subtype(format.mp_format);
wformat->Samples.wValidBitsPerSample = format.used_msb;
wformat->dwChannelMask = mp_chmap_to_waveext(channels);
update_waveformat_datarate(wformat);
}
@ -161,7 +155,21 @@ static void set_waveformat_with_ao(WAVEFORMATEXTENSIBLE *wformat, struct ao *ao)
struct mp_chmap channels = ao->channels;
mp_chmap_reorder_to_waveext(&channels);
set_waveformat(wformat, ao->format, 0, ao->samplerate, &channels);
// First find a format that is actually representable.
// (Notably excludes AF_FORMAT_DOUBLE.)
struct wasapi_sample_fmt format =
{AF_FORMAT_S16, 16, 16, &KSDATAFORMAT_SUBTYPE_PCM};
int alt_formats[AF_FORMAT_COUNT];
af_get_best_sample_formats(ao->format, alt_formats);
for (int n = 0; alt_formats[n]; n++) {
for (int i = 0; wasapi_formats[i].mp_format; i++) {
if (wasapi_formats[i].mp_format == alt_formats[n])
format = wasapi_formats[i];
}
}
set_waveformat(wformat, format, ao->samplerate, &channels);
}
// other wformat parameters must already be set with set_waveformat
@ -181,51 +189,45 @@ static void change_waveformat_channels(WAVEFORMATEXTENSIBLE *wformat,
update_waveformat_datarate(wformat);
}
static WORD waveformat_valid_bits(const WAVEFORMATEX *wf)
static struct wasapi_sample_fmt format_from_waveformat(WAVEFORMATEX *wf)
{
if (wf->wFormatTag == WAVE_FORMAT_EXTENSIBLE) {
WAVEFORMATEXTENSIBLE *wformat = (WAVEFORMATEXTENSIBLE *)wf;
return wformat->Samples.wValidBitsPerSample;
} else {
return wf->wBitsPerSample;
}
}
struct wasapi_sample_fmt res = {0};
static int format_from_waveformat(WAVEFORMATEX *wf)
{
int format;
switch (wf->wFormatTag) {
case WAVE_FORMAT_EXTENSIBLE:
{
WAVEFORMATEXTENSIBLE *wformat = (WAVEFORMATEXTENSIBLE *)wf;
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);
for (int n = 0; wasapi_formats[n].mp_format; n++) {
const struct wasapi_sample_fmt *fmt = &wasapi_formats[n];
int valid_bits = 0;
if (wf->wBitsPerSample != fmt->bits)
continue;
const GUID *wf_guid = NULL;
switch (wf->wFormatTag) {
case WAVE_FORMAT_EXTENSIBLE: {
WAVEFORMATEXTENSIBLE *wformat = (WAVEFORMATEXTENSIBLE *)wf;
wf_guid = &wformat->SubFormat;
if (IsEqualGUID(wf_guid, &KSDATAFORMAT_SUBTYPE_PCM))
valid_bits = wformat->Samples.wValidBitsPerSample;
break;
}
case WAVE_FORMAT_PCM:
wf_guid = &KSDATAFORMAT_SUBTYPE_PCM;
break;
case WAVE_FORMAT_IEEE_FLOAT:
wf_guid = &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
break;
}
if (!wf_guid || !IsEqualGUID(wf_guid, fmt->subtype))
continue;
res = *fmt;
if (valid_bits > 0 && valid_bits < fmt->bits)
res.used_msb = valid_bits;
break;
}
case WAVE_FORMAT_PCM:
format = wf->wBitsPerSample == 8 ? AF_FORMAT_U8 : AF_FORMAT_S32;
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_bytes operations
// should be a no-op for properly configured "special" formats, otherwise it
// will return 0.
if (wf->wBitsPerSample % 8)
return 0;
return af_fmt_change_bytes(format, wf->wBitsPerSample / 8);
return res;
}
static bool chmap_from_waveformat(struct mp_chmap *channels,
@ -251,18 +253,15 @@ static char *waveformat_to_str_buf(char *buf, size_t buf_size, WAVEFORMATEX *wf)
struct mp_chmap channels;
chmap_from_waveformat(&channels, wf);
unsigned valid_bits = waveformat_valid_bits(wf);
char validstr[12] = "";
if (valid_bits != wf->wBitsPerSample)
snprintf(validstr, sizeof(validstr), " (%u valid)", valid_bits);
struct wasapi_sample_fmt format = format_from_waveformat(wf);
snprintf(buf, buf_size, "%s %s%s @ %uhz",
snprintf(buf, buf_size, "%s %s (%d/%d bits) @ %uhz",
mp_chmap_to_str(&channels),
af_fmt_to_str(format_from_waveformat(wf)),
validstr, (unsigned) wf->nSamplesPerSec);
af_fmt_to_str(format.mp_format), format.bits, format.used_msb,
(unsigned) wf->nSamplesPerSec);
return buf;
}
#define waveformat_to_str(wf) waveformat_to_str_buf((char[40]){0}, 40, (wf))
#define waveformat_to_str(wf) waveformat_to_str_buf((char[64]){0}, 64, (wf))
static void waveformat_copy(WAVEFORMATEXTENSIBLE* dst, WAVEFORMATEX* src)
{
@ -277,8 +276,8 @@ 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) {
struct wasapi_sample_fmt format = format_from_waveformat(wf);
if (!format.mp_format) {
MP_ERR(ao, "Unable to construct sample format from WAVEFORMAT %s\n",
waveformat_to_str(wf));
return false;
@ -286,15 +285,28 @@ static bool set_ao_format(struct ao *ao, WAVEFORMATEX *wf,
// 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.mp_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;
}
struct ao_convert_fmt conv = {
.src_fmt = format.mp_format,
.channels = channels.num,
.dst_bits = format.bits,
.pad_lsb = format.bits - format.used_msb,
};
if (!ao_can_convert_inplace(&conv)) {
MP_ERR(ao, "Unable to convert to %s\n", waveformat_to_str(wf));
return false;
}
state->convert_format = conv;
ao->samplerate = wf->nSamplesPerSec;
ao->format = format;
ao->format = format.mp_format;
ao->channels = channels;
}
waveformat_copy(&state->format, wf);
@ -333,17 +345,16 @@ static bool try_format_exclusive_with_spdif_fallback(struct ao *ao,
static bool search_sample_formats(struct ao *ao, WAVEFORMATEXTENSIBLE *wformat,
int samplerate, struct mp_chmap *channels)
{
// some common bit depths / container sizes (requests welcome)
int try[] = {AF_FORMAT_FLOAT , AF_FORMAT_S32 ,
AF_FORMAT_S24 , AF_FORMAT_S32 , AF_FORMAT_S16,
AF_FORMAT_U8 , 0};
unsigned valid[] = {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;
int alt_formats[AF_FORMAT_COUNT];
af_get_best_sample_formats(ao->format, alt_formats);
for (int n = 0; alt_formats[n]; n++) {
for (int i = 0; wasapi_formats[i].mp_format; i++) {
if (wasapi_formats[i].mp_format == alt_formats[n]) {
set_waveformat(wformat, wasapi_formats[i], samplerate, channels);
if (try_format_exclusive(ao, wformat))
return true;
}
}
}
wformat->Format.wBitsPerSample = 0;
@ -436,9 +447,10 @@ static bool find_formats_exclusive(struct ao *ao, bool do_search)
if (!set_ao_format(ao, &wformat.Format, AUDCLNT_SHAREMODE_EXCLUSIVE))
return false;
MP_VERBOSE(ao, "Accepted as %s %s @ %dhz (exclusive)\n",
MP_VERBOSE(ao, "Accepted as %s %s @ %dhz (exclusive) -> %s\n",
mp_chmap_to_str(&ao->channels),
af_fmt_to_str(ao->format), ao->samplerate);
af_fmt_to_str(ao->format), ao->samplerate,
waveformat_to_str(&wformat.Format));
return true;
}
@ -479,9 +491,10 @@ static bool find_formats_shared(struct ao *ao)
if (!set_ao_format(ao, &wformat.Format, AUDCLNT_SHAREMODE_SHARED))
return false;
MP_VERBOSE(ao, "Accepted as %s %s @ %dhz (shared)\n",
MP_VERBOSE(ao, "Accepted as %s %s @ %dhz (shared) -> %s\n",
mp_chmap_to_str(&ao->channels),
af_fmt_to_str(ao->format), ao->samplerate);
af_fmt_to_str(ao->format), ao->samplerate,
waveformat_to_str(&wformat.Format));
return true;
exit_label:
MP_ERR(state, "Error finding shared mode format: %s\n",
@ -623,8 +636,6 @@ static HRESULT fix_format(struct ao *ao, bool align_hack)
REFERENCE_TIME bufferPeriod =
state->share_mode == AUDCLNT_SHAREMODE_EXCLUSIVE ? bufferDuration : 0;
ao->format = af_fmt_from_planar(ao->format);
MP_DBG(state, "IAudioClient::Initialize\n");
hr = IAudioClient_Initialize(state->pAudioClient,
state->share_mode,
@ -923,6 +934,9 @@ HRESULT wasapi_thread_init(struct ao *ao)
int64_t retry_wait = 1;
bool align_hack = false;
HRESULT hr;
ao->format = af_fmt_from_planar(ao->format);
retry: ;
if (state->deviceID) {
hr = load_device(ao->log, &state->pDevice, state->deviceID);