mirror of
https://gitlab.freedesktop.org/pipewire/wireplumber.git
synced 2026-05-05 06:38:01 +02:00
lib: implement WpEndpoint (interface + Proxy + Exported + unit test)
heavily based on the WpSession implementation
This commit is contained in:
parent
c0455c981d
commit
a71d433a5e
7 changed files with 1349 additions and 1 deletions
776
lib/wp/endpoint.c
Normal file
776
lib/wp/endpoint.c
Normal file
|
|
@ -0,0 +1,776 @@
|
|||
/* WirePlumber
|
||||
*
|
||||
* Copyright © 2019 Collabora Ltd.
|
||||
* @author George Kiagiadakis <george.kiagiadakis@collabora.com>
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include "endpoint.h"
|
||||
#include "private.h"
|
||||
#include "wpenums.h"
|
||||
|
||||
#include <pipewire/pipewire.h>
|
||||
#include <pipewire/extensions/session-manager.h>
|
||||
#include <spa/pod/builder.h>
|
||||
#include <spa/pod/parser.h>
|
||||
|
||||
enum {
|
||||
PROXY_PROP_0,
|
||||
PROXY_PROP_INFO,
|
||||
PROXY_PROP_PROPERTIES,
|
||||
};
|
||||
|
||||
enum {
|
||||
EXPORTED_PROP_0,
|
||||
EXPORTED_PROP_GLOBAL_ID,
|
||||
EXPORTED_PROP_PROPERTIES,
|
||||
};
|
||||
|
||||
enum {
|
||||
SIGNAL_CONTROL_CHANGED,
|
||||
N_SIGNALS,
|
||||
};
|
||||
|
||||
static guint32 signals[N_SIGNALS] = {0};
|
||||
|
||||
/* helpers */
|
||||
|
||||
static struct pw_endpoint_info *
|
||||
endpoint_info_update (struct pw_endpoint_info *info,
|
||||
WpProperties ** props_storage,
|
||||
const struct pw_endpoint_info *update)
|
||||
{
|
||||
if (update == NULL)
|
||||
return info;
|
||||
|
||||
if (info == NULL) {
|
||||
info = calloc(1, sizeof(struct pw_endpoint_info));
|
||||
if (info == NULL)
|
||||
return NULL;
|
||||
|
||||
info->id = update->id;
|
||||
info->name = g_strdup(update->name);
|
||||
info->media_class = g_strdup(update->media_class);
|
||||
info->direction = update->direction;
|
||||
info->flags = update->flags;
|
||||
}
|
||||
info->change_mask = update->change_mask;
|
||||
|
||||
if (update->change_mask & PW_ENDPOINT_CHANGE_MASK_STREAMS)
|
||||
info->n_streams = update->n_streams;
|
||||
|
||||
if (update->change_mask & PW_ENDPOINT_CHANGE_MASK_SESSION)
|
||||
info->session_id = update->session_id;
|
||||
|
||||
if (update->change_mask & PW_ENDPOINT_CHANGE_MASK_PROPS) {
|
||||
if (*props_storage)
|
||||
wp_properties_unref (*props_storage);
|
||||
*props_storage = wp_properties_new_copy_dict (update->props);
|
||||
info->props = (struct spa_dict *) wp_properties_peek_dict (*props_storage);
|
||||
}
|
||||
if (update->change_mask & PW_ENDPOINT_CHANGE_MASK_PARAMS) {
|
||||
info->n_params = update->n_params;
|
||||
free((void *) info->params);
|
||||
if (update->params) {
|
||||
size_t size = info->n_params * sizeof(struct spa_param_info);
|
||||
info->params = malloc(size);
|
||||
memcpy(info->params, update->params, size);
|
||||
}
|
||||
else
|
||||
info->params = NULL;
|
||||
}
|
||||
return info;
|
||||
}
|
||||
|
||||
static void
|
||||
endpoint_info_free (struct pw_endpoint_info *info)
|
||||
{
|
||||
g_free(info->name);
|
||||
g_free(info->media_class);
|
||||
free((void *) info->params);
|
||||
free(info);
|
||||
}
|
||||
|
||||
/* interface */
|
||||
|
||||
G_DEFINE_INTERFACE (WpEndpoint, wp_endpoint, G_TYPE_OBJECT)
|
||||
|
||||
static void
|
||||
wp_endpoint_default_init (WpEndpointInterface * klass)
|
||||
{
|
||||
g_object_interface_install_property (klass,
|
||||
g_param_spec_boxed ("properties", "properties",
|
||||
"The pipewire properties of the object", WP_TYPE_PROPERTIES,
|
||||
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
|
||||
|
||||
signals[SIGNAL_CONTROL_CHANGED] = g_signal_new (
|
||||
"control-changed", G_TYPE_FROM_CLASS (klass),
|
||||
G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 1, G_TYPE_UINT);
|
||||
}
|
||||
|
||||
WpProperties *
|
||||
wp_endpoint_get_properties (WpEndpoint * self)
|
||||
{
|
||||
g_return_val_if_fail (WP_IS_ENDPOINT (self), NULL);
|
||||
g_return_val_if_fail (WP_ENDPOINT_GET_IFACE (self)->get_properties, NULL);
|
||||
|
||||
return WP_ENDPOINT_GET_IFACE (self)->get_properties (self);
|
||||
}
|
||||
|
||||
const gchar *
|
||||
wp_endpoint_get_name (WpEndpoint * self)
|
||||
{
|
||||
g_return_val_if_fail (WP_IS_ENDPOINT (self), NULL);
|
||||
g_return_val_if_fail (WP_ENDPOINT_GET_IFACE (self)->get_name, NULL);
|
||||
|
||||
return WP_ENDPOINT_GET_IFACE (self)->get_name (self);
|
||||
}
|
||||
|
||||
const gchar *
|
||||
wp_endpoint_get_media_class (WpEndpoint * self)
|
||||
{
|
||||
g_return_val_if_fail (WP_IS_ENDPOINT (self), NULL);
|
||||
g_return_val_if_fail (WP_ENDPOINT_GET_IFACE (self)->get_media_class, NULL);
|
||||
|
||||
return WP_ENDPOINT_GET_IFACE (self)->get_media_class (self);
|
||||
}
|
||||
|
||||
WpDirection
|
||||
wp_endpoint_get_direction (WpEndpoint * self)
|
||||
{
|
||||
g_return_val_if_fail (WP_IS_ENDPOINT (self), 0);
|
||||
g_return_val_if_fail (WP_ENDPOINT_GET_IFACE (self)->get_direction, 0);
|
||||
|
||||
return WP_ENDPOINT_GET_IFACE (self)->get_direction (self);
|
||||
}
|
||||
|
||||
const struct spa_pod *
|
||||
wp_endpoint_get_control (WpEndpoint * self, guint32 control_id)
|
||||
{
|
||||
g_return_val_if_fail (WP_IS_ENDPOINT (self), NULL);
|
||||
g_return_val_if_fail (WP_ENDPOINT_GET_IFACE (self)->get_control, NULL);
|
||||
|
||||
return WP_ENDPOINT_GET_IFACE (self)->get_control (self, control_id);
|
||||
}
|
||||
|
||||
gboolean
|
||||
wp_endpoint_get_control_boolean (WpEndpoint * self, guint32 control_id,
|
||||
gboolean * value)
|
||||
{
|
||||
const struct spa_pod *pod = wp_endpoint_get_control (self, control_id);
|
||||
bool val;
|
||||
if (pod && spa_pod_get_bool (pod, &val) == 0) {
|
||||
*value = val;
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
gboolean
|
||||
wp_endpoint_get_control_int (WpEndpoint * self, guint32 control_id,
|
||||
gint * value)
|
||||
{
|
||||
const struct spa_pod *pod = wp_endpoint_get_control (self, control_id);
|
||||
return (pod && spa_pod_get_int (pod, value) == 0);
|
||||
}
|
||||
|
||||
gboolean
|
||||
wp_endpoint_get_control_float (WpEndpoint * self, guint32 control_id,
|
||||
gfloat * value)
|
||||
{
|
||||
const struct spa_pod *pod = wp_endpoint_get_control (self, control_id);
|
||||
return (pod && spa_pod_get_float (pod, value) == 0);
|
||||
}
|
||||
|
||||
gboolean
|
||||
wp_endpoint_set_control (WpEndpoint * self, guint32 control_id,
|
||||
const struct spa_pod * value)
|
||||
{
|
||||
g_return_val_if_fail (WP_IS_ENDPOINT (self), FALSE);
|
||||
g_return_val_if_fail (WP_ENDPOINT_GET_IFACE (self)->get_properties, FALSE);
|
||||
|
||||
return WP_ENDPOINT_GET_IFACE (self)->set_control (self, control_id, value);
|
||||
}
|
||||
|
||||
gboolean
|
||||
wp_endpoint_set_control_boolean (WpEndpoint * self, guint32 control_id,
|
||||
gboolean value)
|
||||
{
|
||||
gchar buffer[512];
|
||||
return wp_endpoint_set_control (self, control_id, wp_spa_props_build_pod (
|
||||
buffer, sizeof (buffer), SPA_POD_Bool (value), 0));
|
||||
}
|
||||
|
||||
gboolean
|
||||
wp_endpoint_set_control_int (WpEndpoint * self, guint32 control_id,
|
||||
gint value)
|
||||
{
|
||||
gchar buffer[512];
|
||||
return wp_endpoint_set_control (self, control_id, wp_spa_props_build_pod (
|
||||
buffer, sizeof (buffer), SPA_POD_Int (value), 0));
|
||||
}
|
||||
|
||||
gboolean
|
||||
wp_endpoint_set_control_float (WpEndpoint * self, guint32 control_id,
|
||||
gfloat value)
|
||||
{
|
||||
gchar buffer[512];
|
||||
return wp_endpoint_set_control (self, control_id, wp_spa_props_build_pod (
|
||||
buffer, sizeof (buffer), SPA_POD_Float (value), 0));
|
||||
}
|
||||
|
||||
/* proxy */
|
||||
|
||||
struct _WpProxyEndpoint
|
||||
{
|
||||
WpProxy parent;
|
||||
|
||||
WpProperties *properties;
|
||||
WpSpaProps spa_props;
|
||||
struct pw_endpoint_info *info;
|
||||
struct spa_hook listener;
|
||||
};
|
||||
|
||||
static void wp_proxy_endpoint_iface_init (WpEndpointInterface * iface);
|
||||
|
||||
G_DEFINE_TYPE_WITH_CODE (WpProxyEndpoint, wp_proxy_endpoint, WP_TYPE_PROXY,
|
||||
G_IMPLEMENT_INTERFACE (WP_TYPE_ENDPOINT, wp_proxy_endpoint_iface_init))
|
||||
|
||||
static void
|
||||
wp_proxy_endpoint_init (WpProxyEndpoint * self)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
wp_proxy_endpoint_finalize (GObject * object)
|
||||
{
|
||||
WpProxyEndpoint *self = WP_PROXY_ENDPOINT (object);
|
||||
|
||||
g_clear_pointer (&self->info, endpoint_info_free);
|
||||
g_clear_pointer (&self->properties, wp_properties_unref);
|
||||
wp_spa_props_clear (&self->spa_props);
|
||||
|
||||
G_OBJECT_CLASS (wp_proxy_endpoint_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
wp_proxy_endpoint_get_property (GObject * object, guint property_id,
|
||||
GValue * value, GParamSpec * pspec)
|
||||
{
|
||||
WpProxyEndpoint *self = WP_PROXY_ENDPOINT (object);
|
||||
|
||||
switch (property_id) {
|
||||
case PROXY_PROP_INFO:
|
||||
g_value_set_pointer (value, self->info);
|
||||
break;
|
||||
case PROXY_PROP_PROPERTIES:
|
||||
g_value_set_boxed (value, self->properties);
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
endpoint_event_info (void *data, const struct pw_endpoint_info *info)
|
||||
{
|
||||
WpProxyEndpoint *self = WP_PROXY_ENDPOINT (data);
|
||||
|
||||
self->info = endpoint_info_update (self->info, &self->properties, info);
|
||||
g_object_notify (G_OBJECT (self), "info");
|
||||
|
||||
if (info->change_mask & PW_ENDPOINT_CHANGE_MASK_PROPS)
|
||||
g_object_notify (G_OBJECT (self), "properties");
|
||||
|
||||
wp_proxy_set_feature_ready (WP_PROXY (self), WP_PROXY_FEATURE_INFO);
|
||||
}
|
||||
|
||||
static void
|
||||
endpoint_event_param (void *data, int seq, uint32_t id, uint32_t index,
|
||||
uint32_t next, const struct spa_pod *param)
|
||||
{
|
||||
WpProxyEndpoint *self = WP_PROXY_ENDPOINT (data);
|
||||
g_autoptr (GArray) changed_ids = NULL;
|
||||
guint32 prop_id;
|
||||
|
||||
switch (id) {
|
||||
case SPA_PARAM_PropInfo:
|
||||
wp_spa_props_register_from_prop_info (&self->spa_props, param);
|
||||
break;
|
||||
case SPA_PARAM_Props:
|
||||
changed_ids = g_array_new (FALSE, FALSE, sizeof (uint32_t));
|
||||
wp_spa_props_store_from_props (&self->spa_props, param, changed_ids);
|
||||
|
||||
for (guint i = 0; i < changed_ids->len; i++) {
|
||||
prop_id = g_array_index (changed_ids, uint32_t, i);
|
||||
g_signal_emit (self, signals[SIGNAL_CONTROL_CHANGED], 0, prop_id);
|
||||
}
|
||||
|
||||
wp_proxy_set_feature_ready (WP_PROXY (self),
|
||||
WP_PROXY_ENDPOINT_FEATURE_CONTROLS);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static const struct pw_endpoint_proxy_events endpoint_events = {
|
||||
PW_VERSION_ENDPOINT_PROXY_EVENTS,
|
||||
.info = endpoint_event_info,
|
||||
.param = endpoint_event_param,
|
||||
};
|
||||
|
||||
static void
|
||||
wp_proxy_endpoint_pw_proxy_created (WpProxy * proxy, struct pw_proxy * pw_proxy)
|
||||
{
|
||||
WpProxyEndpoint *self = WP_PROXY_ENDPOINT (proxy);
|
||||
pw_endpoint_proxy_add_listener ((struct pw_endpoint_proxy *) pw_proxy,
|
||||
&self->listener, &endpoint_events, self);
|
||||
}
|
||||
|
||||
static void
|
||||
wp_proxy_endpoint_augment (WpProxy * proxy, WpProxyFeatures features)
|
||||
{
|
||||
/* call the parent impl first to ensure we have a pw proxy if necessary */
|
||||
WP_PROXY_CLASS (wp_proxy_endpoint_parent_class)->augment (proxy, features);
|
||||
|
||||
if (features & WP_PROXY_ENDPOINT_FEATURE_CONTROLS) {
|
||||
struct pw_endpoint_proxy *pw_proxy = NULL;
|
||||
uint32_t ids[] = { SPA_PARAM_Props };
|
||||
|
||||
pw_proxy = (struct pw_endpoint_proxy *) wp_proxy_get_pw_proxy (proxy);
|
||||
if (!pw_proxy)
|
||||
return;
|
||||
|
||||
pw_endpoint_proxy_enum_params (pw_proxy, 0, SPA_PARAM_PropInfo, 0, -1, NULL);
|
||||
pw_endpoint_proxy_subscribe_params (pw_proxy, ids, SPA_N_ELEMENTS (ids));
|
||||
}
|
||||
}
|
||||
|
||||
static WpProperties *
|
||||
wp_proxy_endpoint_get_properties (WpEndpoint * endpoint)
|
||||
{
|
||||
WpProxyEndpoint *self = WP_PROXY_ENDPOINT (endpoint);
|
||||
return wp_properties_ref (self->properties);
|
||||
}
|
||||
|
||||
static const struct spa_pod *
|
||||
wp_proxy_endpoint_get_control (WpEndpoint * endpoint, guint32 control_id)
|
||||
{
|
||||
WpProxyEndpoint *self = WP_PROXY_ENDPOINT (endpoint);
|
||||
return wp_spa_props_get_stored (&self->spa_props, control_id);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
wp_proxy_endpoint_set_control (WpEndpoint * endpoint, guint32 control_id,
|
||||
const struct spa_pod * pod)
|
||||
{
|
||||
WpProxyEndpoint *self = WP_PROXY_ENDPOINT (endpoint);
|
||||
char buf[1024];
|
||||
struct spa_pod_builder b = SPA_POD_BUILDER_INIT (buf, sizeof (buf));
|
||||
struct pw_endpoint_proxy *pw_proxy = NULL;
|
||||
|
||||
/* set the default endpoint id as a property param on the endpoint;
|
||||
our spa_props will be updated by the param event */
|
||||
|
||||
pw_proxy = (struct pw_endpoint_proxy *) wp_proxy_get_pw_proxy (WP_PROXY (self));
|
||||
pw_endpoint_proxy_set_param (pw_proxy,
|
||||
SPA_PARAM_Props, 0,
|
||||
spa_pod_builder_add_object (&b,
|
||||
SPA_TYPE_OBJECT_Props, SPA_PARAM_Props,
|
||||
control_id, SPA_POD_Pod (pod)));
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
wp_proxy_endpoint_class_init (WpProxyEndpointClass * klass)
|
||||
{
|
||||
GObjectClass *object_class = (GObjectClass *) klass;
|
||||
WpProxyClass *proxy_class = (WpProxyClass *) klass;
|
||||
|
||||
object_class->finalize = wp_proxy_endpoint_finalize;
|
||||
object_class->get_property = wp_proxy_endpoint_get_property;
|
||||
|
||||
proxy_class->pw_proxy_created = wp_proxy_endpoint_pw_proxy_created;
|
||||
proxy_class->augment = wp_proxy_endpoint_augment;
|
||||
|
||||
g_object_class_install_property (object_class, PROXY_PROP_INFO,
|
||||
g_param_spec_pointer ("info", "info", "The native info structure",
|
||||
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
|
||||
g_object_class_override_property (object_class, PROXY_PROP_PROPERTIES,
|
||||
"properties");
|
||||
}
|
||||
|
||||
static void
|
||||
wp_proxy_endpoint_iface_init (WpEndpointInterface * iface)
|
||||
{
|
||||
iface->get_properties = wp_proxy_endpoint_get_properties;
|
||||
iface->get_control = wp_proxy_endpoint_get_control;
|
||||
iface->set_control = wp_proxy_endpoint_set_control;
|
||||
}
|
||||
|
||||
const struct pw_endpoint_info *
|
||||
wp_proxy_endpoint_get_info (WpProxyEndpoint * self)
|
||||
{
|
||||
g_return_val_if_fail (WP_IS_PROXY_ENDPOINT (self), NULL);
|
||||
return self->info;
|
||||
}
|
||||
|
||||
/* exported */
|
||||
|
||||
typedef struct _WpExportedEndpointPrivate WpExportedEndpointPrivate;
|
||||
struct _WpExportedEndpointPrivate
|
||||
{
|
||||
WpProxy *client_ep;
|
||||
struct spa_hook listener;
|
||||
struct spa_hook proxy_listener;
|
||||
struct pw_endpoint_info info;
|
||||
struct spa_param_info param_info[2];
|
||||
WpProperties *properties;
|
||||
WpSpaProps spa_props;
|
||||
};
|
||||
|
||||
static void wp_exported_endpoint_iface_init (WpEndpointInterface * iface);
|
||||
|
||||
G_DEFINE_TYPE_WITH_CODE (WpExportedEndpoint, wp_exported_endpoint, WP_TYPE_EXPORTED,
|
||||
G_IMPLEMENT_INTERFACE (WP_TYPE_ENDPOINT, wp_exported_endpoint_iface_init)
|
||||
G_ADD_PRIVATE (WpExportedEndpoint))
|
||||
|
||||
static void
|
||||
wp_exported_endpoint_init (WpExportedEndpoint * self)
|
||||
{
|
||||
WpExportedEndpointPrivate *priv =
|
||||
wp_exported_endpoint_get_instance_private (WP_EXPORTED_ENDPOINT (self));
|
||||
|
||||
priv->properties = wp_properties_new_empty ();
|
||||
|
||||
priv->param_info[0] = SPA_PARAM_INFO (SPA_PARAM_Props, SPA_PARAM_INFO_READWRITE);
|
||||
priv->param_info[1] = SPA_PARAM_INFO (SPA_PARAM_PropInfo, SPA_PARAM_INFO_READ);
|
||||
|
||||
priv->info.version = PW_VERSION_ENDPOINT_INFO;
|
||||
priv->info.props = (struct spa_dict *) wp_properties_peek_dict (priv->properties);
|
||||
priv->info.params = priv->param_info;
|
||||
priv->info.n_params = SPA_N_ELEMENTS (priv->param_info);
|
||||
}
|
||||
|
||||
static void
|
||||
wp_exported_endpoint_finalize (GObject * object)
|
||||
{
|
||||
WpExportedEndpointPrivate *priv =
|
||||
wp_exported_endpoint_get_instance_private (WP_EXPORTED_ENDPOINT (object));
|
||||
|
||||
g_clear_pointer (&priv->properties, wp_properties_unref);
|
||||
wp_spa_props_clear (&priv->spa_props);
|
||||
|
||||
G_OBJECT_CLASS (wp_exported_endpoint_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
wp_exported_endpoint_get_property (GObject * object, guint property_id,
|
||||
GValue * value, GParamSpec * pspec)
|
||||
{
|
||||
WpExportedEndpointPrivate *priv =
|
||||
wp_exported_endpoint_get_instance_private (WP_EXPORTED_ENDPOINT (object));
|
||||
|
||||
switch (property_id) {
|
||||
case EXPORTED_PROP_GLOBAL_ID:
|
||||
g_value_set_uint (value, priv->info.id);
|
||||
break;
|
||||
case EXPORTED_PROP_PROPERTIES:
|
||||
g_value_set_boxed (value, priv->properties);
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
client_endpoint_update (WpExportedEndpoint * self, guint32 change_mask,
|
||||
guint32 info_change_mask)
|
||||
{
|
||||
WpExportedEndpointPrivate *priv =
|
||||
wp_exported_endpoint_get_instance_private (self);
|
||||
char buf[1024];
|
||||
struct spa_pod_builder b = SPA_POD_BUILDER_INIT (buf, sizeof (buf));
|
||||
struct pw_client_endpoint_proxy *pw_proxy = NULL;
|
||||
struct pw_endpoint_info *info = NULL;
|
||||
g_autoptr (GPtrArray) params = NULL;
|
||||
|
||||
pw_proxy = (struct pw_client_endpoint_proxy *) wp_proxy_get_pw_proxy (
|
||||
priv->client_ep);
|
||||
|
||||
if (change_mask & PW_CLIENT_ENDPOINT_UPDATE_PARAMS) {
|
||||
params = wp_spa_props_build_all_pods (&priv->spa_props, &b);
|
||||
}
|
||||
if (change_mask & PW_CLIENT_ENDPOINT_UPDATE_INFO) {
|
||||
info = &priv->info;
|
||||
info->change_mask = info_change_mask;
|
||||
}
|
||||
|
||||
pw_client_endpoint_proxy_update (pw_proxy,
|
||||
change_mask,
|
||||
params ? params->len : 0,
|
||||
(const struct spa_pod **) (params ? params->pdata : NULL),
|
||||
info);
|
||||
|
||||
if (info)
|
||||
info->change_mask = 0;
|
||||
}
|
||||
|
||||
static int
|
||||
client_endpoint_set_param (void *object,
|
||||
uint32_t id, uint32_t flags, const struct spa_pod *param)
|
||||
{
|
||||
WpExportedEndpoint *self = WP_EXPORTED_ENDPOINT (object);
|
||||
WpExportedEndpointPrivate *priv =
|
||||
wp_exported_endpoint_get_instance_private (self);
|
||||
g_autoptr (GArray) changed_ids = NULL;
|
||||
guint32 prop_id;
|
||||
|
||||
if (id != SPA_PARAM_Props)
|
||||
return -ENOENT;
|
||||
|
||||
changed_ids = g_array_new (FALSE, FALSE, sizeof (guint32));
|
||||
wp_spa_props_store_from_props (&priv->spa_props, param, changed_ids);
|
||||
|
||||
for (guint i = 0; i < changed_ids->len; i++) {
|
||||
prop_id = g_array_index (changed_ids, guint32, i);
|
||||
g_signal_emit (self, signals[SIGNAL_CONTROL_CHANGED], 0, prop_id);
|
||||
}
|
||||
|
||||
client_endpoint_update (self, PW_CLIENT_ENDPOINT_UPDATE_PARAMS, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
client_endpoint_proxy_bound (void *object, uint32_t global_id)
|
||||
{
|
||||
WpExportedEndpoint *self = WP_EXPORTED_ENDPOINT (object);
|
||||
WpExportedEndpointPrivate *priv =
|
||||
wp_exported_endpoint_get_instance_private (self);
|
||||
|
||||
priv->info.id = global_id;
|
||||
wp_exported_notify_export_done (WP_EXPORTED (self), NULL);
|
||||
}
|
||||
|
||||
static struct pw_client_endpoint_proxy_events client_endpoint_events = {
|
||||
PW_VERSION_CLIENT_ENDPOINT_PROXY_EVENTS,
|
||||
.set_param = client_endpoint_set_param,
|
||||
};
|
||||
|
||||
static struct pw_proxy_events client_ep_proxy_events = {
|
||||
PW_VERSION_PROXY_EVENTS,
|
||||
.bound = client_endpoint_proxy_bound,
|
||||
};
|
||||
|
||||
static void
|
||||
wp_exported_endpoint_export (WpExported * self)
|
||||
{
|
||||
WpExportedEndpointPrivate *priv =
|
||||
wp_exported_endpoint_get_instance_private (WP_EXPORTED_ENDPOINT (self));
|
||||
g_autoptr (WpCore) core = wp_exported_get_core (self);
|
||||
struct pw_client_endpoint_proxy *pw_proxy = NULL;
|
||||
|
||||
priv->client_ep = wp_core_create_remote_object (core, "client-endpoint",
|
||||
PW_TYPE_INTERFACE_ClientEndpoint, PW_VERSION_CLIENT_ENDPOINT_PROXY,
|
||||
priv->properties);
|
||||
|
||||
pw_proxy = (struct pw_client_endpoint_proxy *) wp_proxy_get_pw_proxy (
|
||||
priv->client_ep);
|
||||
|
||||
pw_client_endpoint_proxy_add_listener (pw_proxy, &priv->listener,
|
||||
&client_endpoint_events, self);
|
||||
pw_proxy_add_listener ((struct pw_proxy *) pw_proxy, &priv->proxy_listener,
|
||||
&client_ep_proxy_events, self);
|
||||
|
||||
client_endpoint_update (WP_EXPORTED_ENDPOINT (self),
|
||||
PW_CLIENT_ENDPOINT_UPDATE_PARAMS | PW_CLIENT_ENDPOINT_UPDATE_INFO,
|
||||
PW_ENDPOINT_CHANGE_MASK_ALL);
|
||||
}
|
||||
|
||||
static void
|
||||
wp_exported_endpoint_unexport (WpExported * self)
|
||||
{
|
||||
WpExportedEndpointPrivate *priv =
|
||||
wp_exported_endpoint_get_instance_private (WP_EXPORTED_ENDPOINT (self));
|
||||
|
||||
g_clear_object (&priv->client_ep);
|
||||
priv->info.id = 0;
|
||||
}
|
||||
|
||||
static WpProxy *
|
||||
wp_exported_endpoint_get_proxy (WpExported * self)
|
||||
{
|
||||
WpExportedEndpointPrivate *priv =
|
||||
wp_exported_endpoint_get_instance_private (WP_EXPORTED_ENDPOINT (self));
|
||||
|
||||
return priv->client_ep ? g_object_ref (priv->client_ep) : NULL;
|
||||
}
|
||||
|
||||
static WpProperties *
|
||||
wp_exported_endpoint_get_properties (WpEndpoint * endpoint)
|
||||
{
|
||||
WpExportedEndpointPrivate *priv =
|
||||
wp_exported_endpoint_get_instance_private (WP_EXPORTED_ENDPOINT (endpoint));
|
||||
|
||||
return wp_properties_ref (priv->properties);
|
||||
}
|
||||
|
||||
static const struct spa_pod *
|
||||
wp_exported_endpoint_get_control (WpEndpoint * endpoint, guint32 control_id)
|
||||
{
|
||||
WpExportedEndpointPrivate *priv =
|
||||
wp_exported_endpoint_get_instance_private (WP_EXPORTED_ENDPOINT (endpoint));
|
||||
|
||||
return wp_spa_props_get_stored (&priv->spa_props, control_id);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
wp_exported_endpoint_set_control (WpEndpoint * endpoint, guint32 control_id,
|
||||
const struct spa_pod * pod)
|
||||
{
|
||||
WpExportedEndpointPrivate *priv =
|
||||
wp_exported_endpoint_get_instance_private (WP_EXPORTED_ENDPOINT (endpoint));
|
||||
|
||||
if (wp_spa_props_store_pod (&priv->spa_props, control_id, pod) < 0)
|
||||
return FALSE;
|
||||
|
||||
g_signal_emit (endpoint, signals[SIGNAL_CONTROL_CHANGED], 0, control_id);
|
||||
|
||||
/* update only after the endpoint has been exported */
|
||||
if (priv->info.id != 0) {
|
||||
client_endpoint_update (WP_EXPORTED_ENDPOINT (endpoint),
|
||||
PW_CLIENT_ENDPOINT_UPDATE_PARAMS, 0);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
wp_exported_endpoint_class_init (WpExportedEndpointClass * klass)
|
||||
{
|
||||
GObjectClass *object_class = (GObjectClass *) klass;
|
||||
WpExportedClass *exported_class = (WpExportedClass *) klass;
|
||||
|
||||
object_class->finalize = wp_exported_endpoint_finalize;
|
||||
object_class->get_property = wp_exported_endpoint_get_property;
|
||||
|
||||
exported_class->export = wp_exported_endpoint_export;
|
||||
exported_class->unexport = wp_exported_endpoint_unexport;
|
||||
exported_class->get_proxy = wp_exported_endpoint_get_proxy;
|
||||
|
||||
g_object_class_install_property (object_class, EXPORTED_PROP_GLOBAL_ID,
|
||||
g_param_spec_uint ("global-id", "global-id",
|
||||
"The pipewire global id of the exported endpoint", 0, G_MAXUINT, 0,
|
||||
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
|
||||
g_object_class_override_property (object_class, EXPORTED_PROP_PROPERTIES,
|
||||
"properties");
|
||||
}
|
||||
|
||||
static void
|
||||
wp_exported_endpoint_iface_init (WpEndpointInterface * iface)
|
||||
{
|
||||
iface->get_properties = wp_exported_endpoint_get_properties;
|
||||
iface->get_control = wp_exported_endpoint_get_control;
|
||||
iface->set_control = wp_exported_endpoint_set_control;
|
||||
}
|
||||
|
||||
WpExportedEndpoint *
|
||||
wp_exported_endpoint_new (WpCore * core)
|
||||
{
|
||||
g_return_val_if_fail (WP_IS_CORE (core), NULL);
|
||||
|
||||
return g_object_new (WP_TYPE_EXPORTED_ENDPOINT,
|
||||
"core", core,
|
||||
NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* wp_exported_endpoint_get_global_id: (method)
|
||||
* @self: the endpoint
|
||||
*
|
||||
* Returns: the pipewire global id of the exported endpoint object. This
|
||||
* is only valid after the wp_exported_export() async operation has finished.
|
||||
*/
|
||||
guint32
|
||||
wp_exported_endpoint_get_global_id (WpExportedEndpoint * self)
|
||||
{
|
||||
WpExportedEndpointPrivate *priv;
|
||||
|
||||
g_return_val_if_fail (WP_IS_EXPORTED_ENDPOINT (self), 0);
|
||||
priv = wp_exported_endpoint_get_instance_private (WP_EXPORTED_ENDPOINT (self));
|
||||
|
||||
return priv->info.id;
|
||||
}
|
||||
|
||||
void
|
||||
wp_exported_endpoint_set_property (WpExportedEndpoint * self,
|
||||
const gchar * key, const gchar * value)
|
||||
{
|
||||
WpExportedEndpointPrivate *priv;
|
||||
|
||||
g_return_if_fail (WP_IS_EXPORTED_ENDPOINT (self));
|
||||
priv = wp_exported_endpoint_get_instance_private (WP_EXPORTED_ENDPOINT (self));
|
||||
|
||||
wp_properties_set (priv->properties, key, value);
|
||||
|
||||
g_object_notify (G_OBJECT (self), "properties");
|
||||
|
||||
/* update only after the endpoint has been exported */
|
||||
if (priv->info.id != 0) {
|
||||
client_endpoint_update (WP_EXPORTED_ENDPOINT (self),
|
||||
PW_CLIENT_ENDPOINT_UPDATE_INFO, PW_ENDPOINT_CHANGE_MASK_PROPS);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
wp_exported_endpoint_update_properties (WpExportedEndpoint * self,
|
||||
WpProperties * updates)
|
||||
{
|
||||
WpExportedEndpointPrivate *priv;
|
||||
|
||||
g_return_if_fail (WP_IS_EXPORTED_ENDPOINT (self));
|
||||
priv = wp_exported_endpoint_get_instance_private (WP_EXPORTED_ENDPOINT (self));
|
||||
|
||||
wp_properties_update_from_dict (priv->properties,
|
||||
wp_properties_peek_dict (updates));
|
||||
|
||||
g_object_notify (G_OBJECT (self), "properties");
|
||||
|
||||
/* update only after the endpoint has been exported */
|
||||
if (priv->info.id != 0) {
|
||||
client_endpoint_update (WP_EXPORTED_ENDPOINT (self),
|
||||
PW_CLIENT_ENDPOINT_UPDATE_INFO, PW_ENDPOINT_CHANGE_MASK_PROPS);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
wp_exported_endpoint_register_control (WpExportedEndpoint * self,
|
||||
WpEndpointControl control)
|
||||
{
|
||||
WpExportedEndpointPrivate *priv;
|
||||
|
||||
g_return_if_fail (WP_IS_EXPORTED_ENDPOINT (self));
|
||||
priv = wp_exported_endpoint_get_instance_private (WP_EXPORTED_ENDPOINT (self));
|
||||
|
||||
switch (control) {
|
||||
case WP_ENDPOINT_CONTROL_VOLUME:
|
||||
wp_spa_props_register (&priv->spa_props, control,
|
||||
"Volume", SPA_POD_CHOICE_RANGE_Float (1.0, 0.0, 10.0));
|
||||
break;
|
||||
case WP_ENDPOINT_CONTROL_MUTE:
|
||||
wp_spa_props_register (&priv->spa_props, control,
|
||||
"Mute", SPA_POD_CHOICE_Bool (false));
|
||||
break;
|
||||
case WP_ENDPOINT_CONTROL_CHANNEL_VOLUMES:
|
||||
wp_spa_props_register (&priv->spa_props, control,
|
||||
"Channel Volumes", SPA_POD_CHOICE_RANGE_Float (1.0, 0.0, 10.0));
|
||||
break;
|
||||
default:
|
||||
g_warning ("Unknown endpoint control: 0x%x", control);
|
||||
break;
|
||||
}
|
||||
}
|
||||
125
lib/wp/endpoint.h
Normal file
125
lib/wp/endpoint.h
Normal file
|
|
@ -0,0 +1,125 @@
|
|||
/* WirePlumber
|
||||
*
|
||||
* Copyright © 2019 Collabora Ltd.
|
||||
* @author George Kiagiadakis <george.kiagiadakis@collabora.com>
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#ifndef __WIREPLUMBER_ENDPOINT_H__
|
||||
#define __WIREPLUMBER_ENDPOINT_H__
|
||||
|
||||
#include "exported.h"
|
||||
#include "proxy.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define WP_TYPE_ENDPOINT (wp_endpoint_get_type ())
|
||||
G_DECLARE_INTERFACE (WpEndpoint, wp_endpoint, WP, ENDPOINT, GObject)
|
||||
|
||||
/**
|
||||
* WpDirection:
|
||||
* @WP_DIRECTION_INPUT: a sink, consuming input
|
||||
* @WP_DIRECTION_OUTPUT: a source, producing output
|
||||
*
|
||||
* The different directions the endpoint can have
|
||||
*/
|
||||
typedef enum {
|
||||
WP_DIRECTION_INPUT,
|
||||
WP_DIRECTION_OUTPUT,
|
||||
} WpDirection;
|
||||
|
||||
typedef enum {
|
||||
WP_ENDPOINT_CONTROL_VOLUME = 0x10003 /* SPA_PROP_volume */,
|
||||
WP_ENDPOINT_CONTROL_MUTE = 0x10004 /* SPA_PROP_mute */,
|
||||
WP_ENDPOINT_CONTROL_CHANNEL_VOLUMES = 0x10008 /* SPA_PROP_channelVolumes */,
|
||||
} WpEndpointControl;
|
||||
|
||||
struct _WpEndpointInterface
|
||||
{
|
||||
GTypeInterface parent;
|
||||
|
||||
WpProperties * (*get_properties) (WpEndpoint * self);
|
||||
|
||||
const gchar * (*get_name) (WpEndpoint * self);
|
||||
const gchar * (*get_media_class) (WpEndpoint * self);
|
||||
WpDirection (*get_direction) (WpEndpoint * self);
|
||||
|
||||
const struct spa_pod * (*get_control) (WpEndpoint * self, guint32 control_id);
|
||||
gboolean (*set_control) (WpEndpoint * self, guint32 control_id,
|
||||
const struct spa_pod * value);
|
||||
|
||||
// void (*create_link) (WpEndpoint * self, WpProperties * props);
|
||||
};
|
||||
|
||||
WpProperties * wp_endpoint_get_properties (WpEndpoint * self);
|
||||
|
||||
const gchar * wp_endpoint_get_name (WpEndpoint * self);
|
||||
const gchar * wp_endpoint_get_media_class (WpEndpoint * self);
|
||||
WpDirection wp_endpoint_get_direction (WpEndpoint * self);
|
||||
|
||||
const struct spa_pod * wp_endpoint_get_control (WpEndpoint * self,
|
||||
guint32 control_id);
|
||||
gboolean wp_endpoint_get_control_boolean (WpEndpoint * self, guint32 control_id,
|
||||
gboolean * value);
|
||||
gboolean wp_endpoint_get_control_int (WpEndpoint * self, guint32 control_id,
|
||||
gint * value);
|
||||
gboolean wp_endpoint_get_control_float (WpEndpoint * self, guint32 control_id,
|
||||
gfloat * value);
|
||||
|
||||
gboolean wp_endpoint_set_control (WpEndpoint * self, guint32 control_id,
|
||||
const struct spa_pod * value);
|
||||
gboolean wp_endpoint_set_control_boolean (WpEndpoint * self, guint32 control_id,
|
||||
gboolean value);
|
||||
gboolean wp_endpoint_set_control_int (WpEndpoint * self, guint32 control_id,
|
||||
gint value);
|
||||
gboolean wp_endpoint_set_control_float (WpEndpoint * self, guint32 control_id,
|
||||
gfloat value);
|
||||
|
||||
// void wp_endpoint_create_link (WpEndpoint * self, WpProperties * props);
|
||||
|
||||
/* proxy */
|
||||
|
||||
typedef enum { /*< flags >*/
|
||||
WP_PROXY_ENDPOINT_FEATURE_CONTROLS = WP_PROXY_FEATURE_LAST,
|
||||
} WpProxyEndpointFeatures;
|
||||
|
||||
#define WP_TYPE_PROXY_ENDPOINT (wp_proxy_endpoint_get_type ())
|
||||
G_DECLARE_FINAL_TYPE (WpProxyEndpoint, wp_proxy_endpoint,
|
||||
WP, PROXY_ENDPOINT, WpProxy)
|
||||
|
||||
const struct pw_endpoint_info * wp_proxy_endpoint_get_info (
|
||||
WpProxyEndpoint * self);
|
||||
|
||||
/* exported */
|
||||
|
||||
#define WP_TYPE_EXPORTED_ENDPOINT (wp_exported_endpoint_get_type ())
|
||||
G_DECLARE_DERIVABLE_TYPE (WpExportedEndpoint, wp_exported_endpoint,
|
||||
WP, EXPORTED_ENDPOINT, WpExported)
|
||||
|
||||
struct _WpExportedEndpointClass
|
||||
{
|
||||
WpExportedClass parent_class;
|
||||
};
|
||||
|
||||
WpExportedEndpoint * wp_exported_endpoint_new (WpCore * core);
|
||||
|
||||
guint32 wp_exported_endpoint_get_global_id (WpExportedEndpoint * self);
|
||||
|
||||
void wp_exported_endpoint_set_property (WpExportedEndpoint * self,
|
||||
const gchar * key, const gchar * value);
|
||||
void wp_exported_endpoint_update_properties (WpExportedEndpoint * self,
|
||||
WpProperties * updates);
|
||||
|
||||
void wp_exported_endpoint_register_control (WpExportedEndpoint * self,
|
||||
WpEndpointControl control);
|
||||
|
||||
// void wp_exported_endpoint_register_stream (WpExportedEndpoint * self,
|
||||
// WpExportedEndpointStream * stream);
|
||||
// void wp_exported_endpoint_remove_stream (WpExportedEndpoint * self,
|
||||
// WpExportedEndpointStream * stream);
|
||||
// GPtrArray * wp_exported_endpoint_list_streams (WpExportedEndpoint * self);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif
|
||||
|
|
@ -2,6 +2,7 @@ wp_lib_sources = [
|
|||
'base-endpoint.c',
|
||||
'configuration.c',
|
||||
'core.c',
|
||||
'endpoint.c',
|
||||
'error.c',
|
||||
'exported.c',
|
||||
'factory.c',
|
||||
|
|
@ -22,6 +23,7 @@ wp_lib_sources = [
|
|||
wp_lib_headers = [
|
||||
'base-endpoint.h',
|
||||
'configuration.h',
|
||||
'endpoint.h',
|
||||
'core.h',
|
||||
'error.h',
|
||||
'exported.h',
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@
|
|||
#include "wpenums.h"
|
||||
#include "private.h"
|
||||
|
||||
#include "endpoint.h"
|
||||
#include "proxy-client.h"
|
||||
#include "proxy-link.h"
|
||||
#include "proxy-node.h"
|
||||
|
|
@ -111,7 +112,7 @@ static struct {
|
|||
{ PW_TYPE_INTERFACE_Device, 0, wp_proxy_get_type, wp_proxy_device_quark },
|
||||
{ PW_TYPE_INTERFACE_Metadata, 0, wp_proxy_get_type, wp_proxy_metadata_quark },
|
||||
{ PW_TYPE_INTERFACE_Session, 0, wp_proxy_session_get_type, wp_proxy_session_quark },
|
||||
{ PW_TYPE_INTERFACE_Endpoint, 0, wp_proxy_get_type, wp_proxy_endpoint_quark },
|
||||
{ PW_TYPE_INTERFACE_Endpoint, 0, wp_proxy_endpoint_get_type, wp_proxy_endpoint_quark },
|
||||
{ PW_TYPE_INTERFACE_EndpointStream, 0, wp_proxy_get_type, wp_proxy_endpoint_stream_quark },
|
||||
{ PW_TYPE_INTERFACE_EndpointLink, 0, wp_proxy_get_type, wp_proxy_endpoint_link_quark },
|
||||
{ PW_TYPE_INTERFACE_ClientNode, 0, wp_proxy_get_type, wp_proxy_client_node_quark },
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@
|
|||
#include "base-endpoint.h"
|
||||
#include "configuration.h"
|
||||
#include "core.h"
|
||||
#include "endpoint.h"
|
||||
#include "error.h"
|
||||
#include "exported.h"
|
||||
#include "factory.h"
|
||||
|
|
|
|||
437
tests/wp/endpoint.c
Normal file
437
tests/wp/endpoint.c
Normal file
|
|
@ -0,0 +1,437 @@
|
|||
/* WirePlumber
|
||||
*
|
||||
* Copyright © 2019 Collabora Ltd.
|
||||
* @author George Kiagiadakis <george.kiagiadakis@collabora.com>
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include <wp/wp.h>
|
||||
#include <pipewire/pipewire.h>
|
||||
|
||||
#include "test-server.h"
|
||||
|
||||
typedef struct {
|
||||
/* the local pipewire server */
|
||||
WpTestServer server;
|
||||
|
||||
/* the main loop */
|
||||
GMainContext *context;
|
||||
GMainLoop *loop;
|
||||
GSource *timeout_source;
|
||||
|
||||
/* the client that exports */
|
||||
WpCore *export_core;
|
||||
WpObjectManager *export_om;
|
||||
|
||||
/* the client that receives a proxy */
|
||||
WpCore *proxy_core;
|
||||
WpObjectManager *proxy_om;
|
||||
|
||||
WpExportedEndpoint *exported_endpoint;
|
||||
WpProxy *proxy_endpoint;
|
||||
|
||||
gint n_events;
|
||||
|
||||
} TestEndpointFixture;
|
||||
|
||||
static gboolean
|
||||
timeout_callback (TestEndpointFixture *fixture)
|
||||
{
|
||||
g_message ("test timed out");
|
||||
g_test_fail ();
|
||||
g_main_loop_quit (fixture->loop);
|
||||
|
||||
return G_SOURCE_REMOVE;
|
||||
}
|
||||
|
||||
static void
|
||||
test_endpoint_remote_state_changed (WpCore *core, WpRemoteState state,
|
||||
TestEndpointFixture *fixture)
|
||||
{
|
||||
const gchar * msg = NULL;
|
||||
|
||||
switch (state) {
|
||||
case WP_REMOTE_STATE_ERROR:
|
||||
wp_core_get_remote_state (core, &msg);
|
||||
g_message ("remote error: %s", msg);
|
||||
g_test_fail ();
|
||||
g_main_loop_quit (fixture->loop);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
test_endpoint_setup (TestEndpointFixture *self, gconstpointer user_data)
|
||||
{
|
||||
g_autoptr (WpProperties) props = NULL;
|
||||
|
||||
wp_test_server_setup (&self->server);
|
||||
pw_thread_loop_lock (self->server.thread_loop);
|
||||
if (!pw_module_load (self->server.core, "libpipewire-module-session-manager",
|
||||
NULL, NULL)) {
|
||||
pw_thread_loop_unlock (self->server.thread_loop);
|
||||
g_test_skip ("libpipewire-module-session-manager is not installed");
|
||||
return;
|
||||
}
|
||||
pw_thread_loop_unlock (self->server.thread_loop);
|
||||
|
||||
props = wp_properties_new (PW_KEY_REMOTE_NAME, self->server.name, NULL);
|
||||
self->context = g_main_context_new ();
|
||||
self->loop = g_main_loop_new (self->context, FALSE);
|
||||
|
||||
self->export_core = wp_core_new (self->context, props);
|
||||
self->export_om = wp_object_manager_new ();
|
||||
|
||||
self->proxy_core = wp_core_new (self->context, props);
|
||||
self->proxy_om = wp_object_manager_new ();
|
||||
|
||||
g_main_context_push_thread_default (self->context);
|
||||
|
||||
/* watchdogs */
|
||||
g_signal_connect (self->export_core, "remote-state-changed",
|
||||
(GCallback) test_endpoint_remote_state_changed, self);
|
||||
g_signal_connect (self->proxy_core, "remote-state-changed",
|
||||
(GCallback) test_endpoint_remote_state_changed, self);
|
||||
|
||||
self->timeout_source = g_timeout_source_new_seconds (3);
|
||||
g_source_set_callback (self->timeout_source, (GSourceFunc) timeout_callback,
|
||||
self, NULL);
|
||||
g_source_attach (self->timeout_source, self->context);
|
||||
}
|
||||
|
||||
static void
|
||||
test_endpoint_teardown (TestEndpointFixture *self, gconstpointer user_data)
|
||||
{
|
||||
g_main_context_pop_thread_default (self->context);
|
||||
|
||||
g_clear_object (&self->proxy_om);
|
||||
g_clear_object (&self->proxy_core);
|
||||
g_clear_object (&self->export_om);
|
||||
g_clear_object (&self->export_core);
|
||||
g_clear_pointer (&self->timeout_source, g_source_unref);
|
||||
g_clear_pointer (&self->loop, g_main_loop_unref);
|
||||
g_clear_pointer (&self->context, g_main_context_unref);
|
||||
wp_test_server_teardown (&self->server);
|
||||
}
|
||||
|
||||
static void
|
||||
test_endpoint_basic_exported_object_added (WpObjectManager *om,
|
||||
WpEndpoint *endpoint, TestEndpointFixture *fixture)
|
||||
{
|
||||
g_debug ("exported object added");
|
||||
|
||||
g_assert_true (WP_IS_ENDPOINT (endpoint));
|
||||
g_assert_true (WP_IS_EXPORTED_ENDPOINT (endpoint));
|
||||
|
||||
g_assert_null (fixture->exported_endpoint);
|
||||
fixture->exported_endpoint = WP_EXPORTED_ENDPOINT (endpoint);
|
||||
|
||||
if (++fixture->n_events == 3)
|
||||
g_main_loop_quit (fixture->loop);
|
||||
}
|
||||
|
||||
static void
|
||||
test_endpoint_basic_exported_object_removed (WpObjectManager *om,
|
||||
WpEndpoint *endpoint, TestEndpointFixture *fixture)
|
||||
{
|
||||
g_debug ("exported object removed");
|
||||
|
||||
g_assert_true (WP_IS_ENDPOINT (endpoint));
|
||||
g_assert_true (WP_IS_EXPORTED_ENDPOINT (endpoint));
|
||||
|
||||
g_assert_nonnull (fixture->exported_endpoint);
|
||||
fixture->exported_endpoint = NULL;
|
||||
|
||||
if (++fixture->n_events == 2)
|
||||
g_main_loop_quit (fixture->loop);
|
||||
}
|
||||
|
||||
static void
|
||||
test_endpoint_basic_proxy_object_added (WpObjectManager *om,
|
||||
WpEndpoint *endpoint, TestEndpointFixture *fixture)
|
||||
{
|
||||
g_debug ("proxy object added");
|
||||
|
||||
g_assert_true (WP_IS_ENDPOINT (endpoint));
|
||||
g_assert_true (WP_IS_PROXY_ENDPOINT (endpoint));
|
||||
|
||||
g_assert_null (fixture->proxy_endpoint);
|
||||
fixture->proxy_endpoint = WP_PROXY (endpoint);
|
||||
|
||||
if (++fixture->n_events == 3)
|
||||
g_main_loop_quit (fixture->loop);
|
||||
}
|
||||
|
||||
static void
|
||||
test_endpoint_basic_proxy_object_removed (WpObjectManager *om,
|
||||
WpEndpoint *endpoint, TestEndpointFixture *fixture)
|
||||
{
|
||||
g_debug ("proxy object removed");
|
||||
|
||||
g_assert_true (WP_IS_ENDPOINT (endpoint));
|
||||
g_assert_true (WP_IS_PROXY_ENDPOINT (endpoint));
|
||||
|
||||
g_assert_nonnull (fixture->proxy_endpoint);
|
||||
fixture->proxy_endpoint = NULL;
|
||||
|
||||
if (++fixture->n_events == 2)
|
||||
g_main_loop_quit (fixture->loop);
|
||||
}
|
||||
|
||||
static void
|
||||
test_endpoint_basic_export_done (WpExported * endpoint, GAsyncResult * res,
|
||||
TestEndpointFixture *fixture)
|
||||
{
|
||||
g_autoptr (GError) error = NULL;
|
||||
|
||||
g_debug ("export done");
|
||||
|
||||
g_assert_true (wp_exported_export_finish (endpoint, res, &error));
|
||||
g_assert_no_error (error);
|
||||
|
||||
g_assert_true (WP_IS_EXPORTED_ENDPOINT (endpoint));
|
||||
|
||||
if (++fixture->n_events == 3)
|
||||
g_main_loop_quit (fixture->loop);
|
||||
}
|
||||
|
||||
static void
|
||||
test_endpoint_basic_control_changed (WpEndpoint * endpoint,
|
||||
guint32 control_id, TestEndpointFixture *fixture)
|
||||
{
|
||||
g_debug ("endpoint changed: %s (0x%x)", G_OBJECT_TYPE_NAME (endpoint),
|
||||
control_id);
|
||||
|
||||
g_assert_true (WP_IS_ENDPOINT (endpoint));
|
||||
|
||||
if (++fixture->n_events == 2)
|
||||
g_main_loop_quit (fixture->loop);
|
||||
}
|
||||
|
||||
static void
|
||||
test_endpoint_basic_notify_properties (WpEndpoint * endpoint, GParamSpec * param,
|
||||
TestEndpointFixture *fixture)
|
||||
{
|
||||
g_debug ("properties changed: %s", G_OBJECT_TYPE_NAME (endpoint));
|
||||
|
||||
g_assert_true (WP_IS_ENDPOINT (endpoint));
|
||||
|
||||
if (++fixture->n_events == 2)
|
||||
g_main_loop_quit (fixture->loop);
|
||||
}
|
||||
|
||||
static void
|
||||
test_endpoint_basic (TestEndpointFixture *fixture, gconstpointer data)
|
||||
{
|
||||
WpRemoteState state;
|
||||
g_autoptr (WpExportedEndpoint) endpoint = NULL;
|
||||
gfloat float_value;
|
||||
gboolean boolean_value;
|
||||
|
||||
/* set up the export side */
|
||||
g_signal_connect (fixture->export_om, "object-added",
|
||||
(GCallback) test_endpoint_basic_exported_object_added, fixture);
|
||||
g_signal_connect (fixture->export_om, "object-removed",
|
||||
(GCallback) test_endpoint_basic_exported_object_removed, fixture);
|
||||
wp_object_manager_add_object_interest (fixture->export_om,
|
||||
WP_TYPE_EXPORTED_ENDPOINT, NULL);
|
||||
wp_core_install_object_manager (fixture->export_core, fixture->export_om);
|
||||
|
||||
g_assert_true (wp_core_connect (fixture->export_core));
|
||||
do {
|
||||
g_main_context_iteration (fixture->context, FALSE);
|
||||
state = wp_core_get_remote_state (fixture->export_core, NULL);
|
||||
g_assert_cmpint (state, !=, WP_REMOTE_STATE_ERROR);
|
||||
} while (state != WP_REMOTE_STATE_CONNECTED);
|
||||
|
||||
/* set up the proxy side */
|
||||
g_signal_connect (fixture->proxy_om, "object-added",
|
||||
(GCallback) test_endpoint_basic_proxy_object_added, fixture);
|
||||
g_signal_connect (fixture->proxy_om, "object-removed",
|
||||
(GCallback) test_endpoint_basic_proxy_object_removed, fixture);
|
||||
wp_object_manager_add_proxy_interest (fixture->proxy_om,
|
||||
PW_TYPE_INTERFACE_Endpoint, NULL,
|
||||
WP_PROXY_FEATURE_INFO | WP_PROXY_ENDPOINT_FEATURE_CONTROLS);
|
||||
wp_core_install_object_manager (fixture->proxy_core, fixture->proxy_om);
|
||||
|
||||
g_assert_true (wp_core_connect (fixture->proxy_core));
|
||||
do {
|
||||
g_main_context_iteration (fixture->context, FALSE);
|
||||
state = wp_core_get_remote_state (fixture->proxy_core, NULL);
|
||||
g_assert_cmpint (state, !=, WP_REMOTE_STATE_ERROR);
|
||||
} while (state != WP_REMOTE_STATE_CONNECTED);
|
||||
|
||||
/* create endpoint */
|
||||
endpoint = wp_exported_endpoint_new (fixture->export_core);
|
||||
wp_exported_endpoint_set_property (endpoint, "test.property", "test-value");
|
||||
wp_exported_endpoint_register_control (endpoint, WP_ENDPOINT_CONTROL_VOLUME);
|
||||
wp_exported_endpoint_register_control (endpoint, WP_ENDPOINT_CONTROL_MUTE);
|
||||
g_assert_true (wp_endpoint_set_control_float (WP_ENDPOINT (endpoint),
|
||||
WP_ENDPOINT_CONTROL_VOLUME, 0.7f));
|
||||
g_assert_true (wp_endpoint_set_control_boolean (WP_ENDPOINT (endpoint),
|
||||
WP_ENDPOINT_CONTROL_MUTE, TRUE));
|
||||
|
||||
/* verify properties are set before export */
|
||||
{
|
||||
g_autoptr (WpProperties) props =
|
||||
wp_endpoint_get_properties (WP_ENDPOINT (endpoint));
|
||||
g_assert_cmpstr (wp_properties_get (props, "test.property"), ==,
|
||||
"test-value");
|
||||
}
|
||||
g_assert_true (wp_endpoint_get_control_float (WP_ENDPOINT (endpoint),
|
||||
WP_ENDPOINT_CONTROL_VOLUME, &float_value));
|
||||
g_assert_true (wp_endpoint_get_control_boolean (WP_ENDPOINT (endpoint),
|
||||
WP_ENDPOINT_CONTROL_MUTE, &boolean_value));
|
||||
g_assert_cmpfloat_with_epsilon (float_value, 0.7f, 0.001);
|
||||
g_assert_cmpint (boolean_value, ==, TRUE);
|
||||
|
||||
/* do export */
|
||||
wp_exported_export (WP_EXPORTED (endpoint), NULL,
|
||||
(GAsyncReadyCallback) test_endpoint_basic_export_done, fixture);
|
||||
|
||||
/* run until objects are created and features are cached */
|
||||
fixture->n_events = 0;
|
||||
g_main_loop_run (fixture->loop);
|
||||
g_assert_cmpint (fixture->n_events, ==, 3);
|
||||
g_assert_nonnull (fixture->exported_endpoint);
|
||||
g_assert_nonnull (fixture->proxy_endpoint);
|
||||
g_assert_true (fixture->exported_endpoint == endpoint);
|
||||
|
||||
/* test round 1: verify the values on the proxy */
|
||||
|
||||
g_assert_cmphex (wp_proxy_get_features (fixture->proxy_endpoint), ==,
|
||||
WP_PROXY_FEATURE_PW_PROXY |
|
||||
WP_PROXY_FEATURE_INFO |
|
||||
WP_PROXY_ENDPOINT_FEATURE_CONTROLS);
|
||||
|
||||
g_assert_cmpuint (wp_proxy_get_global_id (fixture->proxy_endpoint), ==,
|
||||
wp_exported_endpoint_get_global_id (endpoint));
|
||||
|
||||
{
|
||||
g_autoptr (WpProperties) props =
|
||||
wp_endpoint_get_properties (WP_ENDPOINT (fixture->proxy_endpoint));
|
||||
g_assert_cmpstr (wp_properties_get (props, "test.property"), ==,
|
||||
"test-value");
|
||||
}
|
||||
g_assert_true (wp_endpoint_get_control_float (
|
||||
WP_ENDPOINT (fixture->proxy_endpoint),
|
||||
WP_ENDPOINT_CONTROL_VOLUME, &float_value));
|
||||
g_assert_true (wp_endpoint_get_control_boolean (
|
||||
WP_ENDPOINT (fixture->proxy_endpoint),
|
||||
WP_ENDPOINT_CONTROL_MUTE, &boolean_value));
|
||||
g_assert_cmpfloat_with_epsilon (float_value, 0.7f, 0.001);
|
||||
g_assert_cmpint (boolean_value, ==, TRUE);
|
||||
|
||||
/* setup change signals */
|
||||
g_signal_connect (fixture->proxy_endpoint, "control-changed",
|
||||
(GCallback) test_endpoint_basic_control_changed, fixture);
|
||||
g_signal_connect (endpoint, "control-changed",
|
||||
(GCallback) test_endpoint_basic_control_changed, fixture);
|
||||
g_signal_connect (fixture->proxy_endpoint, "notify::properties",
|
||||
(GCallback) test_endpoint_basic_notify_properties, fixture);
|
||||
g_signal_connect (endpoint, "notify::properties",
|
||||
(GCallback) test_endpoint_basic_notify_properties, fixture);
|
||||
|
||||
/* change control on the proxy */
|
||||
g_assert_true (wp_endpoint_set_control_float (
|
||||
WP_ENDPOINT (fixture->proxy_endpoint),
|
||||
WP_ENDPOINT_CONTROL_VOLUME, 1.0f));
|
||||
|
||||
/* run until the change is on both sides */
|
||||
fixture->n_events = 0;
|
||||
g_main_loop_run (fixture->loop);
|
||||
g_assert_cmpint (fixture->n_events, ==, 2);
|
||||
|
||||
/* test round 2: verify the value change on both sides */
|
||||
|
||||
g_assert_true (wp_endpoint_get_control_float (
|
||||
WP_ENDPOINT (fixture->proxy_endpoint),
|
||||
WP_ENDPOINT_CONTROL_VOLUME, &float_value));
|
||||
g_assert_true (wp_endpoint_get_control_boolean (
|
||||
WP_ENDPOINT (fixture->proxy_endpoint),
|
||||
WP_ENDPOINT_CONTROL_MUTE, &boolean_value));
|
||||
g_assert_cmpfloat_with_epsilon (float_value, 1.0f, 0.001);
|
||||
g_assert_cmpint (boolean_value, ==, TRUE);
|
||||
|
||||
g_assert_true (wp_endpoint_get_control_float (WP_ENDPOINT (endpoint),
|
||||
WP_ENDPOINT_CONTROL_VOLUME, &float_value));
|
||||
g_assert_true (wp_endpoint_get_control_boolean (WP_ENDPOINT (endpoint),
|
||||
WP_ENDPOINT_CONTROL_MUTE, &boolean_value));
|
||||
g_assert_cmpfloat_with_epsilon (float_value, 1.0f, 0.001);
|
||||
g_assert_cmpint (boolean_value, ==, TRUE);
|
||||
|
||||
/* change control on the exported */
|
||||
fixture->n_events = 0;
|
||||
g_assert_true (wp_endpoint_set_control_boolean (WP_ENDPOINT (endpoint),
|
||||
WP_ENDPOINT_CONTROL_MUTE, FALSE));
|
||||
|
||||
/* run until the change is on both sides */
|
||||
g_main_loop_run (fixture->loop);
|
||||
g_assert_cmpint (fixture->n_events, ==, 2);
|
||||
|
||||
/* test round 3: verify the value change on both sides */
|
||||
|
||||
g_assert_true (wp_endpoint_get_control_float (
|
||||
WP_ENDPOINT (fixture->proxy_endpoint),
|
||||
WP_ENDPOINT_CONTROL_VOLUME, &float_value));
|
||||
g_assert_true (wp_endpoint_get_control_boolean (
|
||||
WP_ENDPOINT (fixture->proxy_endpoint),
|
||||
WP_ENDPOINT_CONTROL_MUTE, &boolean_value));
|
||||
g_assert_cmpfloat_with_epsilon (float_value, 1.0f, 0.001);
|
||||
g_assert_cmpint (boolean_value, ==, FALSE);
|
||||
|
||||
g_assert_true (wp_endpoint_get_control_float (WP_ENDPOINT (endpoint),
|
||||
WP_ENDPOINT_CONTROL_VOLUME, &float_value));
|
||||
g_assert_true (wp_endpoint_get_control_boolean (WP_ENDPOINT (endpoint),
|
||||
WP_ENDPOINT_CONTROL_MUTE, &boolean_value));
|
||||
g_assert_cmpfloat_with_epsilon (float_value, 1.0f, 0.001);
|
||||
g_assert_cmpint (boolean_value, ==, FALSE);
|
||||
|
||||
/* change a property on the exported */
|
||||
fixture->n_events = 0;
|
||||
wp_exported_endpoint_set_property (endpoint, "test.property", "changed-value");
|
||||
|
||||
/* run until the change is on both sides */
|
||||
g_main_loop_run (fixture->loop);
|
||||
g_assert_cmpint (fixture->n_events, ==, 2);
|
||||
|
||||
/* test round 4: verify the property change on both sides */
|
||||
|
||||
{
|
||||
g_autoptr (WpProperties) props =
|
||||
wp_endpoint_get_properties (WP_ENDPOINT (endpoint));
|
||||
g_assert_cmpstr (wp_properties_get (props, "test.property"), ==,
|
||||
"changed-value");
|
||||
}
|
||||
{
|
||||
g_autoptr (WpProperties) props =
|
||||
wp_endpoint_get_properties (WP_ENDPOINT (fixture->proxy_endpoint));
|
||||
g_assert_cmpstr (wp_properties_get (props, "test.property"), ==,
|
||||
"changed-value");
|
||||
}
|
||||
|
||||
/* unexport */
|
||||
fixture->n_events = 0;
|
||||
wp_exported_unexport (WP_EXPORTED (endpoint));
|
||||
|
||||
/* run until objects are destroyed */
|
||||
g_main_loop_run (fixture->loop);
|
||||
g_assert_cmpint (fixture->n_events, ==, 2);
|
||||
g_assert_null (fixture->exported_endpoint);
|
||||
g_assert_null (fixture->proxy_endpoint);
|
||||
}
|
||||
|
||||
gint
|
||||
main (gint argc, gchar *argv[])
|
||||
{
|
||||
g_test_init (&argc, &argv, NULL);
|
||||
pw_init (NULL, NULL);
|
||||
|
||||
g_test_add ("/wp/endpoint/basic", TestEndpointFixture, NULL,
|
||||
test_endpoint_setup, test_endpoint_basic, test_endpoint_teardown);
|
||||
|
||||
return g_test_run ();
|
||||
}
|
||||
|
|
@ -4,6 +4,12 @@ common_env = [
|
|||
'G_TEST_BUILDDIR=@0@'.format(meson.current_build_dir()),
|
||||
]
|
||||
|
||||
test(
|
||||
'test-endpoint',
|
||||
executable('test-endpoint', 'endpoint.c', dependencies: common_deps),
|
||||
env: common_env,
|
||||
)
|
||||
|
||||
test(
|
||||
'test-properties',
|
||||
executable('test-properties', 'properties.c', dependencies: common_deps),
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue