modules: refactor config-endpoint to use the new session item API

This commit is contained in:
Julian Bouzas 2020-05-04 13:38:52 -04:00 committed by George Kiagiadakis
parent 20708b28c7
commit 288f1f091b
14 changed files with 405 additions and 491 deletions

View file

@ -14,6 +14,8 @@
#include "parser-streams.h"
#include "context.h"
G_DEFINE_QUARK (wp-module-config-endpoint-context-session, session);
struct _WpConfigEndpointContext
{
GObject parent;
@ -21,8 +23,9 @@ struct _WpConfigEndpointContext
/* Props */
GWeakRef core;
WpObjectManager *om;
GHashTable *registered_endpoints;
WpObjectManager *sessions_om;
WpObjectManager *nodes_om;
GHashTable *endpoints;
};
enum {
@ -40,63 +43,52 @@ static guint signals[N_SIGNALS];
G_DEFINE_TYPE (WpConfigEndpointContext, wp_config_endpoint_context,
G_TYPE_OBJECT)
static void
on_endpoint_created (GObject *initable, GAsyncResult *res, gpointer d)
{
WpConfigEndpointContext *self = d;
g_autoptr (WpBaseEndpoint) endpoint = NULL;
g_autoptr (WpProxy) proxy = NULL;
guint global_id = 0;
GError *error = NULL;
/* Get the endpoint */
endpoint = wp_base_endpoint_new_finish (initable, res, &error);
if (error) {
g_warning ("Failed to create endpoint: %s", error->message);
return;
}
/* Get the endpoint global id */
g_object_get (endpoint, "node", &proxy, NULL);
global_id = wp_proxy_get_bound_id (proxy);
/* Register the endpoint and add it to the table */
wp_base_endpoint_register (endpoint);
g_hash_table_insert (self->registered_endpoints, GUINT_TO_POINTER (global_id),
g_object_ref (endpoint));
/* Emit the endpoint-created signal */
g_signal_emit (self, signals[SIGNAL_ENDPOINT_CREATED], 0, endpoint);
}
static GVariant *
create_streams_variant (WpConfiguration *config, const char *streams)
static const struct WpParserStreamsData *
get_streams_data (WpConfiguration *config, const char *file_name)
{
g_autoptr (WpConfigParser) parser = NULL;
const struct WpParserStreamsData *streams_data = NULL;
g_autoptr (GVariantBuilder) ba = NULL;
if (!streams || !config)
return NULL;
g_return_val_if_fail (config, 0);
g_return_val_if_fail (file_name, 0);
/* Get the streams parser */
parser = wp_configuration_get_parser (config, WP_PARSER_STREAMS_EXTENSION);
if (!parser)
return NULL;
return 0;
/* Get the streams data */
streams_data = wp_config_parser_get_matched_data (parser, (gpointer)streams);
if (!streams_data || streams_data->n_streams <= 0)
return NULL;
return wp_config_parser_get_matched_data (parser, (gpointer)file_name);
}
/* Build the variant array with the stream name and priority */
ba = g_variant_builder_new (G_VARIANT_TYPE ("a(su)"));
g_variant_builder_init (ba, G_VARIANT_TYPE_ARRAY);
for (guint i = 0; i < streams_data->n_streams; i++)
g_variant_builder_add (ba, "(su)", streams_data->streams[i].name,
streams_data->streams[i].priority);
static void
endpoint_export_finish_cb (WpSessionItem * ep, GAsyncResult * res,
WpConfigEndpointContext * self)
{
g_autoptr (GError) error = NULL;
gboolean export_ret = wp_session_item_export_finish (ep, res, &error);
g_return_if_fail (error == NULL);
g_return_if_fail (export_ret);
return g_variant_new ("a(su)", ba);
/* Emit the signal */
g_signal_emit (self, signals[SIGNAL_ENDPOINT_CREATED], 0, ep);
}
static void
endpoint_activate_finish_cb (WpSessionItem * ep, GAsyncResult * res,
WpConfigEndpointContext * self)
{
WpSession * session = NULL;
g_autoptr (GError) error = NULL;
gboolean activate_ret = wp_session_item_activate_finish (ep, res, &error);
g_return_if_fail (error == NULL);
g_return_if_fail (activate_ret);
/* Get the session */
session = g_object_get_qdata (G_OBJECT (ep), session_quark ());
g_return_if_fail (session);
wp_session_item_export (ep, WP_SESSION (session),
(GAsyncReadyCallback) endpoint_export_finish_cb, self);
}
static void
@ -106,74 +98,141 @@ on_node_added (WpObjectManager *om, WpProxy *proxy, gpointer d)
g_autoptr (WpCore) core = g_weak_ref_get (&self->core);
g_autoptr (WpConfiguration) config = wp_configuration_get_instance (core);
g_autoptr (WpProperties) props = wp_proxy_get_properties (proxy);
g_autoptr (WpSessionItem) ep = NULL;
g_autoptr (WpSessionItem) streams_ep = NULL;
g_autoptr (WpSession) session = NULL;
g_autoptr (WpConfigParser) parser = NULL;
const struct WpParserEndpointData *endpoint_data = NULL;
GVariantBuilder b;
g_autoptr (GVariant) endpoint_props = NULL;
const char *media_class = NULL, *name = NULL;
g_autoptr (GVariant) streams_variant = NULL;
const struct WpParserStreamsData *streams_data = NULL;
/* Skip nodes with no media class (JACK Clients) */
media_class = wp_properties_get (props, PW_KEY_MEDIA_CLASS);
if (!media_class)
if (!wp_properties_get (props, PW_KEY_MEDIA_CLASS))
return;
/* Get the linked and ep streams data */
/* Get the endpoint configuration data */
parser = wp_configuration_get_parser (config, WP_PARSER_ENDPOINT_EXTENSION);
endpoint_data = wp_config_parser_get_matched_data (parser, proxy);
if (!endpoint_data)
return;
/* Set the name if it is null */
name = endpoint_data->e.name;
if (!name)
name = wp_properties_get (props, PW_KEY_NODE_NAME);
/* Get the session */
session = wp_object_manager_lookup (self->sessions_om, WP_TYPE_SESSION,
WP_CONSTRAINT_TYPE_PW_PROPERTY, "session.name", "=s",
endpoint_data->e.session, NULL);
if (!session) {
wp_warning_object (self, "could not find session for endpoint");
return;
}
/* Set the media class if it is null */
if (endpoint_data->e.media_class)
media_class = endpoint_data->e.media_class;
/* Get the streams data */
streams_data = endpoint_data->e.streams ?
get_streams_data (config, endpoint_data->e.streams) : NULL;
/* Create the streams variant */
streams_variant = create_streams_variant (config, endpoint_data->e.streams);
/* Create the endpoint */
ep = wp_session_item_make (core, endpoint_data->e.type);
if (!ep) {
wp_warning_object (self, "could not create endpoint of type %s",
endpoint_data->e.type);
return;
}
/* Set the properties */
g_variant_builder_init (&b, G_VARIANT_TYPE_VARDICT);
g_variant_builder_add (&b, "{sv}",
"name", g_variant_new_take_string (g_strdup_printf ("%s", name)));
g_variant_builder_add (&b, "{sv}",
"media-class", g_variant_new_string (media_class));
g_variant_builder_add (&b, "{sv}",
"direction", g_variant_new_uint32 (endpoint_data->e.direction));
g_variant_builder_add (&b, "{sv}",
"priority", g_variant_new_uint32 (endpoint_data->e.priority));
g_variant_builder_add (&b, "{sv}",
"node", g_variant_new_uint64 ((guint64) proxy));
if (streams_variant)
g_variant_builder_add (&b, "{sv}", "streams",
g_steal_pointer (&streams_variant));
endpoint_props = g_variant_builder_end (&b);
/* Configure the endpoint */
{
g_auto (GVariantBuilder) b =
G_VARIANT_BUILDER_INIT (G_VARIANT_TYPE_VARDICT);
g_variant_builder_add (&b, "{sv}", "node",
g_variant_new_uint64 ((guint64) proxy));
/* Create the endpoint async */
wp_factory_make (core, endpoint_data->e.type, WP_TYPE_BASE_ENDPOINT,
endpoint_props, on_endpoint_created, self);
if (endpoint_data->e.c.name)
g_variant_builder_add (&b, "{sv}", "name",
g_variant_new_string (endpoint_data->e.c.name));
if (endpoint_data->e.c.media_class)
g_variant_builder_add (&b, "{sv}", "media-class",
g_variant_new_string (endpoint_data->e.c.media_class));
if (endpoint_data->e.c.role)
g_variant_builder_add (&b, "{sv}", "role",
g_variant_new_string (endpoint_data->e.c.role));
g_variant_builder_add (&b, "{sv}", "priority",
g_variant_new_uint32 (endpoint_data->e.c.priority));
g_variant_builder_add (&b, "{sv}", "enable-control-port",
g_variant_new_boolean (endpoint_data->e.c.enable_control_port));
g_variant_builder_add (&b, "{sv}", "enable-monitor",
g_variant_new_boolean (endpoint_data->e.c.enable_monitor));
wp_session_item_configure (ep, g_variant_builder_end (&b));
}
/* TODO: for now we always create softdsp audio endpoints if streams data is
* valid. However, this will need to change once we have video endpoints. */
if (streams_data) {
/* Create the steams endpoint */
streams_ep = wp_session_item_make (core, "si-audio-softdsp-endpoint");
/* Configure the streams endpoint */
{
g_auto (GVariantBuilder) b =
G_VARIANT_BUILDER_INIT (G_VARIANT_TYPE_VARDICT);
g_variant_builder_add (&b, "{sv}", "adapter",
g_variant_new_uint64 ((guint64) ep));
wp_session_item_configure (streams_ep, g_variant_builder_end (&b));
}
/* Add the streams */
for (guint i = 0; i < streams_data->n_streams; i++) {
const struct WpParserStreamsStreamData *sd = streams_data->streams + i;
g_autoptr (WpSessionItem) stream =
wp_session_item_make (core, "si-convert");
{
g_auto (GVariantBuilder) b =
G_VARIANT_BUILDER_INIT (G_VARIANT_TYPE_VARDICT);
g_variant_builder_add (&b, "{sv}", "target",
g_variant_new_uint64 ((guint64) ep));
g_variant_builder_add (&b, "{sv}", "name",
g_variant_new_string (sd->name));
g_variant_builder_add (&b, "{sv}", "enable-control-port",
g_variant_new_boolean (sd->enable_control_port));
wp_session_item_configure (stream, g_variant_builder_end (&b));
}
wp_session_bin_add (WP_SESSION_BIN (streams_ep), g_steal_pointer (&stream));
}
}
/* Activate endpoint */
g_object_set_qdata_full (
G_OBJECT (streams_data ? streams_ep : ep), session_quark (),
g_steal_pointer (&session), g_object_unref);
wp_session_item_activate (streams_data ? streams_ep : ep,
(GAsyncReadyCallback) endpoint_activate_finish_cb, self);
/* Insert the endpoint */
g_hash_table_insert (self->endpoints, proxy,
streams_data ? g_steal_pointer (&streams_ep) : g_steal_pointer (&ep));
}
static void
on_node_removed (WpObjectManager *om, WpProxy *proxy, gpointer d)
on_sessions_changed (WpObjectManager *om, gpointer d)
{
WpConfigEndpointContext *self = d;
WpBaseEndpoint *endpoint = NULL;
guint32 id = wp_proxy_get_bound_id (proxy);
g_autoptr (WpCore) core = g_weak_ref_get (&self->core);
g_return_if_fail (core);
/* Get the endpoint */
endpoint = g_hash_table_lookup (self->registered_endpoints,
GUINT_TO_POINTER(id));
if (!endpoint)
return;
/* Handle node-added signal and install the nodes object manager */
wp_object_manager_add_interest_1 (self->nodes_om, WP_TYPE_NODE, NULL);
wp_object_manager_request_proxy_features (self->nodes_om, WP_TYPE_NODE,
WP_PROXY_FEATURES_STANDARD);
g_signal_connect_object (self->nodes_om, "object-added",
G_CALLBACK (on_node_added), self, 0);
wp_core_install_object_manager (core, self->nodes_om);
/* Unregister the endpoint and remove it from the table */
wp_base_endpoint_unregister (endpoint);
g_hash_table_remove (self->registered_endpoints, GUINT_TO_POINTER(id));
/* Remove handler */
g_signal_handlers_disconnect_by_func (self->sessions_om,
on_sessions_changed, d);
}
static void
@ -195,8 +254,13 @@ wp_config_endpoint_context_constructed (GObject * object)
wp_configuration_reload (config, WP_PARSER_ENDPOINT_EXTENSION);
wp_configuration_reload (config, WP_PARSER_STREAMS_EXTENSION);
/* Install the object manager */
wp_core_install_object_manager (core, self->om);
/* Handle sessions-changed signal and install the session object manager */
wp_object_manager_add_interest_1 (self->sessions_om, WP_TYPE_SESSION, NULL);
wp_object_manager_request_proxy_features (self->sessions_om, WP_TYPE_SESSION,
WP_PROXY_FEATURES_STANDARD);
g_signal_connect_object (self->sessions_om, "objects-changed",
G_CALLBACK (on_sessions_changed), self, 0);
wp_core_install_object_manager (core, self->sessions_om);
G_OBJECT_CLASS (wp_config_endpoint_context_parent_class)->constructed (object);
}
@ -246,8 +310,9 @@ wp_config_endpoint_context_finalize (GObject *object)
}
g_weak_ref_clear (&self->core);
g_clear_object (&self->om);
g_clear_pointer (&self->registered_endpoints, g_hash_table_unref);
g_clear_pointer (&self->endpoints, g_hash_table_unref);
g_clear_object (&self->sessions_om);
g_clear_object (&self->nodes_om);
G_OBJECT_CLASS (wp_config_endpoint_context_parent_class)->finalize (object);
}
@ -255,20 +320,10 @@ wp_config_endpoint_context_finalize (GObject *object)
static void
wp_config_endpoint_context_init (WpConfigEndpointContext *self)
{
self->om = wp_object_manager_new ();
self->registered_endpoints = g_hash_table_new_full (g_direct_hash,
g_direct_equal, NULL, (GDestroyNotify) g_object_unref);
/* Only handle augmented nodes with info set */
wp_object_manager_add_interest_1 (self->om, WP_TYPE_NODE, NULL);
wp_object_manager_request_proxy_features (self->om, WP_TYPE_NODE,
WP_PROXY_FEATURES_STANDARD);
/* Register the global added/removed callbacks */
g_signal_connect(self->om, "object-added",
(GCallback) on_node_added, self);
g_signal_connect(self->om, "object-removed",
(GCallback) on_node_removed, self);
self->nodes_om = wp_object_manager_new ();
self->sessions_om = wp_object_manager_new ();
self->endpoints = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL,
(GDestroyNotify) g_object_unref);
}
static void
@ -290,7 +345,7 @@ wp_config_endpoint_context_class_init (WpConfigEndpointContextClass *klass)
/* Signals */
signals[SIGNAL_ENDPOINT_CREATED] = g_signal_new ("endpoint-created",
G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL,
G_TYPE_NONE, 1, WP_TYPE_ENDPOINT);
G_TYPE_NONE, 1, WP_TYPE_SESSION_ITEM);
}
WpConfigEndpointContext *
@ -300,10 +355,3 @@ wp_config_endpoint_context_new (WpCore *core)
"core", core,
NULL);
}
guint
wp_config_endpoint_context_get_length (WpConfigEndpointContext *self)
{
g_return_val_if_fail (WP_IS_CONFIG_ENDPOINT_CONTEXT (self), 0);
return g_hash_table_size (self->registered_endpoints);
}

