wasapi2deviceprovider: Probe device form factor and enumerator name
Adding form factor and enumerator information to device property struct Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/9326>
This commit is contained in:
parent
9dbc7a491e
commit
fc861119ed
@ -42,6 +42,7 @@ struct _GstWasapi2Device
|
||||
gchar *device_id;
|
||||
const gchar *factory_name;
|
||||
GstWasapi2EndpointClass device_class;
|
||||
gboolean is_default;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE (GstWasapi2Device, gst_wasapi2_device, GST_TYPE_DEVICE);
|
||||
@ -216,24 +217,43 @@ gst_wasapi2_device_provider_probe (GstDeviceProvider * provider)
|
||||
|
||||
auto props = gst_structure_new ("wasapi2-proplist",
|
||||
"device.api", G_TYPE_STRING, "wasapi2",
|
||||
"device.id", G_TYPE_STRING, entry->device_id,
|
||||
"device.id", G_TYPE_STRING, entry->device_id.c_str (),
|
||||
"device.default", G_TYPE_BOOLEAN, entry->is_default,
|
||||
"wasapi2.device.description", G_TYPE_STRING, entry->device_name,
|
||||
nullptr);
|
||||
"wasapi2.device.description", G_TYPE_STRING,
|
||||
entry->device_name.c_str (),
|
||||
"device.form-factor", G_TYPE_INT,
|
||||
(gint) entry->device_props.form_factor,
|
||||
"device.form-factor-name", G_TYPE_STRING,
|
||||
gst_wasapi2_form_factor_to_string (entry->device_props.form_factor),
|
||||
"device.enumerator-name", G_TYPE_STRING,
|
||||
entry->device_props.enumerator_name.c_str (), nullptr);
|
||||
|
||||
if (entry->is_default) {
|
||||
if (!entry->actual_device_id.empty ()) {
|
||||
gst_structure_set (props, "device.actual-id", G_TYPE_STRING,
|
||||
entry->actual_device_id.c_str (), nullptr);
|
||||
}
|
||||
|
||||
if (!entry->actual_device_name.empty ()) {
|
||||
gst_structure_set (props, "device.actual-name", G_TYPE_STRING,
|
||||
entry->actual_device_name.c_str (), nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
if (entry->flow == eCapture) {
|
||||
gst_structure_set (props,
|
||||
"wasapi2.device.loopback", G_TYPE_BOOLEAN, FALSE, nullptr);
|
||||
|
||||
auto device = (GstDevice *) g_object_new (GST_TYPE_WASAPI2_DEVICE,
|
||||
"device", entry->device_id,
|
||||
"display-name", entry->device_name, "caps", entry->caps,
|
||||
"device", entry->device_id.c_str (),
|
||||
"display-name", entry->device_name.c_str (), "caps", entry->caps,
|
||||
"device-class", "Audio/Source", "properties", props, nullptr);
|
||||
gst_structure_free (props);
|
||||
|
||||
GST_WASAPI2_DEVICE (device)->factory_name = "wasapi2src";
|
||||
GST_WASAPI2_DEVICE (device)->device_class =
|
||||
GST_WASAPI2_ENDPOINT_CLASS_CAPTURE;
|
||||
auto wasapi2_dev = GST_WASAPI2_DEVICE (device);
|
||||
wasapi2_dev->factory_name = "wasapi2src";
|
||||
wasapi2_dev->device_class = GST_WASAPI2_ENDPOINT_CLASS_CAPTURE;
|
||||
wasapi2_dev->is_default = entry->is_default;
|
||||
|
||||
devices = g_list_append (devices, device);
|
||||
} else {
|
||||
@ -242,26 +262,28 @@ gst_wasapi2_device_provider_probe (GstDeviceProvider * provider)
|
||||
"wasapi2.device.loopback", G_TYPE_BOOLEAN, TRUE, nullptr);
|
||||
|
||||
auto device = (GstDevice *) g_object_new (GST_TYPE_WASAPI2_DEVICE,
|
||||
"device", entry->device_id,
|
||||
"display-name", entry->device_name, "caps", entry->caps,
|
||||
"device", entry->device_id.c_str (),
|
||||
"display-name", entry->device_name.c_str (), "caps", entry->caps,
|
||||
"device-class", "Audio/Sink", "properties", props, nullptr);
|
||||
gst_structure_free (props);
|
||||
|
||||
GST_WASAPI2_DEVICE (device)->factory_name = "wasapi2sink";
|
||||
GST_WASAPI2_DEVICE (device)->device_class =
|
||||
GST_WASAPI2_ENDPOINT_CLASS_RENDER;
|
||||
auto wasapi2_dev = GST_WASAPI2_DEVICE (device);
|
||||
wasapi2_dev->factory_name = "wasapi2sink";
|
||||
wasapi2_dev->device_class = GST_WASAPI2_ENDPOINT_CLASS_RENDER;
|
||||
wasapi2_dev->is_default = entry->is_default;
|
||||
|
||||
devices = g_list_append (devices, device);
|
||||
|
||||
device = (GstDevice *) g_object_new (GST_TYPE_WASAPI2_DEVICE,
|
||||
"device", entry->device_id,
|
||||
"display-name", entry->device_name, "caps", entry->caps,
|
||||
"device", entry->device_id.c_str (),
|
||||
"display-name", entry->device_name.c_str (), "caps", entry->caps,
|
||||
"device-class", "Audio/Source", "properties", prop_copy, nullptr);
|
||||
gst_structure_free (prop_copy);
|
||||
|
||||
GST_WASAPI2_DEVICE (device)->factory_name = "wasapi2src";
|
||||
GST_WASAPI2_DEVICE (device)->device_class =
|
||||
GST_WASAPI2_ENDPOINT_CLASS_LOOPBACK_CAPTURE;
|
||||
wasapi2_dev = GST_WASAPI2_DEVICE (device);
|
||||
wasapi2_dev->factory_name = "wasapi2src";
|
||||
wasapi2_dev->device_class = GST_WASAPI2_ENDPOINT_CLASS_LOOPBACK_CAPTURE;
|
||||
wasapi2_dev->is_default = entry->is_default;
|
||||
|
||||
devices = g_list_append (devices, device);
|
||||
}
|
||||
@ -342,7 +364,7 @@ gst_wasapi2_device_provider_update_devices (GstWasapi2DeviceProvider * self)
|
||||
GList *new_devices = nullptr;
|
||||
GList *to_add = nullptr;
|
||||
GList *to_remove = nullptr;
|
||||
GList *iter;
|
||||
GList *iter, *walk;
|
||||
|
||||
GST_OBJECT_LOCK (self);
|
||||
prev_devices = g_list_copy_deep (provider->devices,
|
||||
@ -371,6 +393,44 @@ gst_wasapi2_device_provider_update_devices (GstWasapi2DeviceProvider * self)
|
||||
}
|
||||
}
|
||||
|
||||
iter = to_remove;
|
||||
while (iter) {
|
||||
auto prev_dev = GST_WASAPI2_DEVICE (iter->data);
|
||||
|
||||
if (!prev_dev->is_default) {
|
||||
iter = g_list_next (iter);
|
||||
continue;
|
||||
}
|
||||
|
||||
walk = to_add;
|
||||
bool found = false;
|
||||
while (walk) {
|
||||
auto new_dev = GST_WASAPI2_DEVICE (walk->data);
|
||||
|
||||
if (!new_dev->is_default ||
|
||||
prev_dev->device_class != new_dev->device_class) {
|
||||
walk = g_list_next (walk);
|
||||
continue;
|
||||
}
|
||||
|
||||
gst_device_provider_device_changed (provider, GST_DEVICE (new_dev),
|
||||
GST_DEVICE (prev_dev));
|
||||
gst_object_unref (new_dev);
|
||||
to_add = g_list_delete_link (to_add, walk);
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (found) {
|
||||
gst_object_unref (prev_dev);
|
||||
auto next = iter->next;
|
||||
to_remove = g_list_delete_link (to_remove, iter);
|
||||
iter = next;
|
||||
} else {
|
||||
iter = g_list_next (iter);
|
||||
}
|
||||
}
|
||||
|
||||
for (iter = to_remove; iter; iter = g_list_next (iter))
|
||||
gst_device_provider_device_remove (provider, GST_DEVICE (iter->data));
|
||||
|
||||
|
@ -433,10 +433,7 @@ gst_wasapi2_enumerator_activate_notification (GstWasapi2Enumerator * object,
|
||||
void
|
||||
gst_wasapi2_enumerator_entry_free (GstWasapi2EnumeratorEntry * entry)
|
||||
{
|
||||
g_free (entry->device_id);
|
||||
g_free (entry->device_name);
|
||||
gst_clear_caps (&entry->caps);
|
||||
g_free (entry);
|
||||
delete entry;
|
||||
}
|
||||
|
||||
/* *INDENT-OFF* */
|
||||
@ -462,7 +459,9 @@ static void
|
||||
gst_wasapi2_enumerator_add_entry (GstWasapi2Enumerator * self,
|
||||
IAudioClient * client,
|
||||
GstCaps * static_caps, EDataFlow flow, gboolean is_default,
|
||||
gchar * device_id, gchar * device_name, GPtrArray * device_list)
|
||||
gchar * device_id, gchar * device_name,
|
||||
gchar * actual_device_id, gchar * actual_device_name,
|
||||
GstWasapi2DeviceProps * device_props, GPtrArray * device_list)
|
||||
{
|
||||
WAVEFORMATEX *mix_format = nullptr;
|
||||
GstCaps *supported_caps = nullptr;
|
||||
@ -471,6 +470,8 @@ gst_wasapi2_enumerator_add_entry (GstWasapi2Enumerator * self,
|
||||
if (!mix_format) {
|
||||
g_free (device_id);
|
||||
g_free (device_name);
|
||||
g_free (actual_device_id);
|
||||
g_free (actual_device_name);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -481,23 +482,103 @@ gst_wasapi2_enumerator_add_entry (GstWasapi2Enumerator * self,
|
||||
if (!supported_caps) {
|
||||
g_free (device_id);
|
||||
g_free (device_name);
|
||||
g_free (actual_device_id);
|
||||
g_free (actual_device_name);
|
||||
return;
|
||||
}
|
||||
|
||||
auto entry = g_new0 (GstWasapi2EnumeratorEntry, 1);
|
||||
auto entry = new GstWasapi2EnumeratorEntry ();
|
||||
|
||||
entry->device_id = device_id;
|
||||
entry->device_name = device_name;
|
||||
entry->caps = supported_caps;
|
||||
entry->flow = flow;
|
||||
entry->is_default = is_default;
|
||||
if (actual_device_id)
|
||||
entry->actual_device_id = actual_device_id;
|
||||
if (actual_device_name)
|
||||
entry->actual_device_name = actual_device_name;
|
||||
|
||||
if (device_props) {
|
||||
entry->device_props.form_factor = device_props->form_factor;
|
||||
entry->device_props.enumerator_name = device_props->enumerator_name;
|
||||
}
|
||||
|
||||
GST_LOG_OBJECT (self, "Adding entry %s (%s), flow %d, caps %" GST_PTR_FORMAT,
|
||||
device_id, device_name, flow, supported_caps);
|
||||
g_free (device_id);
|
||||
g_free (device_name);
|
||||
g_free (actual_device_id);
|
||||
g_free (actual_device_name);
|
||||
|
||||
g_ptr_array_add (device_list, entry);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_wasapi2_enumerator_probe_props (IPropertyStore * store,
|
||||
GstWasapi2DeviceProps * props)
|
||||
{
|
||||
PROPVARIANT var;
|
||||
PropVariantInit (&var);
|
||||
|
||||
auto hr = store->GetValue (PKEY_AudioEndpoint_FormFactor, &var);
|
||||
if (SUCCEEDED (hr) && var.vt == VT_UI4)
|
||||
props->form_factor = (EndpointFormFactor) var.ulVal;
|
||||
|
||||
PropVariantClear (&var);
|
||||
|
||||
hr = store->GetValue (PKEY_Device_EnumeratorName, &var);
|
||||
if (SUCCEEDED (hr) && var.vt == VT_LPWSTR) {
|
||||
auto name = g_utf16_to_utf8 ((gunichar2 *) var.pwszVal,
|
||||
-1, nullptr, nullptr, nullptr);
|
||||
props->enumerator_name = name;
|
||||
g_free (name);
|
||||
}
|
||||
|
||||
PropVariantClear (&var);
|
||||
}
|
||||
|
||||
static void
|
||||
probe_default_device_props (GstWasapi2Enumerator * self, EDataFlow flow,
|
||||
GstWasapi2DeviceProps * props, gchar ** actual_device_id,
|
||||
gchar ** actual_device_name)
|
||||
{
|
||||
auto priv = self->priv;
|
||||
ComPtr < IMMDevice > device;
|
||||
ComPtr < IPropertyStore > prop;
|
||||
|
||||
*actual_device_id = nullptr;
|
||||
*actual_device_name = nullptr;
|
||||
|
||||
auto hr = priv->handle->GetDefaultAudioEndpoint (flow,
|
||||
eConsole, &device);
|
||||
if (FAILED (hr))
|
||||
return;
|
||||
|
||||
LPWSTR wid = nullptr;
|
||||
hr = device->GetId (&wid);
|
||||
if (SUCCEEDED (hr)) {
|
||||
*actual_device_id = g_utf16_to_utf8 ((gunichar2 *) wid,
|
||||
-1, nullptr, nullptr, nullptr);
|
||||
CoTaskMemFree (wid);
|
||||
}
|
||||
|
||||
hr = device->OpenPropertyStore (STGM_READ, &prop);
|
||||
if (FAILED (hr))
|
||||
return;
|
||||
|
||||
PROPVARIANT var;
|
||||
PropVariantInit (&var);
|
||||
hr = prop->GetValue (PKEY_Device_FriendlyName, &var);
|
||||
if (SUCCEEDED (hr)) {
|
||||
*actual_device_name = g_utf16_to_utf8 ((gunichar2 *) var.pwszVal,
|
||||
-1, nullptr, nullptr, nullptr);
|
||||
PropVariantClear (&var);
|
||||
}
|
||||
|
||||
gst_wasapi2_enumerator_probe_props (prop.Get (), props);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_wasapi2_enumerator_enumerate_internal (EnumerateData * data)
|
||||
{
|
||||
@ -529,17 +610,37 @@ gst_wasapi2_enumerator_enumerate_internal (EnumerateData * data)
|
||||
priv->render_activator->GetClient (&default_render_client, 10000);
|
||||
|
||||
if (default_capture_client) {
|
||||
GstWasapi2DeviceProps props;
|
||||
props.form_factor = UnknownFormFactor;
|
||||
props.enumerator_name = "UNKNOWN";
|
||||
|
||||
gchar *actual_device_id = nullptr;
|
||||
gchar *actual_device_name = nullptr;
|
||||
probe_default_device_props (self, eCapture, &props, &actual_device_id,
|
||||
&actual_device_name);
|
||||
|
||||
gst_wasapi2_enumerator_add_entry (self, default_capture_client.Get (),
|
||||
scaps, eCapture, TRUE,
|
||||
g_strdup (gst_wasapi2_get_default_device_id (eCapture)),
|
||||
g_strdup ("Default Audio Capture Device"), data->device_list);
|
||||
g_strdup ("Default Audio Capture Device"), actual_device_id,
|
||||
actual_device_name, &props, data->device_list);
|
||||
}
|
||||
|
||||
if (default_render_client) {
|
||||
GstWasapi2DeviceProps props;
|
||||
props.form_factor = UnknownFormFactor;
|
||||
props.enumerator_name = "UNKNOWN";
|
||||
|
||||
gchar *actual_device_id = nullptr;
|
||||
gchar *actual_device_name = nullptr;
|
||||
probe_default_device_props (self, eRender, &props, &actual_device_id,
|
||||
&actual_device_name);
|
||||
|
||||
gst_wasapi2_enumerator_add_entry (self, default_render_client.Get (),
|
||||
scaps, eRender, TRUE,
|
||||
g_strdup (gst_wasapi2_get_default_device_id (eRender)),
|
||||
g_strdup ("Default Audio Render Device"), data->device_list);
|
||||
g_strdup ("Default Audio Render Device"), actual_device_id,
|
||||
actual_device_name, &props, data->device_list);
|
||||
}
|
||||
|
||||
for (UINT i = 0; i < count; i++) {
|
||||
@ -547,6 +648,10 @@ gst_wasapi2_enumerator_enumerate_internal (EnumerateData * data)
|
||||
ComPtr < IMMEndpoint > endpoint;
|
||||
EDataFlow flow;
|
||||
|
||||
GstWasapi2DeviceProps props;
|
||||
props.form_factor = UnknownFormFactor;
|
||||
props.enumerator_name = "UNKNOWN";
|
||||
|
||||
hr = collection->Item (i, &device);
|
||||
if (!gst_wasapi2_result (hr))
|
||||
continue;
|
||||
@ -594,8 +699,10 @@ gst_wasapi2_enumerator_enumerate_internal (EnumerateData * data)
|
||||
continue;
|
||||
}
|
||||
|
||||
gst_wasapi2_enumerator_probe_props (prop.Get (), &props);
|
||||
|
||||
gst_wasapi2_enumerator_add_entry (self, client.Get (), scaps, flow, FALSE,
|
||||
device_id, desc, data->device_list);
|
||||
device_id, desc, nullptr, nullptr, &props, data->device_list);
|
||||
}
|
||||
|
||||
gst_caps_unref (scaps);
|
||||
@ -618,3 +725,31 @@ gst_wasapi2_enumerator_enumerate_devices (GstWasapi2Enumerator * object,
|
||||
|
||||
WaitForSingleObject (data.event, INFINITE);
|
||||
}
|
||||
|
||||
const gchar *
|
||||
gst_wasapi2_form_factor_to_string (EndpointFormFactor form_factor)
|
||||
{
|
||||
switch (form_factor) {
|
||||
case RemoteNetworkDevice:
|
||||
return "RemoteNetworkDevice";
|
||||
case Speakers:
|
||||
return "Speakers";
|
||||
case LineLevel:
|
||||
return "LineLevel";
|
||||
case Microphone:
|
||||
return "Microphone";
|
||||
case Headset:
|
||||
return "Headset";
|
||||
case Handset:
|
||||
return "Handset";
|
||||
case UnknownDigitalPassthrough:
|
||||
return "UnknownDigitalPassthrough";
|
||||
case SPDIF:
|
||||
return "SPDIF";
|
||||
case DigitalAudioDisplayDevice:
|
||||
return "DigitalAudioDisplayDevice";
|
||||
case UnknownFormFactor:
|
||||
default:
|
||||
return "UnknownFormFactor";
|
||||
}
|
||||
}
|
||||
|
@ -21,6 +21,7 @@
|
||||
|
||||
#include <gst/gst.h>
|
||||
#include "gstwasapi2util.h"
|
||||
#include <string>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
@ -28,14 +29,30 @@ G_BEGIN_DECLS
|
||||
G_DECLARE_FINAL_TYPE (GstWasapi2Enumerator, gst_wasapi2_enumerator,
|
||||
GST, WASAPI2_ENUMERATOR, GstObject);
|
||||
|
||||
typedef struct _GstWasapi2EnumeratorEntry
|
||||
G_END_DECLS
|
||||
|
||||
struct GstWasapi2DeviceProps
|
||||
{
|
||||
gchar *device_id;
|
||||
gchar *device_name;
|
||||
gboolean is_default;
|
||||
GstCaps *caps;
|
||||
EndpointFormFactor form_factor;
|
||||
std::string enumerator_name;
|
||||
};
|
||||
|
||||
struct GstWasapi2EnumeratorEntry
|
||||
{
|
||||
~GstWasapi2EnumeratorEntry()
|
||||
{
|
||||
gst_clear_caps (&caps);
|
||||
}
|
||||
|
||||
std::string device_id;
|
||||
std::string device_name;
|
||||
std::string actual_device_id;
|
||||
std::string actual_device_name;
|
||||
gboolean is_default = FALSE;
|
||||
GstCaps *caps = nullptr;
|
||||
EDataFlow flow;
|
||||
} GstWasapi2EnumeratorEntry;
|
||||
GstWasapi2DeviceProps device_props = { };
|
||||
};
|
||||
|
||||
GstWasapi2Enumerator * gst_wasapi2_enumerator_new (void);
|
||||
|
||||
@ -47,5 +64,5 @@ void gst_wasapi2_enumerator_entry_free (GstWasapi2EnumeratorEntry * entry);
|
||||
void gst_wasapi2_enumerator_enumerate_devices (GstWasapi2Enumerator * object,
|
||||
GPtrArray * entry);
|
||||
|
||||
G_END_DECLS
|
||||
const gchar * gst_wasapi2_form_factor_to_string (EndpointFormFactor form_factor);
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user