tests: remove audiotestsrc-play example
This commit is contained in:
@@ -1,406 +0,0 @@
|
|||||||
/* WirePlumber
|
|
||||||
*
|
|
||||||
* Copyright © 2020 Collabora Ltd.
|
|
||||||
* @author George Kiagiadakis <george.kiagiadakis@collabora.com>
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This is a very simplistic session manager example that also runs an internal
|
|
||||||
* PipeWire server for ease of use. The PipeWire server runs in its own thread
|
|
||||||
* and our main thread's WpCore (the AppData.core) connects to it through
|
|
||||||
* a socket, as if the PipeWire server was in a different process.
|
|
||||||
*
|
|
||||||
* This example starts 2 media nodes in the media graph: audiotestsrc & alsasink
|
|
||||||
* Then, the session management part constructs endpoints for these nodes
|
|
||||||
* and links them by creating an endpoint link.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <wp/wp.h>
|
|
||||||
#include <glib-unix.h>
|
|
||||||
#include "../common/test-server.h"
|
|
||||||
|
|
||||||
#define APP_ERROR_DOMAIN (app_error_domain_quark ())
|
|
||||||
G_DEFINE_QUARK (app-error, app_error_domain)
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
/* our internal test PipeWire server */
|
|
||||||
WpTestServer server;
|
|
||||||
|
|
||||||
/* cmdline arguments */
|
|
||||||
const gchar *alsa_device;
|
|
||||||
|
|
||||||
/* our main loop and core */
|
|
||||||
GMainContext *context;
|
|
||||||
GMainLoop *loop;
|
|
||||||
WpCore *core;
|
|
||||||
WpSession *session;
|
|
||||||
|
|
||||||
/* nodes provider data */
|
|
||||||
WpNode *audiotestsrc;
|
|
||||||
WpNode *alsasink;
|
|
||||||
|
|
||||||
/* endpoints provider data */
|
|
||||||
WpObjectManager *nodes_om;
|
|
||||||
GPtrArray *session_items;
|
|
||||||
|
|
||||||
/* policy manager data */
|
|
||||||
GSource *interrupt_source;
|
|
||||||
|
|
||||||
} AppData;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* policy manager: link endpoints together
|
|
||||||
*/
|
|
||||||
|
|
||||||
static void
|
|
||||||
on_endpoints_changed (WpSession * session, AppData * d)
|
|
||||||
{
|
|
||||||
g_autoptr (WpEndpoint) src = NULL;
|
|
||||||
g_autoptr (WpEndpoint) sink = NULL;
|
|
||||||
|
|
||||||
g_print ("Endpoints changed, n_endpoints=%u\n",
|
|
||||||
wp_session_get_n_endpoints (session));
|
|
||||||
|
|
||||||
/* a very simplistic lookup, since we don't expect any other endpoints
|
|
||||||
to show up here, but this is the general idea...
|
|
||||||
match endpoints, create links, cache the state and move forward */
|
|
||||||
src = wp_session_lookup_endpoint (session,
|
|
||||||
WP_CONSTRAINT_TYPE_PW_PROPERTY, "media.class", "=s", "Audio/Source", NULL);
|
|
||||||
sink = wp_session_lookup_endpoint (session,
|
|
||||||
WP_CONSTRAINT_TYPE_PW_PROPERTY, "media.class", "=s", "Audio/Sink", NULL);
|
|
||||||
|
|
||||||
if (src) {
|
|
||||||
g_print ("Got endpoint src: %s\n", wp_endpoint_get_name (src));
|
|
||||||
}
|
|
||||||
if (sink) {
|
|
||||||
g_print ("Got endpoint sink: %s\n", wp_endpoint_get_name (sink));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (src && sink) {
|
|
||||||
g_autoptr (WpProperties) props = NULL;
|
|
||||||
g_autofree gchar * id =
|
|
||||||
g_strdup_printf ("%u", wp_proxy_get_bound_id (WP_PROXY (sink)));
|
|
||||||
|
|
||||||
/* only the peer endpoint id is required when linking the default streams;
|
|
||||||
everything else will be discovered */
|
|
||||||
props = wp_properties_new ("endpoint-link.input.endpoint", id, NULL);
|
|
||||||
wp_endpoint_create_link (src, props);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
on_links_changed (WpSession * session, AppData * d)
|
|
||||||
{
|
|
||||||
guint n_links = wp_session_get_n_links (session);
|
|
||||||
|
|
||||||
/* activate the link - when endpoint links are created,
|
|
||||||
they don't do anything unless they are activated first */
|
|
||||||
if (n_links == 1) {
|
|
||||||
/* lookup with no constraints will just return the first available object */
|
|
||||||
g_autoptr (WpEndpointLink) link = wp_session_lookup_link (session, NULL);
|
|
||||||
|
|
||||||
g_print ("Requesting link activation...\n");
|
|
||||||
wp_endpoint_link_request_state (link, WP_ENDPOINT_LINK_STATE_ACTIVE);
|
|
||||||
}
|
|
||||||
else if (n_links == 0) {
|
|
||||||
g_print ("Last endpoint link was destroyed; exiting...\n");
|
|
||||||
g_main_loop_quit (d->loop);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static gboolean
|
|
||||||
on_interrupted (AppData * d)
|
|
||||||
{
|
|
||||||
g_print ("interrupted; let's try to destroy the link...\n");
|
|
||||||
|
|
||||||
g_autoptr (WpEndpointLink) link = wp_session_lookup_link (d->session, NULL);
|
|
||||||
if (link)
|
|
||||||
wp_global_proxy_request_destroy (WP_GLOBAL_PROXY (link));
|
|
||||||
|
|
||||||
/* remove the interrupt handler so that we can actually
|
|
||||||
interrupt if things get stuck */
|
|
||||||
g_clear_pointer (&d->interrupt_source, g_source_unref);
|
|
||||||
return G_SOURCE_REMOVE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
start_policy_manager (AppData * d)
|
|
||||||
{
|
|
||||||
/* reuse the session pointer that we already have in AppData;
|
|
||||||
under other circumstances, we would retrieve the session
|
|
||||||
with a WpObjectManager */
|
|
||||||
g_signal_connect (d->session, "endpoints-changed",
|
|
||||||
G_CALLBACK (on_endpoints_changed), d);
|
|
||||||
g_signal_connect (d->session, "links-changed",
|
|
||||||
G_CALLBACK (on_links_changed), d);
|
|
||||||
|
|
||||||
d->interrupt_source = g_unix_signal_source_new (SIGINT);
|
|
||||||
g_source_set_callback (d->interrupt_source,
|
|
||||||
G_SOURCE_FUNC (on_interrupted), d, NULL);
|
|
||||||
g_source_attach (d->interrupt_source, d->context);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* endpoints provider: creates endpoints for the discovered nodes
|
|
||||||
*/
|
|
||||||
|
|
||||||
static void
|
|
||||||
on_si_activated (WpObject * object, GAsyncResult * res, AppData * d)
|
|
||||||
{
|
|
||||||
g_autoptr (GError) error = NULL;
|
|
||||||
|
|
||||||
if (!wp_object_activate_finish (object, res, &error)) {
|
|
||||||
g_printerr ("Failed to activate session item: %s\n", error->message);
|
|
||||||
g_main_loop_quit (d->loop);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
g_print ("Item " WP_OBJECT_FORMAT " exported\n", WP_OBJECT_ARGS (object));
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
on_node_added (WpObjectManager * om, WpNode *node, AppData * d)
|
|
||||||
{
|
|
||||||
g_autoptr (WpSessionItem) item = NULL;
|
|
||||||
g_autoptr (WpProperties) props = wp_properties_new_empty ();
|
|
||||||
|
|
||||||
g_print ("Node " WP_OBJECT_FORMAT " added, creating session item\n",
|
|
||||||
WP_OBJECT_ARGS (node));
|
|
||||||
|
|
||||||
/* load the "si-audio-adapter" Session Item */
|
|
||||||
item = wp_session_item_make (d->core, "si-audio-adapter");
|
|
||||||
|
|
||||||
/* and configure it */
|
|
||||||
wp_properties_setf (props, "node", "%p", node);
|
|
||||||
wp_properties_setf (props, "session", "%p", d->session);
|
|
||||||
if (!wp_session_item_configure (item, g_steal_pointer (&props))) {
|
|
||||||
g_printerr ("Failed to configure session item\n");
|
|
||||||
g_main_loop_quit (d->loop);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
wp_object_activate (WP_OBJECT (item),
|
|
||||||
WP_SESSION_ITEM_FEATURE_ACTIVE | WP_SESSION_ITEM_FEATURE_EXPORTED,
|
|
||||||
NULL, (GAsyncReadyCallback) on_si_activated, d);
|
|
||||||
g_ptr_array_add (d->session_items, g_steal_pointer (&item));
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
start_endpoints_provider (AppData * d)
|
|
||||||
{
|
|
||||||
g_print ("Installing watch for nodes...\n");
|
|
||||||
|
|
||||||
/* register a WpObjectManager to listen for available nodes */
|
|
||||||
/* for example purposes, we pretend we don't have access to the data set by
|
|
||||||
start_nodes_provider(), i.e. d->audiotestsrc & d->alsasink */
|
|
||||||
d->nodes_om = wp_object_manager_new ();
|
|
||||||
wp_object_manager_add_interest (d->nodes_om, WP_TYPE_NODE, NULL);
|
|
||||||
wp_object_manager_request_object_features (d->nodes_om, WP_TYPE_NODE,
|
|
||||||
WP_PIPEWIRE_OBJECT_FEATURES_MINIMAL);
|
|
||||||
|
|
||||||
d->session_items = g_ptr_array_new_with_free_func (g_object_unref);
|
|
||||||
|
|
||||||
/* the object manager will emit 'object-added' for every node that is
|
|
||||||
made available, once the node has all the features we requested above */
|
|
||||||
g_signal_connect (d->nodes_om, "object-added", G_CALLBACK (on_node_added), d);
|
|
||||||
wp_core_install_object_manager (d->core, d->nodes_om);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* nodes provider: creates the nodes
|
|
||||||
*/
|
|
||||||
|
|
||||||
static void
|
|
||||||
on_node_ready (WpObject * node, GAsyncResult * res, AppData * d)
|
|
||||||
{
|
|
||||||
g_autoptr (GError) error = NULL;
|
|
||||||
|
|
||||||
if (!wp_object_activate_finish (node, res, &error)) {
|
|
||||||
g_printerr ("Failed to prepare node: %s\n", error->message);
|
|
||||||
g_main_loop_quit (d->loop);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
g_print ("Node " WP_OBJECT_FORMAT " is ready\n", WP_OBJECT_ARGS (node));
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
start_nodes_provider (AppData * d)
|
|
||||||
{
|
|
||||||
g_print ("Creating nodes...\n");
|
|
||||||
|
|
||||||
d->audiotestsrc = wp_node_new_from_factory (d->core,
|
|
||||||
"adapter", /* the pipewire factory name */
|
|
||||||
wp_properties_new (
|
|
||||||
/* the spa factory name */
|
|
||||||
"factory.name", "audiotestsrc",
|
|
||||||
/* a friendly name for our node */
|
|
||||||
"node.name", "audiotestsrc",
|
|
||||||
NULL));
|
|
||||||
g_assert (d->audiotestsrc);
|
|
||||||
wp_object_activate (WP_OBJECT (d->audiotestsrc),
|
|
||||||
WP_PIPEWIRE_OBJECT_FEATURES_MINIMAL, NULL,
|
|
||||||
(GAsyncReadyCallback) on_node_ready, d);
|
|
||||||
|
|
||||||
d->alsasink = wp_node_new_from_factory (d->core,
|
|
||||||
"adapter", /* the pipewire factory name */
|
|
||||||
wp_properties_new (
|
|
||||||
/* the spa factory name */
|
|
||||||
"factory.name", "api.alsa.pcm.sink",
|
|
||||||
/* a friendly name for our node */
|
|
||||||
"node.name", "alsasink",
|
|
||||||
/* set the device handle (ex. hw:0,0) on the sink */
|
|
||||||
"api.alsa.path", d->alsa_device,
|
|
||||||
NULL));
|
|
||||||
g_assert (d->alsasink);
|
|
||||||
wp_object_activate (WP_OBJECT (d->alsasink),
|
|
||||||
WP_PIPEWIRE_OBJECT_FEATURES_MINIMAL, NULL,
|
|
||||||
(GAsyncReadyCallback) on_node_ready, d);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* main application: loads modules and the session
|
|
||||||
*/
|
|
||||||
|
|
||||||
static void
|
|
||||||
on_session_ready (WpObject * session, GAsyncResult * res, AppData * d)
|
|
||||||
{
|
|
||||||
g_autoptr (GError) error = NULL;
|
|
||||||
|
|
||||||
if (!wp_object_activate_finish (session, res, &error)) {
|
|
||||||
g_printerr ("Failed to prepare session: %s\n", error->message);
|
|
||||||
g_main_loop_quit (d->loop);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
g_print ("Session is ready, starting components...\n");
|
|
||||||
|
|
||||||
start_nodes_provider (d);
|
|
||||||
start_endpoints_provider (d);
|
|
||||||
start_policy_manager (d);
|
|
||||||
}
|
|
||||||
|
|
||||||
static gboolean
|
|
||||||
appdata_init (AppData * d, GError ** error)
|
|
||||||
{
|
|
||||||
WpImplSession *session;
|
|
||||||
|
|
||||||
/* setup the internal test PipeWire server */
|
|
||||||
wp_test_server_setup (&d->server);
|
|
||||||
{
|
|
||||||
/* load server modules (pipewire.conf) */
|
|
||||||
g_autoptr (WpTestServerLocker) lock =
|
|
||||||
wp_test_server_locker_new (&d->server);
|
|
||||||
|
|
||||||
pw_context_add_spa_lib (d->server.context,
|
|
||||||
"audiotestsrc", "audiotestsrc/libspa-audiotestsrc");
|
|
||||||
pw_context_add_spa_lib (d->server.context,
|
|
||||||
"api.alsa.*", "alsa/libspa-alsa");
|
|
||||||
|
|
||||||
if (!pw_context_load_module (d->server.context,
|
|
||||||
"libpipewire-module-spa-node-factory", NULL, NULL)) {
|
|
||||||
g_set_error (error, APP_ERROR_DOMAIN, 0,
|
|
||||||
"Failed to load libpipewire-module-spa-node-factory");
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
if (!pw_context_load_module (d->server.context,
|
|
||||||
"libpipewire-module-link-factory", NULL, NULL)) {
|
|
||||||
g_set_error (error, APP_ERROR_DOMAIN, 0,
|
|
||||||
"Failed to load libpipewire-module-link-factory");
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
/* adapter is loaded by pw_context */
|
|
||||||
}
|
|
||||||
|
|
||||||
/* init our main loop */
|
|
||||||
d->context = g_main_context_new ();
|
|
||||||
d->loop = g_main_loop_new (d->context, FALSE);
|
|
||||||
|
|
||||||
/* push the context as the thread default for GTask to work with it,
|
|
||||||
otherwise it will try to use the "default" main context, which we are
|
|
||||||
not using in our main loop, for demonstration purposes */
|
|
||||||
g_main_context_push_thread_default (d->context);
|
|
||||||
|
|
||||||
/* init our core; the "remote.name" key tells it to connect to our
|
|
||||||
test server instead of the default "pipewire-0" */
|
|
||||||
d->core = wp_core_new (d->context, wp_properties_new (
|
|
||||||
"remote.name", d->server.name,
|
|
||||||
NULL));
|
|
||||||
|
|
||||||
/* load wireplumber modules (wireplumber.conf) */
|
|
||||||
if (!(wp_core_load_component (d->core,
|
|
||||||
"libwireplumber-module-si-node", "module", NULL, error)))
|
|
||||||
return FALSE;
|
|
||||||
|
|
||||||
if (!(wp_core_load_component (d->core,
|
|
||||||
"libwireplumber-module-si-audio-adapter", "module", NULL, error)))
|
|
||||||
return FALSE;
|
|
||||||
|
|
||||||
if (!(wp_core_load_component (d->core,
|
|
||||||
"libwireplumber-module-si-standard-link", "module", NULL, error)))
|
|
||||||
return FALSE;
|
|
||||||
|
|
||||||
/* connect */
|
|
||||||
if (!wp_core_connect (d->core)) {
|
|
||||||
g_set_error (error, APP_ERROR_DOMAIN, 0,
|
|
||||||
"Failed to connect to the test server");
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
g_print ("Creating session...\n");
|
|
||||||
|
|
||||||
/* create a session */
|
|
||||||
d->session = WP_SESSION (session = wp_impl_session_new (d->core));
|
|
||||||
wp_impl_session_set_property (session, "session.name", "audio");
|
|
||||||
wp_object_activate (WP_OBJECT (session), WP_OBJECT_FEATURES_ALL, NULL,
|
|
||||||
(GAsyncReadyCallback) on_session_ready, d);
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
appdata_clear (AppData * d)
|
|
||||||
{
|
|
||||||
/* policy manager data */
|
|
||||||
g_clear_pointer (&d->interrupt_source, g_source_unref);
|
|
||||||
|
|
||||||
/* endpoints provider data */
|
|
||||||
g_clear_pointer (&d->session_items, g_ptr_array_unref);
|
|
||||||
g_clear_object (&d->nodes_om);
|
|
||||||
|
|
||||||
/* nodes provider data */
|
|
||||||
g_clear_object (&d->audiotestsrc);
|
|
||||||
g_clear_object (&d->alsasink);
|
|
||||||
|
|
||||||
/* main app data */
|
|
||||||
g_clear_object (&d->session);
|
|
||||||
g_clear_object (&d->core);
|
|
||||||
g_clear_pointer (&d->loop, g_main_loop_unref);
|
|
||||||
g_clear_pointer (&d->context, g_main_context_unref);
|
|
||||||
wp_test_server_teardown (&d->server);
|
|
||||||
}
|
|
||||||
|
|
||||||
G_DEFINE_AUTO_CLEANUP_CLEAR_FUNC (AppData, appdata_clear)
|
|
||||||
|
|
||||||
gint
|
|
||||||
main (gint argc, gchar *argv[])
|
|
||||||
{
|
|
||||||
g_auto (AppData) data = {0};
|
|
||||||
g_autoptr (GError) error = NULL;
|
|
||||||
|
|
||||||
wp_init (WP_INIT_ALL);
|
|
||||||
|
|
||||||
if (argc > 1)
|
|
||||||
data.alsa_device = argv[1];
|
|
||||||
else
|
|
||||||
data.alsa_device = "hw:0,0";
|
|
||||||
|
|
||||||
if (!appdata_init (&data, &error)) {
|
|
||||||
g_printerr ("Initialization failed:\n %s\n", error->message);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
g_main_loop_run (data.loop);
|
|
||||||
return 0;
|
|
||||||
}
|
|
@@ -1,13 +0,0 @@
|
|||||||
# disabled temporarily
|
|
||||||
if false
|
|
||||||
executable('audiotestsrc-play',
|
|
||||||
'audiotestsrc-play.c',
|
|
||||||
c_args : [
|
|
||||||
'-D_GNU_SOURCE',
|
|
||||||
'-DG_LOG_USE_STRUCTURED',
|
|
||||||
'-DG_LOG_DOMAIN="audiotestsrc-play"',
|
|
||||||
],
|
|
||||||
install: false,
|
|
||||||
dependencies : [giounix_dep, wp_dep, pipewire_dep],
|
|
||||||
)
|
|
||||||
endif
|
|
@@ -65,4 +65,3 @@ if build_modules
|
|||||||
subdir('scripts')
|
subdir('scripts')
|
||||||
subdir('modules')
|
subdir('modules')
|
||||||
endif
|
endif
|
||||||
subdir('examples')
|
|
||||||
|
Reference in New Issue
Block a user