Commit 7b9e8a03 authored by George Kiagiadakis's avatar George Kiagiadakis

lib: add a new WpRemote base class and move the pw_remote glue into a new WpRemotePipewire

This is a cleaner way to interface with the remote pipewire daemon.
The WpRemote base class can be subclassed also for interfacing
with other daemons (hardware-specific managers, etc)
parent 08ef641a
......@@ -214,10 +214,9 @@ wp_core_remove_global (WpCore * self, GQuark key, gpointer obj)
}
}
G_DEFINE_QUARK (pw-core, wp_global_pw_core)
G_DEFINE_QUARK (pw-remote, wp_global_pw_remote)
G_DEFINE_QUARK (endpoint, wp_global_endpoint)
G_DEFINE_QUARK (factory, wp_global_factory)
G_DEFINE_QUARK (module, wp_global_module)
G_DEFINE_QUARK (policy-manager, wp_global_policy_manager)
G_DEFINE_QUARK (proxy, wp_global_proxy)
G_DEFINE_QUARK (remote-pipewire, wp_global_remote_pipewire)
......@@ -34,20 +34,6 @@ void wp_core_register_global (WpCore * self, GQuark key, gpointer obj,
GDestroyNotify destroy_obj);
void wp_core_remove_global (WpCore * self, GQuark key, gpointer obj);
/**
* WP_GLOBAL_PW_CORE:
* The key to access the pw_core global object
*/
#define WP_GLOBAL_PW_CORE (wp_global_pw_core_quark ())
GQuark wp_global_pw_core_quark (void);
/**
* WP_GLOBAL_PW_REMOTE:
* The key to access the pw_remote global object
*/
#define WP_GLOBAL_PW_REMOTE (wp_global_pw_remote_quark ())
GQuark wp_global_pw_remote_quark (void);
#define WP_GLOBAL_ENDPOINT (wp_global_endpoint_quark ())
GQuark wp_global_endpoint_quark (void);
......@@ -63,6 +49,14 @@ GQuark wp_global_policy_manager_quark (void);
#define WP_GLOBAL_PROXY (wp_global_proxy_quark ())
GQuark wp_global_proxy_quark (void);
/**
* WP_GLOBAL_REMOTE_PIPEWIRE:
* The key to access the #WpRemote global object that maintains
* the connection to pipewire
*/
#define WP_GLOBAL_REMOTE_PIPEWIRE (wp_global_remote_pipewire_quark ())
GQuark wp_global_remote_pipewire_quark (void);
G_END_DECLS
#endif
......@@ -8,6 +8,8 @@ wp_lib_sources = [
'proxy.c',
'proxy-node.c',
'proxy-port.c',
'remote.c',
'remote-pipewire.c',
]
wp_lib_headers = [
......@@ -20,6 +22,8 @@ wp_lib_headers = [
'proxy.h',
'proxy-node.h',
'proxy-port.h',
'remote.h',
'remote-pipewire.h',
'wp.h',
]
......
/* WirePlumber
*
* Copyright © 2019 Collabora Ltd.
* @author George Kiagiadakis <george.kiagiadakis@collabora.com>
*
* SPDX-License-Identifier: MIT
*/
#include "remote-pipewire.h"
#include <pipewire/pipewire.h>
/**
* Integration between the PipeWire main loop and GMainLoop
*/
#define WP_LOOP_SOURCE(x) ((WpLoopSource *) x)
typedef struct _WpLoopSource WpLoopSource;
struct _WpLoopSource
{
GSource parent;
struct pw_loop *loop;
};
static gboolean
wp_loop_source_dispatch (GSource * s, GSourceFunc callback, gpointer user_data)
{
int result;
pw_loop_enter (WP_LOOP_SOURCE(s)->loop);
result = pw_loop_iterate (WP_LOOP_SOURCE(s)->loop, 0);
pw_loop_leave (WP_LOOP_SOURCE(s)->loop);
if (G_UNLIKELY (result < 0))
g_warning ("pw_loop_iterate failed: %s", spa_strerror (result));
return G_SOURCE_CONTINUE;
}
static void
wp_loop_source_finalize (GSource * s)
{
pw_loop_destroy (WP_LOOP_SOURCE(s)->loop);
}
static GSourceFuncs source_funcs = {
NULL,
NULL,
wp_loop_source_dispatch,
wp_loop_source_finalize
};
static GSource *
wp_loop_source_new (void)
{
GSource *s = g_source_new (&source_funcs, sizeof (WpLoopSource));
WP_LOOP_SOURCE(s)->loop = pw_loop_new (NULL);
g_source_add_unix_fd (s,
pw_loop_get_fd (WP_LOOP_SOURCE(s)->loop),
G_IO_IN | G_IO_ERR | G_IO_HUP);
return (GSource *) s;
}
/**
* WpRemotePipewire
*/
struct _WpRemotePipewire
{
WpRemote parent;
struct pw_core *core;
struct pw_remote *remote;
struct spa_hook remote_listener;
GMainContext *context;
};
enum {
PROP_0,
PROP_STATE,
PROP_ERROR_MESSAGE,
PROP_PW_CORE,
PROP_PW_REMOTE,
PROP_CONTEXT,
};
G_DEFINE_TYPE (WpRemotePipewire, wp_remote_pipewire, WP_TYPE_REMOTE)
static void
on_remote_state_changed (void *d, enum pw_remote_state old_state,
enum pw_remote_state new_state, const char *error)
{
WpRemotePipewire *self = d;
g_debug ("pipewire remote state changed, old:%s new:%s",
pw_remote_state_as_string (old_state),
pw_remote_state_as_string (new_state));
g_object_notify (G_OBJECT (self), "state");
}
static const struct pw_remote_events remote_events = {
PW_VERSION_REMOTE_EVENTS,
.state_changed = on_remote_state_changed,
};
static void
wp_remote_pipewire_init (WpRemotePipewire *self)
{
}
static void
wp_remote_pipewire_constructed (GObject *object)
{
WpRemotePipewire *self = WP_REMOTE_PIPEWIRE (object);
GSource *source;
source = wp_loop_source_new ();
g_source_attach (source, self->context);
self->core = pw_core_new (WP_LOOP_SOURCE (source)->loop, NULL, 0);
self->remote = pw_remote_new (self->core, NULL, 0);
pw_remote_add_listener (self->remote, &self->remote_listener, &remote_events,
self);
G_OBJECT_CLASS (wp_remote_pipewire_parent_class)->constructed (object);
}
static void
wp_remote_pipewire_finalize (GObject *object)
{
WpRemotePipewire *self = WP_REMOTE_PIPEWIRE (object);
pw_remote_destroy (self->remote);
pw_core_destroy (self->core);
g_clear_pointer (&self->context, g_main_context_unref);
G_OBJECT_CLASS (wp_remote_pipewire_parent_class)->finalize (object);
}
static void
wp_remote_pipewire_get_property (GObject * object, guint property_id,
GValue * value, GParamSpec * pspec)
{
WpRemotePipewire *self = WP_REMOTE_PIPEWIRE (object);
switch (property_id) {
case PROP_STATE:
/* enum pw_remote_state matches values with WpRemoteState */
g_value_set_enum (value, pw_remote_get_state (self->remote, NULL));
break;
case PROP_ERROR_MESSAGE:
{
const gchar *msg;
(void) pw_remote_get_state (self->remote, &msg);
g_value_set_string (value, msg);
break;
}
case PROP_PW_CORE:
g_value_set_pointer (value, self->core);
break;
case PROP_PW_REMOTE:
g_value_set_pointer (value, self->remote);
break;
case PROP_CONTEXT:
g_value_set_boxed (value, self->context);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
wp_remote_pipewire_set_property (GObject * object, guint property_id,
const GValue * value, GParamSpec * pspec)
{
WpRemotePipewire *self = WP_REMOTE_PIPEWIRE (object);
switch (property_id) {
case PROP_CONTEXT:
self->context = g_value_dup_boxed (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static gboolean
connect_in_idle (WpRemotePipewire *self)
{
pw_remote_connect (self->remote);
return G_SOURCE_REMOVE;
}
static gboolean
wp_remote_pipewire_connect (WpRemote *remote)
{
WpRemotePipewire *self = WP_REMOTE_PIPEWIRE (remote);
GSource *source;
source = g_idle_source_new ();
g_source_set_callback (source, (GSourceFunc) connect_in_idle, self, NULL);
g_source_attach (source, self->context);
return TRUE;
}
static void
wp_remote_pipewire_class_init (WpRemotePipewireClass *klass)
{
GObjectClass *object_class = (GObjectClass *) klass;
WpRemoteClass *remote_class = (WpRemoteClass *) klass;
pw_init (NULL, NULL);
object_class->constructed = wp_remote_pipewire_constructed;
object_class->finalize = wp_remote_pipewire_finalize;
object_class->get_property = wp_remote_pipewire_get_property;
object_class->set_property = wp_remote_pipewire_set_property;
remote_class->connect = wp_remote_pipewire_connect;
g_object_class_override_property (object_class, PROP_STATE, "state");
g_object_class_override_property (object_class, PROP_ERROR_MESSAGE,
"error-message");
g_object_class_install_property (object_class, PROP_CONTEXT,
g_param_spec_boxed ("context", "context", "A GMainContext to attach to",
G_TYPE_MAIN_CONTEXT,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (object_class, PROP_PW_CORE,
g_param_spec_pointer ("pw-core", "pw-core", "The pipewire core",
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (object_class, PROP_PW_REMOTE,
g_param_spec_pointer ("pw-remote", "pw-remote", "The pipewire remote",
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
}
WpRemote *
wp_remote_pipewire_new (WpCore *core, GMainContext *context)
{
WpRemote *remote;
g_return_val_if_fail (WP_IS_CORE (core), NULL);
remote = g_object_new (WP_TYPE_REMOTE_PIPEWIRE,
"core", core,
"context", context,
NULL);
wp_core_register_global (core, WP_GLOBAL_REMOTE_PIPEWIRE,
g_object_ref (remote), g_object_unref);
return remote;
}
......@@ -6,16 +6,19 @@
* SPDX-License-Identifier: MIT
*/
#include <wp/wp.h>
#include <pipewire/pipewire.h>
#ifndef __WIREPLUMBER_REMOTE_PIPEWIRE_H__
#define __WIREPLUMBER_REMOTE_PIPEWIRE_H__
#define WP_LOOP_SOURCE(x) ((WpLoopSource *) x)
#include "remote.h"
typedef struct _WpLoopSource WpLoopSource;
struct _WpLoopSource
{
GSource parent;
struct pw_loop *loop;
};
G_BEGIN_DECLS
GSource * wp_loop_source_new (void);
#define WP_TYPE_REMOTE_PIPEWIRE (wp_remote_pipewire_get_type ())
G_DECLARE_FINAL_TYPE (WpRemotePipewire, wp_remote_pipewire,
WP, REMOTE_PIPEWIRE, WpRemote)
WpRemote *wp_remote_pipewire_new (WpCore *core, GMainContext *context);
G_END_DECLS
#endif
/* WirePlumber
*
* Copyright © 2019 Collabora Ltd.
* @author George Kiagiadakis <george.kiagiadakis@collabora.com>
*
* SPDX-License-Identifier: MIT
*/
#include "remote.h"
#include "wpenums.h"
enum {
PROP_0,
PROP_CORE,
PROP_STATE,
PROP_ERROR_MESSAGE,
};
enum {
SIGNAL_STATE_CHANGED,
N_SIGNALS
};
static guint signals[N_SIGNALS];
typedef struct _WpRemotePrivate WpRemotePrivate;
struct _WpRemotePrivate
{
GWeakRef core;
};
G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (WpRemote, wp_remote, G_TYPE_OBJECT)
static void
wp_remote_init (WpRemote *self)
{
WpRemotePrivate *priv = wp_remote_get_instance_private (self);
g_weak_ref_init (&priv->core, NULL);
}
static void
wp_remote_finalize (GObject *object)
{
WpRemotePrivate *priv = wp_remote_get_instance_private (WP_REMOTE (object));
g_weak_ref_clear (&priv->core);
G_OBJECT_CLASS (wp_remote_parent_class)->finalize (object);
}
static void
wp_remote_set_property (GObject * object, guint property_id,
const GValue * value, GParamSpec * pspec)
{
WpRemotePrivate *priv = wp_remote_get_instance_private (WP_REMOTE (object));
switch (property_id) {
case PROP_CORE:
g_weak_ref_set (&priv->core, g_value_get_object (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
wp_remote_get_property (GObject * object, guint property_id,
GValue * value, GParamSpec * pspec)
{
WpRemotePrivate *priv = wp_remote_get_instance_private (WP_REMOTE (object));
switch (property_id) {
case PROP_CORE:
g_value_take_object (value, g_weak_ref_get (&priv->core));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
wp_remote_notify (GObject *object, GParamSpec *param)
{
if (!g_strcmp0 (param->name, "state")) {
WpRemoteState state;
GParamSpecEnum *param_enum = (GParamSpecEnum *) param;
GEnumValue *value;
GQuark detail;
g_object_get (object, "state", &state, NULL);
value = g_enum_get_value (param_enum->enum_class, state);
detail = g_quark_from_static_string (value->value_nick);
g_signal_emit (object, signals[SIGNAL_STATE_CHANGED], detail, state);
}
if (G_OBJECT_CLASS (wp_remote_parent_class)->notify)
G_OBJECT_CLASS (wp_remote_parent_class)->notify (object, param);
}
static void
wp_remote_class_init (WpRemoteClass *klass)
{
GObjectClass *object_class = (GObjectClass *) klass;
object_class->finalize = wp_remote_finalize;
object_class->set_property = wp_remote_set_property;
object_class->get_property = wp_remote_get_property;
object_class->notify = wp_remote_notify;
g_object_class_install_property (object_class, PROP_CORE,
g_param_spec_object ("core", "core", "The wireplumber core",
WP_TYPE_CORE,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (object_class, PROP_STATE,
g_param_spec_enum ("state", "state", "The state of the remote",
WP_TYPE_REMOTE_STATE, WP_REMOTE_STATE_UNCONNECTED,
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (object_class, PROP_ERROR_MESSAGE,
g_param_spec_string ("error-message", "error-message",
"The last error message of the remote", NULL,
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
signals[SIGNAL_STATE_CHANGED] = g_signal_new ("state-changed",
G_TYPE_FROM_CLASS (klass), G_SIGNAL_DETAILED | G_SIGNAL_RUN_LAST,
0, NULL, NULL, NULL, G_TYPE_NONE, 1, WP_TYPE_REMOTE_STATE);
}
WpCore *
wp_remote_get_core (WpRemote *self)
{
WpRemotePrivate *priv = wp_remote_get_instance_private (self);
return g_weak_ref_get (&priv->core);
}
gboolean
wp_remote_connect (WpRemote *self)
{
if (WP_REMOTE_GET_CLASS (self)->connect)
return WP_REMOTE_GET_CLASS (self)->connect (self);
return FALSE;
}
/* WirePlumber
*
* Copyright © 2019 Collabora Ltd.
* @author George Kiagiadakis <george.kiagiadakis@collabora.com>
*
* SPDX-License-Identifier: MIT
*/
#ifndef __WIREPLUMBER_REMOTE_H__
#define __WIREPLUMBER_REMOTE_H__
#include "core.h"
G_BEGIN_DECLS
typedef enum {
WP_REMOTE_STATE_ERROR = -1, /**< remote is in error */
WP_REMOTE_STATE_UNCONNECTED = 0, /**< not connected */
WP_REMOTE_STATE_CONNECTING = 1, /**< connecting to remote service */
WP_REMOTE_STATE_CONNECTED = 2, /**< remote is connected and ready */
} WpRemoteState;
#define WP_TYPE_REMOTE (wp_remote_get_type ())
G_DECLARE_DERIVABLE_TYPE (WpRemote, wp_remote, WP, REMOTE, GObject)
struct _WpRemoteClass
{
GObjectClass parent_class;
gboolean (*connect) (WpRemote *self);
};
WpCore *wp_remote_get_core (WpRemote *self);
gboolean wp_remote_connect (WpRemote *self);
G_END_DECLS
#endif
......@@ -15,3 +15,5 @@
#include "proxy.h"
#include "proxy-node.h"
#include "proxy-port.h"
#include "remote.h"
#include "remote-pipewire.h"
......@@ -7,7 +7,6 @@ shared_library(
'wireplumber-module-pipewire',
[
'module-pipewire.c',
'module-pipewire/loop-source.c',
'module-pipewire/remote-endpoint.c',
'module-pipewire/simple-endpoint-link.c',
'module-pipewire/simple-endpoint.c',
......
......@@ -16,8 +16,6 @@
#include <pipewire/pipewire.h>
#include <spa/param/audio/format-utils.h>
#include "module-pipewire/loop-source.h"
void remote_endpoint_init (WpCore * core, struct pw_core * pw_core,
struct pw_remote * remote);
gpointer simple_endpoint_factory (WpFactory * factory, GType type,
......@@ -29,11 +27,6 @@ struct module_data
{
WpModule *module;
struct pw_core *core;
struct pw_remote *remote;
struct spa_hook remote_listener;
struct pw_registry_proxy *registry_proxy;
struct spa_hook registry_listener;
......@@ -187,60 +180,21 @@ static const struct pw_registry_proxy_events registry_events = {
};
static void
on_remote_state_changed (void *d, enum pw_remote_state old_state,
enum pw_remote_state new_state, const char *error)
on_remote_connected (WpRemote *remote, WpRemoteState state,
struct module_data *data)
{
struct module_data *data = d;
struct pw_core_proxy *core_proxy;
struct pw_remote *pw_remote;
g_debug ("remote state changed, old:%s new:%s",
pw_remote_state_as_string (old_state),
pw_remote_state_as_string (new_state));
switch (new_state) {
case PW_REMOTE_STATE_CONNECTED:
core_proxy = data->core_proxy = pw_remote_get_core_proxy (data->remote);
pw_core_proxy_add_listener(data->core_proxy, &data->core_listener,
&core_events, data);
data->registry_proxy = pw_core_proxy_get_registry (core_proxy,
PW_TYPE_INTERFACE_Registry, PW_VERSION_REGISTRY, 0);
pw_registry_proxy_add_listener(data->registry_proxy,
&data->registry_listener, &registry_events, data);
break;
case PW_REMOTE_STATE_UNCONNECTED: {
g_autoptr (WpCore) core = wp_module_get_core (data->module);
if (core) {
g_message ("disconnected from PipeWire");
g_main_loop_quit (wp_core_get_global (core,
g_quark_from_string ("main-loop")));
}
break;
}
case PW_REMOTE_STATE_ERROR: {
g_autoptr (WpCore) core = wp_module_get_core (data->module);
if (core) {
g_message ("PipeWire remote error: %s", error);
g_main_loop_quit (wp_core_get_global (core,
g_quark_from_string ("main-loop")));
}
break;
}
default:
break;
}
}
static const struct pw_remote_events remote_events = {
PW_VERSION_REMOTE_EVENTS,
.state_changed = on_remote_state_changed,
};
g_object_get (remote, "pw-remote", &pw_remote, NULL);
static gboolean
connect_in_idle (struct pw_remote *remote)
{
pw_remote_connect (remote);
return G_SOURCE_REMOVE;
core_proxy = data->core_proxy = pw_remote_get_core_proxy (pw_remote);
pw_core_proxy_add_listener(data->core_proxy, &data->core_listener,
&core_events, data);
data->registry_proxy = pw_core_proxy_get_registry (core_proxy,
PW_TYPE_INTERFACE_Registry, PW_VERSION_REGISTRY, 0);
pw_registry_proxy_add_listener(data->registry_proxy,
&data->registry_listener, &registry_events, data);
}
static void
......@@ -249,40 +203,39 @@ module_destroy (gpointer d)
struct module_data *data = d;
g_queue_free_full(data->done_queue, done_data_destroy);
pw_remote_destroy (data->remote);
pw_core_destroy (data->core);
g_slice_free (struct module_data, data);
}
void
wireplumber__module_init (WpModule * module, WpCore * core, GVariant * args)
{
GSource *source;
struct module_data *data;
pw_init (NULL, NULL);
WpRemote *remote;
struct pw_core *pw_core;
struct pw_remote *pw_remote;
remote = wp_core_get_global (core, WP_GLOBAL_REMOTE_PIPEWIRE);
if (!remote) {
g_critical ("module-pipewire cannot be loaded without a registered "
"WpRemotePipewire object");
return;
}
data = g_slice_new0 (struct module_data);
data->module = module;
data->done_queue = g_queue_new();
wp_module_set_destroy_callback (module, module_destroy, data);
source = wp_loop_source_new ();
g_source_attach (source, NULL);
data->core = pw_core_new (WP_LOOP_SOURCE (source)->loop, NULL, 0);
wp_core_register_global (core, WP_GLOBAL_PW_CORE, data->core, NULL);
g_signal_connect (remote, "state-changed::connected",
(GCallback) on_remote_connected, data);
data->remote = pw_remote_new (data->core, NULL, 0);
pw_remote_add_listener (data->remote, &data->remote_listener, &remote_events,
data);
wp_core_register_global (core, WP_GLOBAL_PW_REMOTE, data->remote, NULL);
remote_endpoint_init (core, data->core, data->remote);
g_object_get (remote,
"pw-core", &pw_core,
"pw-remote", &pw_remote,
NULL);
remote_endpoint_init (core, pw_core, pw_remote);
wp_factory_new (core, "pipewire-simple-endpoint", simple_endpoint_factory);
wp_factory_new (core, "pipewire-simple-endpoint-link",
simple_endpoint_link_factory);
g_idle_add ((GSourceFunc) connect_in_idle, data->remote);
}
/* WirePlumber
*
* Copyright © 2019 Collabora Ltd.
* @author George Kiagiadakis <george.kiagiadakis@collabora.com>
*
* SPDX-License-Identifier: MIT
*/
/**
* Integration between the PipeWire main loop and GMainLoop
*/
#include "loop-source.h"
static gboolean
wp_loop_source_dispatch (GSource * s, GSourceFunc callback, gpointer user_data)
{
int result;
pw_loop_enter (WP_LOOP_SOURCE(s)->loop);
result = pw_loop_iterate (WP_LOOP_SOURCE(s)->loop, 0);
pw_loop_leave (WP_LOOP_SOURCE(s)->loop);
if (G_UNLIKELY (result < 0))
g_warning ("pw_loop_iterate failed: %s", spa_strerror (result));
return G_SOURCE_CONTINUE;
}
static void
wp_loop_source_finalize (GSource * s)
{
pw_loop_destroy (WP_LOOP_SOURCE(s)->loop);
}
static GSourceFuncs source_funcs = {