wasapi2: Demote S24_32LE in exclusive-mode format ordering

Some endpoints accept 24-bit in 32-bit PCM (S24_32LE) in exclusive mode
but playback at very low volume. Until the root cause is identified,
push S24_32LE to the end of the candidate list

Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/9586>
This commit is contained in:
Seungha Yang 2025-08-20 18:14:35 +09:00 committed by GStreamer Marge Bot
parent f2aabd6a71
commit 0d94713288

View File

@ -565,6 +565,7 @@ gst_wasapi2_get_default_mix_format (void)
format->wBitsPerSample = 16;
format->nBlockAlign = format->nChannels * format->wBitsPerSample / 8;
format->nAvgBytesPerSec = format->nSamplesPerSec * format->nBlockAlign;
format->cbSize = 0;
return format;
}
@ -713,6 +714,7 @@ make_wfx_ext (DWORD nSamplesPerSec, WORD nChannels, WORD wBitsPerSample,
return w;
}
/* *INDENT-OFF* */
gboolean
gst_wasapi2_get_exclusive_formats (IAudioClient * client,
@ -722,6 +724,7 @@ gst_wasapi2_get_exclusive_formats (IAudioClient * client,
PropVariantInit (&var);
WAVEFORMATEX *device_format = nullptr;
WAVEFORMATEX *closest = nullptr;
WAVEFORMATEX *basis = nullptr;
/* Prefer device format if supported */
auto hr = props->GetValue (PKEY_AudioEngine_DeviceFormat, &var);
@ -740,9 +743,11 @@ gst_wasapi2_get_exclusive_formats (IAudioClient * client,
&closest);
if (hr == S_OK) {
basis = gst_wasapi2_copy_wfx (device_format);
g_ptr_array_add (list, device_format);
device_format = nullptr;
} else if (hr == S_FALSE && closest) {
basis = gst_wasapi2_copy_wfx (closest);
g_ptr_array_add (list, closest);
closest = nullptr;
}
@ -761,9 +766,9 @@ gst_wasapi2_get_exclusive_formats (IAudioClient * client,
const DepthPair depth_pairs[] = {
{32, 32, true}, /* 32-float */
{32, 32, false}, /* 32-int */
{32, 24, false}, /* 24-in-32 */
{24, 24, false}, /* 24-packed */
{16, 16, false}, /* 16-int */
{32, 24, false}, /* 24-in-32 */
};
const DWORD rates[] = { 192000, 176400, 96000, 88200, 48000, 44100 };
@ -786,6 +791,18 @@ gst_wasapi2_get_exclusive_formats (IAudioClient * client,
}
}
if (!basis) {
if (list && list->len > 0) {
auto first = (WAVEFORMATEX *) g_ptr_array_index (list, 0);
basis = gst_wasapi2_copy_wfx (first);
} else {
basis = gst_wasapi2_get_default_mix_format ();
}
}
gst_wasapi2_sort_wfx (list, basis);
gst_wasapi2_free_wfx (basis);
return TRUE;
}
@ -835,6 +852,7 @@ struct FormatView
GUID subformat;
WORD bits_per_sample;
WORD valid_bits_per_sample;
WORD raw_valid_bits_per_sample;
DWORD channel_mask;
WORD format_tag;
};
@ -846,6 +864,36 @@ is_extensible_format (const WAVEFORMATEX * wfx)
wfx->cbSize >= (sizeof (WAVEFORMATEXTENSIBLE) - sizeof (WAVEFORMATEX));
}
static inline gboolean
is_float_subformat (const FormatView * v)
{
return IsEqualGUID (v->subformat, GST_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT);
}
static inline gboolean
is_pcm_subformat (const FormatView * v)
{
return IsEqualGUID (v->subformat, GST_KSDATAFORMAT_SUBTYPE_PCM);
}
static inline gint
effective_bits (const FormatView * v)
{
if (is_float_subformat (v))
return 32;
return v->valid_bits_per_sample ? v->
valid_bits_per_sample : v->bits_per_sample;
}
static inline gboolean
is_s24_in_32 (const FormatView * v)
{
return is_pcm_subformat (v) &&
v->bits_per_sample == 32 &&
(v->raw_valid_bits_per_sample == 24 || v->valid_bits_per_sample == 24);
}
static FormatView
make_view (const WAVEFORMATEX * wfx)
{
@ -859,10 +907,10 @@ make_view (const WAVEFORMATEX * wfx)
if (is_extensible_format (wfx)) {
auto wfe = (const WAVEFORMATEXTENSIBLE *) wfx;
view.subformat = wfe->SubFormat;
view.valid_bits_per_sample = wfe->Samples.wValidBitsPerSample;
view.raw_valid_bits_per_sample = wfe->Samples.wValidBitsPerSample;
view.valid_bits_per_sample = view.raw_valid_bits_per_sample ?
view.raw_valid_bits_per_sample : view.bits_per_sample;
view.channel_mask = wfe->dwChannelMask;
if (view.valid_bits_per_sample == 0)
view.valid_bits_per_sample = view.bits_per_sample;
} else {
if (wfx->wFormatTag == WAVE_FORMAT_PCM) {
view.subformat = GST_KSDATAFORMAT_SUBTYPE_PCM;
@ -870,6 +918,7 @@ make_view (const WAVEFORMATEX * wfx)
view.subformat = GST_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
}
view.raw_valid_bits_per_sample = view.bits_per_sample;
view.valid_bits_per_sample = view.bits_per_sample;
view.channel_mask = 0;
}
@ -936,6 +985,12 @@ compare_wfx_func (gconstpointer pa, gconstpointer pb, gpointer user_data)
FormatView b = make_view (B);
FormatView basis = make_view (basis_wfx);
/* S24_32LE is the lowest */
gboolean a_s2432 = is_s24_in_32 (&a);
gboolean b_s2432 = is_s24_in_32 (&b);
if (a_s2432 != b_s2432)
return a_s2432 ? 1 : -1;
/* Prefer same channel */
gint dch_a = abs ((gint) a.channels - (gint) basis.channels);
gint dch_b = abs ((gint) b.channels - (gint) basis.channels);
@ -950,6 +1005,16 @@ compare_wfx_func (gconstpointer pa, gconstpointer pb, gpointer user_data)
if (dra != drb)
return (dra < drb) ? -1 : 1;
/* Prefere higher sample rate */
if (a.sample_rate != b.sample_rate)
return (a.sample_rate > b.sample_rate) ? -1 : +1;
/* High bit first */
gint a_bits = effective_bits (&a);
gint b_bits = effective_bits (&b);
if (a_bits != b_bits)
return (a_bits > b_bits) ? -1 : +1;
/* format compare */
gint fcmp = compare_format_similarity (&a, &b, &basis);
if (fcmp != 0)
@ -958,6 +1023,37 @@ compare_wfx_func (gconstpointer pa, gconstpointer pb, gpointer user_data)
return 0;
}
/* *INDENT-OFF* */
static void
demote_s24_32le (GPtrArray *list)
{
if (!list || list->len == 0)
return;
std::vector<gpointer> head;
std::vector<gpointer> tail;
head.reserve (list->len);
tail.reserve (list->len);
for (guint i = 0; i < list->len; i++) {
auto wfx = (WAVEFORMATEX *) g_ptr_array_index (list, i);
FormatView v = make_view (wfx);
if (is_s24_in_32 (&v))
tail.push_back ((gpointer) wfx);
else
head.push_back ((gpointer) wfx);
}
guint idx = 0;
for (gpointer p : head)
list->pdata[idx++] = p;
for (gpointer p : tail)
list->pdata[idx++] = p;
}
/* *INDENT-ON* */
void
gst_wasapi2_sort_wfx (GPtrArray * list, WAVEFORMATEX * wfx)
{
@ -965,4 +1061,5 @@ gst_wasapi2_sort_wfx (GPtrArray * list, WAVEFORMATEX * wfx)
return;
g_ptr_array_sort_with_data (list, compare_wfx_func, wfx);
demote_s24_32le (list);
}