Skip to content
Snippets Groups Projects
Commit ab5a5871 authored by Julian Bouzas's avatar Julian Bouzas
Browse files

m-default-metadata: add support for default audio nodes

parent f86a5efc
No related branches found
No related tags found
No related merge requests found
......@@ -20,25 +20,26 @@
"default.session.endpoint.sink" : "default.session.endpoint.source")
#define default_audio_node_key(dir) ((dir == WP_DIRECTION_INPUT) ? \
"default.audio.sink" : "default.audio.source")
"default.configured.audio.sink" : "default.configured.audio.source")
G_DECLARE_FINAL_TYPE (WpDefaultMetadata, wp_default_metadata, WP,
DEFAULT_METADATA, WpPlugin)
struct _WpDefaultEndpoints
struct _WpDefaultData
{
WpDefaultMetadata *self;
gchar *group;
WpProperties *props;
};
typedef struct _WpDefaultEndpoints WpDefaultEndpoints;
typedef struct _WpDefaultData WpDefaultData;
struct _WpDefaultMetadata
{
WpPlugin parent;
WpState *state;
WpDefaultEndpoints default_endpoints[2];
WpDefaultData default_datas[2][2]; /* AudioNode/Endpoint Input/Output*/
WpObjectManager *metadatas_om;
WpObjectManager *nodes_om;
WpObjectManager *sessions_om;
guint metadata_id;
GSource *timeout_source;
......@@ -49,7 +50,7 @@ G_DEFINE_TYPE (WpDefaultMetadata, wp_default_metadata, WP_TYPE_PLUGIN)
static gboolean
timeout_save_callback (gpointer p)
{
WpDefaultEndpoints *d = p;
WpDefaultData *d = p;
WpDefaultMetadata *self = d->self;
if (!wp_state_save (self->state, d->group, d->props))
......@@ -60,7 +61,8 @@ timeout_save_callback (gpointer p)
}
static void
timeout_save_default_endpoints (WpDefaultMetadata *self, guint dir, guint ms)
timeout_save_default_data (WpDefaultMetadata *self, gboolean is_ep, guint dir,
guint ms)
{
g_autoptr (WpCore) core = wp_object_get_core (WP_OBJECT (self));
g_return_if_fail (core);
......@@ -72,7 +74,7 @@ timeout_save_default_endpoints (WpDefaultMetadata *self, guint dir, guint ms)
/* Add the timeout callback */
wp_core_timeout_add (core, &self->timeout_source, ms, timeout_save_callback,
self->default_endpoints + dir, NULL);
&self->default_datas[is_ep][dir], NULL);
}
static WpEndpoint *
......@@ -98,37 +100,11 @@ find_endpoint_with_endpoint_id (WpDefaultMetadata * self, guint session_id,
return g_object_ref (ep);
}
static WpEndpoint *
find_endpoint_with_node_id (WpDefaultMetadata * self, guint node_id,
WpSession **session)
{
g_autoptr (WpIterator) it = NULL;
g_auto (GValue) value = G_VALUE_INIT;
it = wp_object_manager_new_iterator (self->sessions_om);
for (; wp_iterator_next (it, &value); g_value_unset (&value)) {
WpSession *s = g_value_get_object (&value);
g_autoptr (WpEndpoint) ep = NULL;
ep = wp_session_lookup_endpoint (s, WP_CONSTRAINT_TYPE_PW_PROPERTY,
PW_KEY_NODE_ID, "=u", node_id, NULL);
if (ep) {
if (session)
*session = g_object_ref (s);
return g_object_ref (ep);
}
}
return NULL;
}
static void
on_default_metadata_changed (WpMetadata *m, guint32 subject,
const gchar *key, const gchar *type, const gchar *value, gpointer *d)
{
WpDefaultMetadata * self = WP_DEFAULT_METADATA (d);
g_autoptr (WpSession) session = NULL;
g_autoptr (WpEndpoint) ep = NULL;
const gchar *session_name = NULL, *ep_name = NULL;
guint dir = WP_DIRECTION_INPUT;
gboolean is_default_ep = FALSE;
......@@ -149,39 +125,52 @@ on_default_metadata_changed (WpMetadata *m, guint32 subject,
return;
}
/* Get the edpoint and session */
ep = is_default_ep ?
find_endpoint_with_endpoint_id (self, subject, atoi (value), &session) :
find_endpoint_with_node_id (self, atoi (value), &session);
if (!ep || !session)
return;
/* Update the default node when default endpoint changes, and vice versa */
g_signal_handlers_block_by_func (m, on_default_metadata_changed, self);
/* Endpoint */
if (is_default_ep) {
const gchar *n_id = NULL, *mc = NULL;
n_id = wp_pipewire_object_get_property (WP_PIPEWIRE_OBJECT (ep),
PW_KEY_NODE_ID);
mc = wp_endpoint_get_media_class (ep);
if (n_id && g_str_has_prefix (mc, "Audio/"))
wp_metadata_set (m, 0, default_audio_node_key (dir), "Spa:Int", n_id);
} else {
g_autofree gchar *v = g_strdup_printf ("%d",
wp_proxy_get_bound_id (WP_PROXY (ep)));
wp_metadata_set (m, wp_proxy_get_bound_id (WP_PROXY (session)),
default_endpoint_key (dir), "Spa:Int", v);
const gchar *session_name = NULL, *ep_name = NULL;
g_autoptr (WpSession) session = NULL;
g_autoptr (WpEndpoint) ep = NULL;
/* Find endpoint and session */
ep = find_endpoint_with_endpoint_id (self, subject,
atoi (value), &session);
if (!ep || !session)
return;
/* Get endpoint name and session name */
session_name = wp_session_get_name (session);
ep_name = wp_endpoint_get_name (ep);
g_return_if_fail (session_name);
g_return_if_fail (ep_name);
/* Set state properties */
wp_properties_set (self->default_datas[1][dir].props,
session_name, ep_name);
}
g_signal_handlers_unblock_by_func (m, on_default_metadata_changed, self);
/* Get the session name and endpoint name */
session_name = wp_session_get_name (session);
g_return_if_fail (session_name);
ep_name = wp_endpoint_get_name (ep);
g_return_if_fail (ep_name);
/* Audio Node */
else {
g_autoptr (WpNode) node = NULL;
const gchar *node_name = NULL;
/* Find node */
node = wp_object_manager_lookup (self->nodes_om, WP_TYPE_NODE,
WP_CONSTRAINT_TYPE_G_PROPERTY, "bound-id", "=u", atoi (value), NULL);
if (!node)
return;
/* Get node name */
node_name = wp_pipewire_object_get_property (WP_PIPEWIRE_OBJECT (node),
PW_KEY_NODE_NAME);
g_return_if_fail (node_name);
/* Set state properties */
wp_properties_set (self->default_datas[0][dir].props,
"audio", node_name);
}
/* Set the property and save state */
wp_properties_set (self->default_endpoints[dir].props, session_name, ep_name);
timeout_save_default_endpoints (self, dir, SAVE_INTERVAL_MS);
/* Save state after specific interval */
timeout_save_default_data (self, is_default_ep, dir, SAVE_INTERVAL_MS);
}
static WpEndpoint *
......@@ -216,15 +205,15 @@ reevaluate_default_endpoints (WpDefaultMetadata * self, WpMetadata *m,
WpSession *session, guint dir)
{
g_autoptr (WpEndpoint) ep = NULL;
const gchar *session_name = NULL, *ep_name = NULL, *n_id = NULL, *mc = NULL;
const gchar *session_name = NULL, *ep_name = NULL;
guint ep_id = 0;
g_return_if_fail (m);
g_return_if_fail (self->default_endpoints[dir].props);
g_return_if_fail (self->default_datas[1][dir].props);
/* Find the default endpoint */
session_name = wp_session_get_name (session);
ep_name = wp_properties_get (self->default_endpoints[dir].props, session_name);
ep_name = wp_properties_get (self->default_datas[1][dir].props, session_name);
if (ep_name) {
ep = wp_session_lookup_endpoint (session,
WP_CONSTRAINT_TYPE_PW_PROPERTY, "endpoint.name", "=s", ep_name,
......@@ -238,9 +227,6 @@ reevaluate_default_endpoints (WpDefaultMetadata * self, WpMetadata *m,
if (ep) {
ep_id = wp_proxy_get_bound_id (WP_PROXY (ep));
n_id = wp_pipewire_object_get_property (WP_PIPEWIRE_OBJECT (ep),
PW_KEY_NODE_ID);
mc = wp_endpoint_get_media_class (ep);
/* block the signal to avoid storing this; only selections done by the user
* should be stored */
......@@ -251,10 +237,6 @@ reevaluate_default_endpoints (WpDefaultMetadata * self, WpMetadata *m,
wp_metadata_set (m, wp_proxy_get_bound_id (WP_PROXY (session)),
default_endpoint_key (dir), "Spa:Int", value);
/* Also set the default node if audio endpoint and node Id is present */
if (n_id && g_str_has_prefix (mc, "Audio/"))
wp_metadata_set (m, 0, default_audio_node_key (dir), "Spa:Int", n_id);
g_signal_handlers_unblock_by_func (m, on_default_metadata_changed, self);
wp_info_object (self, "set default %s endpoint with id %d on session '%s'",
......@@ -262,6 +244,50 @@ reevaluate_default_endpoints (WpDefaultMetadata * self, WpMetadata *m,
}
}
static void
reevaluate_default_audio_nodes (WpDefaultMetadata * self, WpMetadata *m,
guint dir)
{
g_autoptr (WpNode) node = NULL;
const gchar *node_name = NULL;
guint node_id = 0;
g_return_if_fail (m);
g_return_if_fail (self->default_datas[0][dir].props);
/* Find the default node */
node_name = wp_properties_get (self->default_datas[0][dir].props, "audio");
if (node_name) {
node = wp_object_manager_lookup (self->nodes_om, WP_TYPE_NODE,
WP_CONSTRAINT_TYPE_PW_PROPERTY, "node.name", "=s", node_name,
WP_CONSTRAINT_TYPE_PW_PROPERTY, "media.class", "=s",
(dir == WP_DIRECTION_INPUT) ? "Audio/Sink" : "Audio/Source", NULL);
}
/* If not found, get the first one available */
if (!node)
node = wp_object_manager_lookup (self->sessions_om, WP_TYPE_NODE,
WP_CONSTRAINT_TYPE_PW_PROPERTY, "media.class", "=s",
(dir == WP_DIRECTION_INPUT) ? "Audio/Sink" : "Audio/Source", NULL);
if (node) {
node_id = wp_proxy_get_bound_id (WP_PROXY (node));
/* block the signal to avoid storing this; only selections done by the user
* should be stored */
g_signal_handlers_block_by_func (m, on_default_metadata_changed, self);
/* Set default node */
g_autofree gchar *value = g_strdup_printf ("%d", node_id);
wp_metadata_set (m, 0, default_audio_node_key (dir), "Spa:Int", value);
g_signal_handlers_unblock_by_func (m, on_default_metadata_changed, self);
wp_info_object (self, "set default %s audio node with id %d",
direction_to_dbg_string (dir), node_id);
}
}
static void
on_endpoints_changed (WpSession * session, WpDefaultMetadata * self)
{
......@@ -286,6 +312,22 @@ on_session_added (WpObjectManager * om, WpSession * session,
G_CALLBACK (on_endpoints_changed), self, 0);
}
static void
on_nodes_changed (WpObjectManager * om, WpDefaultMetadata * self)
{
g_autoptr (WpMetadata) metadata = NULL;
/* Get the metadata */
metadata = wp_object_manager_lookup (self->metadatas_om, WP_TYPE_METADATA,
WP_CONSTRAINT_TYPE_G_PROPERTY, "bound-id", "=u", self->metadata_id, NULL);
if (!metadata)
return;
wp_trace_object (om, "nodes changed, re-evaluating defaults");
reevaluate_default_audio_nodes (self, metadata, WP_DIRECTION_INPUT);
reevaluate_default_audio_nodes (self, metadata, WP_DIRECTION_OUTPUT);
}
static void
on_metadata_added (WpObjectManager *om, WpMetadata *metadata, gpointer d)
{
......@@ -303,6 +345,15 @@ on_metadata_added (WpObjectManager *om, WpMetadata *metadata, gpointer d)
g_signal_connect_object (metadata, "changed",
G_CALLBACK (on_default_metadata_changed), self, 0);
/* Create the nodes object manager */
self->nodes_om = wp_object_manager_new ();
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_OBJECT_FEATURES_ALL);
g_signal_connect_object (self->nodes_om, "objects-changed",
G_CALLBACK (on_nodes_changed), self, 0);
wp_core_install_object_manager (core, self->nodes_om);
/* Create the sessions object manager */
self->sessions_om = wp_object_manager_new ();
wp_object_manager_add_interest (self->sessions_om, WP_TYPE_SESSION, NULL);
......@@ -340,11 +391,12 @@ wp_default_metadata_disable (WpPlugin * plugin)
WpDefaultMetadata * self = WP_DEFAULT_METADATA (plugin);
g_clear_object (&self->metadatas_om);
g_clear_object (&self->nodes_om);
g_clear_object (&self->sessions_om);
}
static void
unload_default_endpoints (WpDefaultMetadata * self, WpDefaultEndpoints * d)
unload_default_data (WpDefaultMetadata * self, WpDefaultData * d)
{
g_clear_pointer (&d->props, wp_properties_unref);
g_clear_pointer (&d->group, g_free);
......@@ -352,7 +404,7 @@ unload_default_endpoints (WpDefaultMetadata * self, WpDefaultEndpoints * d)
}
static void
load_default_endpoints (WpDefaultMetadata * self, WpDefaultEndpoints * d,
load_default_data (WpDefaultMetadata * self, WpDefaultData * d,
const gchar *group)
{
d->self = self;
......@@ -373,8 +425,10 @@ wp_default_metadata_finalize (GObject * object)
g_source_destroy (self->timeout_source);
g_clear_pointer (&self->timeout_source, g_source_unref);
unload_default_endpoints (self, self->default_endpoints + WP_DIRECTION_INPUT);
unload_default_endpoints (self, self->default_endpoints + WP_DIRECTION_OUTPUT);
unload_default_data (self, &self->default_datas[0][WP_DIRECTION_INPUT]);
unload_default_data (self, &self->default_datas[0][WP_DIRECTION_OUTPUT]);
unload_default_data (self, &self->default_datas[1][WP_DIRECTION_INPUT]);
unload_default_data (self, &self->default_datas[1][WP_DIRECTION_OUTPUT]);
g_clear_object (&self->state);
G_OBJECT_CLASS (wp_default_metadata_parent_class)->finalize (object);
......@@ -384,9 +438,13 @@ static void
wp_default_metadata_init (WpDefaultMetadata * self)
{
self->state = wp_state_new (STATE_NAME);
load_default_endpoints (self, self->default_endpoints + WP_DIRECTION_INPUT,
load_default_data (self, &self->default_datas[0][WP_DIRECTION_INPUT],
default_audio_node_key (WP_DIRECTION_INPUT));
load_default_data (self, &self->default_datas[0][WP_DIRECTION_OUTPUT],
default_audio_node_key (WP_DIRECTION_OUTPUT));
load_default_data (self, &self->default_datas[1][WP_DIRECTION_INPUT],
default_endpoint_key (WP_DIRECTION_INPUT));
load_default_endpoints (self, self->default_endpoints + WP_DIRECTION_OUTPUT,
load_default_data (self, &self->default_datas[1][WP_DIRECTION_OUTPUT],
default_endpoint_key (WP_DIRECTION_OUTPUT));
}
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment