m-default-nodes: lift restrictions on media.class for audio nodes
Previously a default source had to be Audio/Source and a default sink had to be Audio/Sink. This did not account for: - monitor sources (Audio/Sink) - duplex nodes (Audio/Duplex) - virtual sources (Audio/Source/Virtual) With this change the restriction is now on the number of input/output ports, so any Audio/* node with output ports is a valid target to select as a default source, while any Audio/* node with input ports is a valid target for a default sink. Fixes #60
This commit is contained in:
@@ -156,7 +156,7 @@ wp_default_nodes_api_get_default_node (WpDefaultNodesApi * self,
|
||||
{
|
||||
gint node_t = -1;
|
||||
for (gint i = 0; i < N_DEFAULT_NODES; i++) {
|
||||
if (!g_strcmp0 (media_class, MEDIA_CLASS[i])) {
|
||||
if (!g_strcmp0 (media_class, NODE_TYPE_STR[i])) {
|
||||
node_t = i;
|
||||
break;
|
||||
}
|
||||
@@ -166,8 +166,6 @@ wp_default_nodes_api_get_default_node (WpDefaultNodesApi * self,
|
||||
WP_TYPE_NODE,
|
||||
WP_CONSTRAINT_TYPE_PW_PROPERTY,
|
||||
PW_KEY_NODE_NAME, "=s", self->defaults[node_t],
|
||||
WP_CONSTRAINT_TYPE_PW_PROPERTY,
|
||||
PW_KEY_MEDIA_CLASS, "=s", MEDIA_CLASS[node_t],
|
||||
NULL);
|
||||
if (node)
|
||||
return wp_proxy_get_bound_id (WP_PROXY (node));
|
||||
|
@@ -10,7 +10,7 @@
|
||||
#include <errno.h>
|
||||
#include <pipewire/keys.h>
|
||||
|
||||
#define DEFAULT_CONFIG_KEYS 1
|
||||
#define COMPILING_MODULE_DEFAULT_NODES 1
|
||||
#include "module-default-nodes/common.h"
|
||||
|
||||
#define NAME "default-nodes"
|
||||
@@ -39,6 +39,7 @@ struct _WpDefaultNodes
|
||||
WpObjectManager *metadata_om;
|
||||
WpObjectManager *nodes_om;
|
||||
GSource *timeout_source;
|
||||
GSource *idle_source;
|
||||
|
||||
/* properties */
|
||||
guint save_interval_ms;
|
||||
@@ -107,7 +108,8 @@ find_highest_priority_node (WpDefaultNodes * self, gint node_t)
|
||||
WpNode *res = NULL;
|
||||
|
||||
it = wp_object_manager_new_filtered_iterator (self->nodes_om, WP_TYPE_NODE,
|
||||
WP_CONSTRAINT_TYPE_PW_PROPERTY, PW_KEY_MEDIA_CLASS, "=s", MEDIA_CLASS[node_t],
|
||||
WP_CONSTRAINT_TYPE_PW_PROPERTY, PW_KEY_MEDIA_CLASS, "#s", MEDIA_CLASS_MATCH[node_t],
|
||||
WP_CONSTRAINT_TYPE_G_PROPERTY, N_PORTS_KEY[node_t], "!u", 0,
|
||||
NULL);
|
||||
|
||||
for (; wp_iterator_next (it, &val); g_value_unset (&val)) {
|
||||
@@ -136,7 +138,8 @@ reevaluate_default_node (WpDefaultNodes * self, WpMetadata *m, gint node_t)
|
||||
if (node_name) {
|
||||
node = wp_object_manager_lookup (self->nodes_om, WP_TYPE_NODE,
|
||||
WP_CONSTRAINT_TYPE_PW_PROPERTY, PW_KEY_NODE_NAME, "=s", node_name,
|
||||
WP_CONSTRAINT_TYPE_PW_PROPERTY, PW_KEY_MEDIA_CLASS, "=s", MEDIA_CLASS[node_t],
|
||||
WP_CONSTRAINT_TYPE_PW_PROPERTY, PW_KEY_MEDIA_CLASS, "#s", MEDIA_CLASS_MATCH[node_t],
|
||||
WP_CONSTRAINT_TYPE_G_PROPERTY, N_PORTS_KEY[node_t], "!u", 0,
|
||||
NULL);
|
||||
}
|
||||
|
||||
@@ -156,7 +159,7 @@ reevaluate_default_node (WpDefaultNodes * self, WpMetadata *m, gint node_t)
|
||||
self->defaults[node_t].value = g_strdup (node_name);
|
||||
|
||||
wp_info_object (self, "set default node for %s: %s",
|
||||
MEDIA_CLASS[node_t], node_name);
|
||||
NODE_TYPE_STR[node_t], node_name);
|
||||
|
||||
g_snprintf (buf, sizeof(buf), "{ \"name\": \"%s\" }", node_name);
|
||||
wp_metadata_set (m, 0, DEFAULT_KEY[node_t], "Spa:String:JSON", buf);
|
||||
@@ -203,21 +206,46 @@ on_metadata_changed (WpMetadata *m, guint32 subject,
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
on_nodes_changed (WpObjectManager * om, WpDefaultNodes * self)
|
||||
static gboolean
|
||||
rescan (WpDefaultNodes * self)
|
||||
{
|
||||
g_autoptr (WpMetadata) metadata = NULL;
|
||||
|
||||
g_clear_pointer (&self->idle_source, g_source_unref);
|
||||
|
||||
/* Get the metadata */
|
||||
metadata = wp_object_manager_lookup (self->metadata_om, WP_TYPE_METADATA,
|
||||
NULL);
|
||||
if (!metadata)
|
||||
return;
|
||||
return G_SOURCE_REMOVE;
|
||||
|
||||
wp_trace_object (om, "nodes changed, re-evaluating defaults");
|
||||
wp_trace_object (self, "nodes changed, re-evaluating defaults");
|
||||
reevaluate_default_node (self, metadata, AUDIO_SINK);
|
||||
reevaluate_default_node (self, metadata, AUDIO_SOURCE);
|
||||
reevaluate_default_node (self, metadata, VIDEO_SOURCE);
|
||||
|
||||
return G_SOURCE_REMOVE;
|
||||
}
|
||||
|
||||
static void
|
||||
schedule_rescan (WpDefaultNodes * self)
|
||||
{
|
||||
if (!self->idle_source) {
|
||||
g_autoptr (WpCore) core = wp_object_get_core (WP_OBJECT (self));
|
||||
g_return_if_fail (core);
|
||||
|
||||
wp_core_idle_add_closure (core, &self->idle_source,
|
||||
g_cclosure_new_object (G_CALLBACK (rescan), G_OBJECT (self)));
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
on_node_added (WpObjectManager * om, WpNode * node, WpDefaultNodes * self)
|
||||
{
|
||||
g_signal_connect_object (node, "notify::n-input-ports",
|
||||
G_CALLBACK (schedule_rescan), self, G_CONNECT_SWAPPED);
|
||||
g_signal_connect_object (node, "notify::n-output-ports",
|
||||
G_CALLBACK (schedule_rescan), self, G_CONNECT_SWAPPED);
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -246,8 +274,12 @@ on_metadata_added (WpObjectManager *om, WpMetadata *metadata, gpointer d)
|
||||
wp_object_manager_add_interest (self->nodes_om, WP_TYPE_NODE, NULL);
|
||||
wp_object_manager_request_object_features (self->nodes_om, WP_TYPE_NODE,
|
||||
WP_PIPEWIRE_OBJECT_FEATURES_MINIMAL);
|
||||
g_signal_connect_object (self->nodes_om, "objects-changed",
|
||||
G_CALLBACK (on_nodes_changed), self, 0);
|
||||
g_signal_connect_object (self->nodes_om, "object-added",
|
||||
G_CALLBACK (on_node_added), self, 0);
|
||||
g_signal_connect_object (self->nodes_om, "object-added",
|
||||
G_CALLBACK (schedule_rescan), self, G_CONNECT_SWAPPED);
|
||||
g_signal_connect_object (self->nodes_om, "object-removed",
|
||||
G_CALLBACK (schedule_rescan), self, G_CONNECT_SWAPPED);
|
||||
wp_core_install_object_manager (core, self->nodes_om);
|
||||
}
|
||||
|
||||
@@ -282,6 +314,11 @@ wp_default_nodes_disable (WpPlugin * plugin)
|
||||
{
|
||||
WpDefaultNodes * self = WP_DEFAULT_NODES (plugin);
|
||||
|
||||
/* Clear the current rescan callback */
|
||||
if (self->idle_source)
|
||||
g_source_destroy (self->idle_source);
|
||||
g_clear_pointer (&self->idle_source, g_source_unref);
|
||||
|
||||
/* Clear the current timeout callback */
|
||||
if (self->timeout_source)
|
||||
g_source_destroy (self->timeout_source);
|
||||
|
@@ -23,20 +23,34 @@ static const gchar * DEFAULT_KEY[N_DEFAULT_NODES] = {
|
||||
[VIDEO_SOURCE] = "default.video.source",
|
||||
};
|
||||
|
||||
#if DEFAULT_CONFIG_KEYS
|
||||
static const gchar * NODE_TYPE_STR[N_DEFAULT_NODES] = {
|
||||
[AUDIO_SINK] = "Audio/Sink",
|
||||
[AUDIO_SOURCE] = "Audio/Source",
|
||||
[VIDEO_SOURCE] = "Video/Source",
|
||||
};
|
||||
|
||||
#if COMPILING_MODULE_DEFAULT_NODES
|
||||
|
||||
static const gchar * DEFAULT_CONFIG_KEY[N_DEFAULT_NODES] = {
|
||||
[AUDIO_SINK] = "default.configured.audio.sink",
|
||||
[AUDIO_SOURCE] = "default.configured.audio.source",
|
||||
[VIDEO_SOURCE] = "default.configured.video.source",
|
||||
};
|
||||
#endif
|
||||
|
||||
static const gchar * MEDIA_CLASS[N_DEFAULT_NODES] = {
|
||||
[AUDIO_SINK] = "Audio/Sink",
|
||||
[AUDIO_SOURCE] = "Audio/Source",
|
||||
[VIDEO_SOURCE] = "Video/Source",
|
||||
static const gchar * MEDIA_CLASS_MATCH[N_DEFAULT_NODES] = {
|
||||
[AUDIO_SINK] = "Audio/*",
|
||||
[AUDIO_SOURCE] = "Audio/*",
|
||||
[VIDEO_SOURCE] = "Video/*",
|
||||
};
|
||||
|
||||
static const gchar * N_PORTS_KEY[N_DEFAULT_NODES] = {
|
||||
[AUDIO_SINK] = "n-input-ports",
|
||||
[AUDIO_SOURCE] = "n-output-ports",
|
||||
[VIDEO_SOURCE] = "n-output-ports",
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
static int
|
||||
json_object_find (const char *obj, const char *key, char *value, size_t len)
|
||||
{
|
||||
|
Reference in New Issue
Block a user