wasapi2: Enumerate supported shared mode formats

... and report it via device provider property

Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/9586>
This commit is contained in:
Seungha Yang 2025-08-22 08:26:11 +09:00 committed by GStreamer Marge Bot
parent c97ed1e6f6
commit d9279a5145
4 changed files with 118 additions and 62 deletions

View File

@ -261,14 +261,14 @@ struct GstWasapi2EnumeratorPrivate
{
device_list = g_ptr_array_new_with_free_func ((GDestroyNotify)
gst_wasapi2_enumerator_entry_free);
exclusive_formats = g_ptr_array_new_with_free_func ((GDestroyNotify)
endpoint_formats = g_ptr_array_new_with_free_func ((GDestroyNotify)
gst_wasapi2_free_wfx);
}
~GstWasapi2EnumeratorPrivate ()
{
g_ptr_array_unref (device_list);
g_ptr_array_unref (exclusive_formats);
g_ptr_array_unref (endpoint_formats);
}
ComPtr<IMMDeviceEnumerator> handle;
@ -280,7 +280,7 @@ struct GstWasapi2EnumeratorPrivate
Wasapi2ActivationHandler *render_activator = nullptr;
std::atomic<int> notify_count = { 0 };
GPtrArray *device_list;
GPtrArray *exclusive_formats;
GPtrArray *endpoint_formats;
void ClearCOM ()
{
@ -557,39 +557,16 @@ struct EnumerateData
static GstWasapi2EnumeratorEntry *
gst_wasapi2_enumerator_build_entry (GstWasapi2Enumerator * self,
IAudioClient * client, EDataFlow flow, gboolean is_default,
GstCaps * caps, EDataFlow flow, gboolean is_default,
gchar * device_id, gchar * device_name,
gchar * actual_device_id, gchar * actual_device_name,
GstWasapi2DeviceProps * device_props)
{
WAVEFORMATEX *mix_format = nullptr;
GstCaps *supported_caps = nullptr;
client->GetMixFormat (&mix_format);
if (!mix_format) {
g_free (device_id);
g_free (device_name);
g_free (actual_device_id);
g_free (actual_device_name);
return nullptr;
}
gst_wasapi2_util_parse_waveformatex (mix_format, &supported_caps, nullptr);
CoTaskMemFree (mix_format);
if (!supported_caps) {
g_free (device_id);
g_free (device_name);
g_free (actual_device_id);
g_free (actual_device_name);
return nullptr;
}
auto entry = new GstWasapi2EnumeratorEntry ();
entry->device_id = device_id;
entry->device_name = device_name;
entry->caps = supported_caps;
entry->caps = caps;
entry->flow = flow;
entry->is_default = is_default;
if (actual_device_id)
@ -603,7 +580,7 @@ gst_wasapi2_enumerator_build_entry (GstWasapi2Enumerator * self,
}
GST_LOG_OBJECT (self, "Adding entry %s (%s), flow %d, caps %" GST_PTR_FORMAT,
device_id, device_name, flow, supported_caps);
device_id, device_name, flow, caps);
g_free (device_id);
g_free (device_name);
g_free (actual_device_id);
@ -736,15 +713,23 @@ gst_wasapi2_enumerator_execute (GstWasapi2Enumerator * self,
if (default_capture_prop)
gst_wasapi2_enumerator_probe_props (default_capture_prop.Get (), &props);
auto entry = gst_wasapi2_enumerator_build_entry (self,
default_capture_client.Get (), eCapture, TRUE,
g_strdup (gst_wasapi2_get_default_device_id (eCapture)),
g_strdup ("Default Audio Capture Device"),
g_strdup (default_capture_device_id),
g_strdup (default_capture_device_name), &props);
g_ptr_array_set_size (priv->endpoint_formats, 0);
gst_wasapi2_get_shared_mode_formats (default_capture_client.Get (),
priv->endpoint_formats);
auto caps = gst_wasapi2_wfx_list_to_caps (priv->endpoint_formats);
g_ptr_array_set_size (priv->endpoint_formats, 0);
if (entry)
g_ptr_array_add (priv->device_list, entry);
if (caps) {
auto entry = gst_wasapi2_enumerator_build_entry (self,
caps, eCapture, TRUE,
g_strdup (gst_wasapi2_get_default_device_id (eCapture)),
g_strdup ("Default Audio Capture Device"),
g_strdup (default_capture_device_id),
g_strdup (default_capture_device_name), &props);
if (entry)
g_ptr_array_add (priv->device_list, entry);
}
}
if (default_render_client) {
@ -755,15 +740,23 @@ gst_wasapi2_enumerator_execute (GstWasapi2Enumerator * self,
if (default_render_prop)
gst_wasapi2_enumerator_probe_props (default_render_prop.Get (), &props);
auto entry = gst_wasapi2_enumerator_build_entry (self,
default_render_client.Get (), eRender, TRUE,
g_strdup (gst_wasapi2_get_default_device_id (eRender)),
g_strdup ("Default Audio Render Device"),
g_strdup (default_render_device_id),
g_strdup (default_render_device_name), &props);
g_ptr_array_set_size (priv->endpoint_formats, 0);
gst_wasapi2_get_shared_mode_formats (default_render_client.Get (),
priv->endpoint_formats);
auto caps = gst_wasapi2_wfx_list_to_caps (priv->endpoint_formats);
g_ptr_array_set_size (priv->endpoint_formats, 0);
if (entry)
g_ptr_array_add (priv->device_list, entry);
if (caps) {
auto entry = gst_wasapi2_enumerator_build_entry (self,
caps, eRender, TRUE,
g_strdup (gst_wasapi2_get_default_device_id (eRender)),
g_strdup ("Default Audio Render Device"),
g_strdup (default_render_device_id),
g_strdup (default_render_device_name), &props);
if (entry)
g_ptr_array_add (priv->device_list, entry);
}
}
for (UINT i = 0; i < count; i++) {
@ -832,18 +825,26 @@ gst_wasapi2_enumerator_execute (GstWasapi2Enumerator * self,
gst_wasapi2_enumerator_probe_props (prop.Get (), &props);
auto entry = gst_wasapi2_enumerator_build_entry (self, client.Get (), flow,
FALSE, device_id, desc, nullptr, nullptr, &props);
if (entry) {
g_ptr_array_set_size (priv->exclusive_formats, 0);
gst_wasapi2_get_exclusive_formats (client.Get (),
prop.Get (), priv->exclusive_formats);
auto exclusive_caps =
gst_wasapi2_wfx_list_to_caps (priv->exclusive_formats);
g_ptr_array_set_size (priv->exclusive_formats, 0);
g_ptr_array_set_size (priv->endpoint_formats, 0);
gst_wasapi2_get_shared_mode_formats (default_render_client.Get (),
priv->endpoint_formats);
auto caps = gst_wasapi2_wfx_list_to_caps (priv->endpoint_formats);
g_ptr_array_set_size (priv->endpoint_formats, 0);
entry->exclusive_caps = exclusive_caps;
g_ptr_array_add (priv->device_list, entry);
if (caps) {
auto entry = gst_wasapi2_enumerator_build_entry (self, caps, flow,
FALSE, device_id, desc, nullptr, nullptr, &props);
if (entry) {
g_ptr_array_set_size (priv->endpoint_formats, 0);
gst_wasapi2_get_exclusive_mode_formats (client.Get (),
prop.Get (), priv->endpoint_formats);
auto exclusive_caps =
gst_wasapi2_wfx_list_to_caps (priv->endpoint_formats);
g_ptr_array_set_size (priv->endpoint_formats, 0);
entry->exclusive_caps = exclusive_caps;
g_ptr_array_add (priv->device_list, entry);
}
}
}

View File

@ -813,7 +813,7 @@ gst_wasapi2_device_manager_create_ctx (IMMDeviceEnumerator * enumerator,
if (!gst_wasapi2_result (hr))
return;
gst_wasapi2_get_exclusive_formats (ctx->client.Get (), prop.Get (),
gst_wasapi2_get_exclusive_mode_formats (ctx->client.Get (), prop.Get (),
exclusive_formats);
if (exclusive_formats->len == 0) {
GST_WARNING ("Couldn't get exclusive mode formats");

View File

@ -717,7 +717,7 @@ make_wfx_ext (DWORD nSamplesPerSec, WORD nChannels, WORD wBitsPerSample,
/* *INDENT-OFF* */
gboolean
gst_wasapi2_get_exclusive_formats (IAudioClient * client,
gst_wasapi2_get_exclusive_mode_formats (IAudioClient * client,
IPropertyStore * props, GPtrArray * list)
{
PROPVARIANT var;
@ -766,8 +766,8 @@ gst_wasapi2_get_exclusive_formats (IAudioClient * client,
const DepthPair depth_pairs[] = {
{32, 32, true}, /* 32-float */
{32, 32, false}, /* 32-int */
{24, 24, false}, /* 24-packed */
{16, 16, false}, /* 16-int */
{24, 24, false}, /* 24-packed */
{32, 24, false}, /* 24-in-32 */
};
@ -806,6 +806,58 @@ gst_wasapi2_get_exclusive_formats (IAudioClient * client,
return TRUE;
}
gboolean
gst_wasapi2_get_shared_mode_formats (IAudioClient * client, GPtrArray * list)
{
PROPVARIANT var;
PropVariantInit (&var);
WAVEFORMATEX *mix_format = nullptr;
WAVEFORMATEX *closest = nullptr;
auto hr = client->GetMixFormat (&mix_format);
if (!gst_wasapi2_result (hr))
return FALSE;
g_ptr_array_add (list, gst_wasapi2_copy_wfx (mix_format));
/* Checks using pre-defined format list */
struct DepthPair
{
WORD wBitsPerSample;
WORD wValidBitsPerSample;
bool is_float;
};
const DepthPair depth_pairs[] = {
{32, 32, true}, /* 32-float */
{32, 32, false}, /* 32-int */
{16, 16, false}, /* 16-int */
{24, 24, false}, /* 24-packed */
};
const DWORD rates[] = { 192000, 176400, 96000, 88200, 48000, 44100 };
for (auto r : rates) {
for (auto d : depth_pairs) {
auto wfx = make_wfx_ext (r, mix_format->nChannels, d.wBitsPerSample,
d.wValidBitsPerSample, d.is_float);
hr = client->IsFormatSupported (AUDCLNT_SHAREMODE_SHARED,
(WAVEFORMATEX *) &wfx, &closest);
if (hr == S_OK) {
g_ptr_array_add (list, gst_wasapi2_copy_wfx ((WAVEFORMATEX *) &wfx));
} else if (hr == S_FALSE && closest) {
g_ptr_array_add (list, closest);
closest = nullptr;
}
}
}
gst_wasapi2_sort_wfx (list, mix_format);
gst_wasapi2_free_wfx (mix_format);
return TRUE;
}
GstCaps *
gst_wasapi2_wfx_list_to_caps (GPtrArray * list)
{

View File

@ -126,9 +126,12 @@ void gst_wasapi2_clear_wfx (WAVEFORMATEX ** wfx);
WAVEFORMATEX * gst_wasapi2_copy_wfx (WAVEFORMATEX * format);
gboolean gst_wasapi2_get_exclusive_formats (IAudioClient * client,
IPropertyStore * props,
GPtrArray * list);
gboolean gst_wasapi2_get_exclusive_mode_formats (IAudioClient * client,
IPropertyStore * props,
GPtrArray * list);
gboolean gst_wasapi2_get_shared_mode_formats (IAudioClient * client,
GPtrArray * list);
GstCaps * gst_wasapi2_wfx_list_to_caps (GPtrArray * list);