View file

@ -19,8 +19,6 @@ G_DECLARE_FINAL_TYPE (WpConfigEndpointContext, wp_config_endpoint_context,
WpConfigEndpointContext * wp_config_endpoint_context_new (WpCore *core);
guint wp_config_endpoint_context_get_length (WpConfigEndpointContext *self);
G_END_DECLS
#endif

View file

@ -32,14 +32,14 @@ wp_parser_endpoint_data_destroy (gpointer p)
{
struct WpParserEndpointData *data = p;
/* Free the strings */
g_clear_pointer (&data->filename, g_free);
g_clear_pointer (&data->mn.props, wp_properties_unref);
g_clear_pointer (&data->e.name, g_free);
g_clear_pointer (&data->e.media_class, g_free);
g_clear_pointer (&data->e.props, wp_properties_unref);
g_clear_pointer (&data->e.session, g_free);
g_clear_pointer (&data->e.type, g_free);
g_clear_pointer (&data->e.streams, g_free);
g_clear_pointer (&data->e.c.name, g_free);
g_clear_pointer (&data->e.c.media_class, g_free);
g_clear_pointer (&data->e.c.role, g_free);
g_slice_free (struct WpParserEndpointData, data);
}
@ -76,25 +76,12 @@ parse_properties (WpTomlTable *table, const char *name)
return props;
}
static guint
parse_endpoint_direction (const char *direction)
{
if (g_strcmp0 (direction, "sink") == 0)
return PW_DIRECTION_INPUT;
else if (g_strcmp0 (direction, "source") == 0)
return PW_DIRECTION_OUTPUT;
g_return_val_if_reached (PW_DIRECTION_INPUT);
}
static struct WpParserEndpointData *
wp_parser_endpoint_data_new (const gchar *location)
{
g_autoptr (WpTomlFile) file = NULL;
g_autoptr (WpTomlTable) table = NULL, mn = NULL, e = NULL;
g_autoptr (WpTomlArray) streams = NULL;
g_autoptr (WpTomlTable) table = NULL, mn = NULL, e = NULL, c = NULL;
struct WpParserEndpointData *res = NULL;
g_autofree char *direction = NULL;
/* File format:
* ------------
@ -102,13 +89,17 @@ wp_parser_endpoint_data_new (const gchar *location)
* properties (WpProperties)
*
* [endpoint]
* name (string)
* media_class (string)
* direction (string)
* priority (uint32)
* properties (WpProperties)
* session (string)
* type (string)
* streams (string)
*
* [endpoint.config]
* name (string)
* media_class (string)
* role (string)
* priority (uint32)
* enable_control_port (bool)
* enable_monitor (bool)
*/
/* Get the TOML file */
@ -140,33 +131,33 @@ wp_parser_endpoint_data_new (const gchar *location)
if (!e)
goto error;
/* Get the name from the endpoint table */
res->e.name = wp_toml_table_get_string (e, "name");
/* Get the media class from the endpoint table */
res->e.media_class = wp_toml_table_get_string (e, "media_class");
/* Get the direction from the endpoint table */
direction = wp_toml_table_get_string (e, "direction");
if (!direction)
/* Get the endpoint session */
res->e.session = wp_toml_table_get_string (e, "session");
if (!res->e.session)
goto error;
res->e.direction = parse_endpoint_direction (direction);
/* Get the priority from the endpoint table */
res->e.priority = 0;
wp_toml_table_get_uint32 (e, "priority", &res->e.priority);
/* Get the endpoint properties */
res->e.props = parse_properties (e, "properties");
/* Get the endpoint type */
res->e.type = wp_toml_table_get_string (e, "type");
if (!res->e.type)
goto error;
/* Get the endpoint streams */
/* Get the optional streams */
res->e.streams = wp_toml_table_get_string (e, "streams");
/* Get the optional endpoint config table */
c = wp_toml_table_get_table (e, "config");
if (c) {
res->e.c.name = wp_toml_table_get_string (c, "name");
res->e.c.media_class = wp_toml_table_get_string (c, "media_class");
res->e.c.role = wp_toml_table_get_string (c, "role");
res->e.c.priority = 0;
wp_toml_table_get_uint32 (c, "priority", &res->e.c.priority);
res->e.c.enable_control_port = FALSE;
wp_toml_table_get_boolean (c, "enable-control-port", &res->e.c.enable_control_port);
res->e.c.enable_monitor = FALSE;
wp_toml_table_get_boolean (c, "enable-monitor", &res->e.c.enable_monitor);
}
return res;
error:
@ -193,7 +184,7 @@ wp_parser_endpoint_add_file (WpConfigParser *parser,
/* Parse the file */
data = wp_parser_endpoint_data_new (name);
if (!data) {
g_warning ("Failed to parse configuration file '%s'", name);
wp_warning_object (parser, "Failed to parse configuration file '%s'", name);
return FALSE;
}

View file

@ -21,13 +21,18 @@ struct WpParserEndpointData {
WpProperties *props;
} mn;
struct Endpoint {
char *name;
char *media_class;
guint direction;
guint priority;
WpProperties *props;
char *session;
char *type;
char *streams;
struct Config {
char *name;
char *media_class;
char *role;
guint priority;
gboolean enable_control_port;
gboolean enable_monitor;
guint direction;
} c;
} e;
};

View file

@ -97,6 +97,11 @@ streams_for_each (const WpTomlTable *table, gpointer user_data)
stream->priority = 0;
wp_toml_table_get_uint32 (table, "priority", &stream->priority);
/* Parse the optional enable_control_port */
stream->enable_control_port = FALSE;
wp_toml_table_get_boolean (table, "enable_control_port",
&stream->enable_control_port);
/* Increment the number of streams */
data->n_streams++;
}
@ -115,6 +120,7 @@ wp_parser_streams_data_new (const gchar *location)
* [[streams]]
* name (string)
* priority (uint32)
* enable_control_port (bool)
*/
/* Get the TOML file */
@ -152,7 +158,7 @@ wp_parser_streams_add_file (WpConfigParser *parser,
/* Parse the file */
data = wp_parser_streams_data_new (name);
if (!data) {
g_warning ("Failed to parse configuration file '%s'", name);
wp_warning_object (parser, "Failed to parse configuration file '%s'", name);
return FALSE;
}

View file

@ -21,6 +21,7 @@ G_BEGIN_DECLS
struct WpParserStreamsStreamData {
char *name;
guint priority;
gboolean enable_control_port;
};
struct WpParserStreamsData {

View file

@ -7,7 +7,6 @@
*/
#include "../common/base-test-fixture.h"
#include "config-endpoint/endpoint-audiotestsrc.h"
#include "../../modules/module-config-endpoint/context.h"
typedef struct {
@ -15,64 +14,183 @@ typedef struct {
} TestConfigEndpointFixture;
static void
config_endpoint_setup (TestConfigEndpointFixture *self, gconstpointer data)
config_endpoint_setup (TestConfigEndpointFixture *f, gconstpointer data)
{
wp_base_test_fixture_setup (&self->base, 0);
wp_base_test_fixture_setup (&f->base, 0);
/* load audiotestsrc */
pw_thread_loop_lock (self->base.server.thread_loop);
pw_context_add_spa_lib (self->base.server.context, "audiotestsrc",
"audiotestsrc/libspa-audiotestsrc");
if (!pw_context_load_module (self->base.server.context,
"libpipewire-module-spa-node", "audiotestsrc", NULL)) {
pw_thread_loop_unlock (self->base.server.thread_loop);
g_test_skip ("audiotestsrc SPA plugin is not installed");
return;
/* load modules */
{
g_autoptr (WpTestServerLocker) lock =
wp_test_server_locker_new (&f->base.server);
g_assert_cmpint (pw_context_add_spa_lib (f->base.server.context,
"audiotestsrc", "audiotestsrc/libspa-audiotestsrc"), ==, 0);
g_assert_nonnull (pw_context_load_module (f->base.server.context,
"libpipewire-module-spa-node-factory", NULL, NULL));
g_assert_nonnull (pw_context_load_module (f->base.server.context,
"libpipewire-module-adapter", NULL, NULL));
}
{
g_autoptr (GError) error = NULL;
WpModule *module = wp_module_load (f->base.core, "C",
"libwireplumber-module-si-simple-node-endpoint", NULL, &error);
g_assert_no_error (error);
g_assert_nonnull (module);
}
{
g_autoptr (GError) error = NULL;
WpModule *module = wp_module_load (f->base.core, "C",
"libwireplumber-module-si-adapter", NULL, &error);
g_assert_no_error (error);
g_assert_nonnull (module);
}
{
g_autoptr (GError) error = NULL;
WpModule *module = wp_module_load (f->base.core, "C",
"libwireplumber-module-si-convert", NULL, &error);
g_assert_no_error (error);
g_assert_nonnull (module);
}
{
g_autoptr (GError) error = NULL;
WpModule *module = wp_module_load (f->base.core, "C",
"libwireplumber-module-si-audio-softdsp-endpoint", NULL, &error);
g_assert_no_error (error);
g_assert_nonnull (module);
}
pw_thread_loop_unlock (self->base.server.thread_loop);
/* Register the wp-endpoint-audiotestsrc */
wp_factory_new (self->base.core, "wp-endpoint-audiotestsrc",
wp_endpoint_audiotestsrc_factory);
}
static void
config_endpoint_teardown (TestConfigEndpointFixture *self, gconstpointer data)
config_endpoint_teardown (TestConfigEndpointFixture *f, gconstpointer data)
{
wp_base_test_fixture_teardown (&self->base);
wp_base_test_fixture_teardown (&f->base);
}
static void
on_audiotestsrc_created (WpConfigEndpointContext *ctx, WpEndpoint *ep,
on_default_session_exported (WpProxy * session, GAsyncResult * res,
TestConfigEndpointFixture *f)
{
g_assert_nonnull (ep);
g_autoptr (GError) error = NULL;
g_assert_true (wp_proxy_augment_finish (session, res, &error));
g_assert_no_error (error);
g_assert_true (WP_IS_IMPL_SESSION (session));
g_main_loop_quit (f->base.loop);
}
static void
basic (TestConfigEndpointFixture *f, gconstpointer data)
on_audiotestsrc_simple_endpoint_created (WpConfigEndpointContext *ctx,
WpSessionItem *ep, TestConfigEndpointFixture *f)
{
g_autoptr (WpNode) node = NULL;
g_autoptr (WpProperties) props = NULL;
g_assert_nonnull (ep);
g_autoptr (GVariant) v = wp_session_item_get_configuration (ep);
const gchar *str;
guint32 prio;
g_assert_true (g_variant_lookup (v, "name", "&s", &str));
g_assert_cmpstr (str, ==, "audiotestsrc-endpoint");
g_assert_true (g_variant_lookup (v, "media-class", "&s", &str));
g_assert_cmpstr (str, ==, "Audio/Source");
g_assert_true (g_variant_lookup (v, "role", "&s", &str));
g_assert_cmpstr (str, ==, "Multimedia");
g_assert_true (g_variant_lookup (v, "priority", "u", &prio));
g_assert_cmpuint (prio, ==, 0);
g_main_loop_quit (f->base.loop);
}
static void
on_audiotestsrc_streams_endpoint_created (WpConfigEndpointContext *ctx,
WpSessionItem *ep, TestConfigEndpointFixture *f)
{
g_assert_nonnull (ep);
g_assert_cmpuint (5, ==, wp_session_bin_get_n_children (WP_SESSION_BIN (ep)));
g_autoptr (GVariant) v = wp_session_item_get_configuration (ep);
guint64 p_i;
g_assert_true (g_variant_lookup (v, "adapter", "t", &p_i));
g_assert_nonnull ((gpointer)p_i);
g_autoptr (GVariant) v2 = wp_session_item_get_configuration ((gpointer)p_i);
const gchar *str;
guint32 prio;
g_assert_true (g_variant_lookup (v2, "name", "&s", &str));
g_assert_cmpstr (str, ==, "audiotestsrc-endpoint");
g_assert_true (g_variant_lookup (v2, "media-class", "&s", &str));
g_assert_cmpstr (str, ==, "Audio/Source");
g_assert_true (g_variant_lookup (v2, "role", "&s", &str));
g_assert_cmpstr (str, ==, "Multimedia");
g_assert_true (g_variant_lookup (v2, "priority", "u", &prio));
g_assert_cmpuint (prio, ==, 0);
g_main_loop_quit (f->base.loop);
}
static void
simple (TestConfigEndpointFixture *f, gconstpointer data)
{
/* Set the configuration path */
g_autoptr (WpConfiguration) config = wp_configuration_get_instance (f->base.core);
g_assert_nonnull (config);
wp_configuration_add_path (config, "config-endpoint/basic");
wp_configuration_add_path (config, "config-endpoint/simple");
/* Create the context and handle the endpoint-created callback */
/* Create the endpoint context and handle the endpoint-created callback */
g_autoptr (WpConfigEndpointContext) ctx =
wp_config_endpoint_context_new (f->base.core);
g_assert_nonnull (ctx);
g_assert_cmpint (wp_config_endpoint_context_get_length (ctx), ==, 0);
/* Add a handler to stop the main loop when the endpoint is created */
g_signal_connect (ctx, "endpoint-created",
(GCallback) on_audiotestsrc_created, f);
(GCallback) on_audiotestsrc_simple_endpoint_created, f);
/* Run the main loop */
/* Create and export the default session */
g_autoptr (WpImplSession) session = wp_impl_session_new (f->base.core);
wp_impl_session_set_property (session, "session.name", "default");
wp_proxy_augment (WP_PROXY (session), WP_PROXY_FEATURE_BOUND, NULL,
(GAsyncReadyCallback) on_default_session_exported, f);
g_main_loop_run (f->base.loop);
/* Check if the endpoint was created */
g_assert_cmpint (wp_config_endpoint_context_get_length (ctx), ==, 1);
/* Create the audiotestsrc node and run until the endpoint is created */
g_autoptr (WpNode) node = wp_node_new_from_factory (f->base.core,
"spa-node-factory",
wp_properties_new (
"factory.name", "audiotestsrc",
"node.name", "audiotestsrc0",
NULL));
g_assert_nonnull (node);
g_main_loop_run (f->base.loop);
}
static void
streams (TestConfigEndpointFixture *f, gconstpointer data)
{
/* Set the configuration path */
g_autoptr (WpConfiguration) config = wp_configuration_get_instance (f->base.core);
g_assert_nonnull (config);
wp_configuration_add_path (config, "config-endpoint/streams");
/* Create the endpoint context and handle the endpoint-created callback */
g_autoptr (WpConfigEndpointContext) ctx =
wp_config_endpoint_context_new (f->base.core);
g_assert_nonnull (ctx);
g_signal_connect (ctx, "endpoint-created",
(GCallback) on_audiotestsrc_streams_endpoint_created, f);
/* Create and export the default session */
g_autoptr (WpImplSession) session = wp_impl_session_new (f->base.core);
wp_impl_session_set_property (session, "session.name", "default");
wp_proxy_augment (WP_PROXY (session), WP_PROXY_FEATURE_BOUND, NULL,
(GAsyncReadyCallback) on_default_session_exported, f);
g_main_loop_run (f->base.loop);
/* create audiotestsrc adapter node and run until the endpoint is created */
g_autoptr (WpNode) node = wp_node_new_from_factory (f->base.core,
"adapter",
wp_properties_new (
"factory.name", "audiotestsrc",
"node.name", "adapter-audiotestsrc0",
NULL));
g_assert_nonnull (node);
g_main_loop_run (f->base.loop);
}
int
@ -82,8 +200,10 @@ main (int argc, char *argv[])
pw_init (NULL, NULL);
g_log_set_writer_func (wp_log_writer_default, NULL, NULL);
g_test_add ("/modules/config-endpoint/basic", TestConfigEndpointFixture,
NULL, config_endpoint_setup, basic, config_endpoint_teardown);
g_test_add ("/modules/config-endpoint/simple", TestConfigEndpointFixture,
NULL, config_endpoint_setup, simple, config_endpoint_teardown);
g_test_add ("/modules/config-endpoint/streams", TestConfigEndpointFixture,
NULL, config_endpoint_setup, streams, config_endpoint_teardown);
return g_test_run ();
}

View file

@ -1,9 +0,0 @@
[match-node]
properties = [
{ name = "media.class", value = "Audio/Source" },
]
[endpoint]
direction = "source"
type = "wp-endpoint-audiotestsrc"
streams = "default.streams"

View file

@ -1,254 +0,0 @@
/* WirePlumber
*
* Copyright © 2019 Collabora Ltd.
* @author Julian Bouzas <julian.bouzas@collabora.com>
*
* SPDX-License-Identifier: MIT
*/
#include <wp/wp.h>
#include "endpoint-audiotestsrc.h"
struct _WpEndpointAudiotestsrc
{
WpBaseEndpoint parent;
GTask *init_task;
guint id;
/* Props */
WpNode *node;
GVariant *streams;
};
enum {
PROP_0,
PROP_PROXY_NODE,
PROP_STREAMS,
};
static GAsyncInitableIface *wp_endpoint_audiotestsrc_parent_interface = NULL;
static void wp_endpoint_audiotestsrc_async_initable_init (gpointer iface,
gpointer iface_data);
G_DEFINE_TYPE_WITH_CODE (WpEndpointAudiotestsrc, wp_endpoint_audiotestsrc,
WP_TYPE_BASE_ENDPOINT,
G_IMPLEMENT_INTERFACE (G_TYPE_ASYNC_INITABLE,
wp_endpoint_audiotestsrc_async_initable_init))
static WpProperties *
wp_endpoint_audiotestsrc_get_properties (WpBaseEndpoint * ep)
{
WpEndpointAudiotestsrc *self = WP_ENDPOINT_AUDIOTESTSRC (ep);
return wp_proxy_get_properties (WP_PROXY (self->node));
}
static const char *
wp_endpoint_audiotestsrc_get_role (WpBaseEndpoint * ep)
{
return NULL;
}
static guint32
wp_endpoint_audiotestsrc_get_global_id (WpBaseEndpoint * ep)
{
WpEndpointAudiotestsrc *self = WP_ENDPOINT_AUDIOTESTSRC (ep);
return self->id;
}
static gboolean
wp_endpoint_audiotestsrc_prepare_link (WpBaseEndpoint * ep, guint32 stream_id,
WpBaseEndpointLink * link, GVariant ** properties, GError ** error)
{
return TRUE;
}
static const char *
wp_endpoint_audiotestsrc_get_endpoint_link_factory (WpBaseEndpoint * ep)
{
return NULL;
}
static void
wp_endpoint_audiotestsrc_constructed (GObject * object)
{
WpEndpointAudiotestsrc *self = WP_ENDPOINT_AUDIOTESTSRC (object);
GVariantDict d;
GVariantIter iter;
const gchar *stream;
guint priority;
int i;
if (self->streams) {
g_variant_iter_init (&iter, self->streams);
for (i = 0; g_variant_iter_next (&iter, "(&su)", &stream, &priority); i++) {
g_variant_dict_init (&d, NULL);
g_variant_dict_insert (&d, "id", "u", i);
g_variant_dict_insert (&d, "name", "s", stream);
g_variant_dict_insert (&d, "priority", "u", priority);
wp_base_endpoint_register_stream (WP_BASE_ENDPOINT (self), g_variant_dict_end (&d));
}
}
G_OBJECT_CLASS (wp_endpoint_audiotestsrc_parent_class)->constructed (object);
}
static void
wp_endpoint_audiotestsrc_set_property (GObject * object, guint property_id,
const GValue * value, GParamSpec * pspec)
{
WpEndpointAudiotestsrc *self = WP_ENDPOINT_AUDIOTESTSRC (object);
switch (property_id) {
case PROP_PROXY_NODE:
self->node = g_value_dup_object (value);
break;
case PROP_STREAMS:
self->streams = g_value_dup_variant(value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
wp_endpoint_audiotestsrc_get_property (GObject * object, guint property_id,
GValue * value, GParamSpec * pspec)
{
WpEndpointAudiotestsrc *self = WP_ENDPOINT_AUDIOTESTSRC (object);
switch (property_id) {
case PROP_PROXY_NODE:
g_value_set_object (value, self->node);
break;
case PROP_STREAMS:
g_value_set_variant (value, self->streams);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
wp_endpoint_audiotestsrc_finalize (GObject * object)
{
WpEndpointAudiotestsrc *self = WP_ENDPOINT_AUDIOTESTSRC (object);
g_clear_object(&self->node);
g_clear_pointer(&self->streams, g_variant_unref);
G_OBJECT_CLASS (wp_endpoint_audiotestsrc_parent_class)->finalize (object);
}
static void
wp_endpoint_audiotestsrc_finish_creation (WpCore *core, GAsyncResult *res,
WpEndpointAudiotestsrc *self)
{
g_task_return_boolean (self->init_task, TRUE);
g_clear_object (&self->init_task);
}
static void
wp_endpoint_audiotestsrc_init_async (GAsyncInitable *initable, int io_priority,
GCancellable *cancellable, GAsyncReadyCallback callback, gpointer data)
{
WpEndpointAudiotestsrc *self = WP_ENDPOINT_AUDIOTESTSRC (initable);
self->init_task = g_task_new (initable, cancellable, callback, data);
wp_endpoint_audiotestsrc_parent_interface->init_async (initable, io_priority,
cancellable, callback, data);
g_autoptr (WpCore) core = wp_base_endpoint_get_core (WP_BASE_ENDPOINT(self));
g_return_if_fail (core);
wp_core_sync (core, NULL,
(GAsyncReadyCallback) wp_endpoint_audiotestsrc_finish_creation, self);
}
static void
wp_endpoint_audiotestsrc_async_initable_init (gpointer iface,
gpointer iface_data)
{
GAsyncInitableIface *ai_iface = iface;
wp_endpoint_audiotestsrc_parent_interface =
g_type_interface_peek_parent (iface);
ai_iface->init_async = wp_endpoint_audiotestsrc_init_async;
}
static void
wp_endpoint_audiotestsrc_init (WpEndpointAudiotestsrc * self)
{
static guint id = 0;
self->id = id++;
}
static void
wp_endpoint_audiotestsrc_class_init (WpEndpointAudiotestsrcClass * klass)
{
GObjectClass *object_class = (GObjectClass *) klass;
WpBaseEndpointClass *endpoint_class = (WpBaseEndpointClass *) klass;
object_class->constructed = wp_endpoint_audiotestsrc_constructed;
object_class->finalize = wp_endpoint_audiotestsrc_finalize;
object_class->set_property = wp_endpoint_audiotestsrc_set_property;
object_class->get_property = wp_endpoint_audiotestsrc_get_property;
endpoint_class->get_properties = wp_endpoint_audiotestsrc_get_properties;
endpoint_class->get_role = wp_endpoint_audiotestsrc_get_role;
endpoint_class->get_global_id = wp_endpoint_audiotestsrc_get_global_id;
endpoint_class->prepare_link = wp_endpoint_audiotestsrc_prepare_link;
endpoint_class->get_endpoint_link_factory =
wp_endpoint_audiotestsrc_get_endpoint_link_factory;
g_object_class_install_property (object_class, PROP_PROXY_NODE,
g_param_spec_object ("node", "node",
"The node this endpoint refers to", WP_TYPE_NODE,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (object_class, PROP_STREAMS,
g_param_spec_variant ("streams", "streams",
"The stream names for the streams to register",
G_VARIANT_TYPE ("a(su)"), NULL,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
}
void
wp_endpoint_audiotestsrc_factory (WpFactory * factory, GType type,
GVariant * properties, GAsyncReadyCallback ready, gpointer data)
{
g_autoptr (WpCore) core = NULL;
const gchar *name, *media_class;
guint direction, priority;
guint64 node;
g_autoptr (GVariant) streams = NULL;
core = wp_factory_get_core(factory);
g_return_if_fail (core);
if (!g_variant_lookup (properties, "name", "&s", &name))
return;
if (!g_variant_lookup (properties, "media-class", "&s", &media_class))
return;
if (!g_variant_lookup (properties, "direction", "u", &direction))
return;
if (!g_variant_lookup (properties, "priority", "u", &priority))
return;
if (!g_variant_lookup (properties, "node", "t", &node))
return;
streams = g_variant_lookup_value (properties, "streams",
G_VARIANT_TYPE ("a(su)"));
g_async_initable_new_async (wp_endpoint_audiotestsrc_get_type (),
G_PRIORITY_DEFAULT, NULL, ready, data,
"core", core,
"name", name,
"media-class", media_class,
"direction", direction,
"priority", priority,
"node", (gpointer) node,
"streams", streams,
NULL);
}

View file

@ -1,24 +0,0 @@
/* WirePlumber
*
* Copyright © 2019 Collabora Ltd.
* @author Julian Bouzas <julian.bouzas@collabora.com>
*
* SPDX-License-Identifier: MIT
*/
#ifndef __WIREPLUMBER_ENDPOINT_AUDIOTESTSRC_H__
#define __WIREPLUMBER_ENDPOINT_AUDIOTESTSRC_H__
#include <wp/wp.h>
G_BEGIN_DECLS
G_DECLARE_FINAL_TYPE (WpEndpointAudiotestsrc, wp_endpoint_audiotestsrc, WP,
ENDPOINT_AUDIOTESTSRC, WpBaseEndpoint)
void wp_endpoint_audiotestsrc_factory (WpFactory * factory, GType type,
GVariant * properties, GAsyncReadyCallback ready, gpointer data);
G_END_DECLS
#endif

View file

@ -0,0 +1,14 @@
[match-node]
properties = [
{ name = "media.class", value = "Audio/Source" },
]
[endpoint]
session = "default"
type = "si-simple-node-endpoint"
[endpoint.config]
name = "audiotestsrc-endpoint"
media_class = "Audio/Source"
role = "Multimedia"
priority = 0

View file

@ -0,0 +1,15 @@
[match-node]
properties = [
{ name = "media.class", value = "Audio/Source" },
]
[endpoint]
session = "default"
type = "si-adapter"
streams = "default.streams"
[endpoint.config]
name = "audiotestsrc-endpoint"
media_class = "Audio/Source"
role = "Multimedia"
priority = 0

View file

@ -1,15 +1,19 @@
[[streams]]
name = "0"
priority = 0
enable_control_port = false
[[streams]]
name = "2"
priority = 2
enable_control_port = false
[[streams]]
name = "3"
priority = 3
enable_control_port = false
[[streams]]
name = "4"
priority = 4
enable_control_port = false

View file

@ -38,7 +38,6 @@ test(
executable('test-config-endpoint',
[
'config-endpoint.c',
'config-endpoint/endpoint-audiotestsrc.c',
'../../modules/module-config-endpoint/parser-endpoint.c',
'../../modules/module-config-endpoint/parser-streams.c',
'../../modules/module-config-endpoint/context.c',