lib: remove WpEndpoint and WpSiEndpoint APIs

This commit is contained in:
Julian Bouzas 2022-12-09 13:01:13 -05:00
parent d43b55d01e
commit 9004362cda
21 changed files with 11 additions and 1871 deletions

View file

@ -24,7 +24,6 @@ C API Documentation
c_api/device_api.rst
c_api/client_api.rst
c_api/metadata_api.rst
c_api/endpoint_api.rst
c_api/spa_device_api.rst
c_api/impl_node_api.rst
c_api/impl_module_api.rst

View file

@ -1,24 +0,0 @@
.. _endpoint_api:
PipeWire Endpoint
=================
.. graphviz::
:align: center
digraph inheritance {
rankdir=LR;
GObject -> WpObject;
WpObject -> WpProxy;
WpProxy -> WpGlobalProxy;
WpGlobalProxy -> WpEndpoint;
GInterface -> WpPipewireObject;
WpPipewireObject -> WpEndpoint;
WpEndpoint -> WpImplEndpoint;
}
.. doxygenstruct:: WpEndpoint
.. doxygenstruct:: WpImplEndpoint
.. doxygengroup:: wpendpoint
:content-only:

View file

@ -4,7 +4,6 @@ sphinx_files += files(
'component_loader_api.rst',
'core_api.rst',
'device_api.rst',
'endpoint_api.rst',
'global_proxy_api.rst',
'impl_module_api.rst',
'impl_node_api.rst',

View file

@ -7,17 +7,12 @@ Session Items Interfaces
digraph inheritance {
rankdir=LR;
GInterface -> WpSiEndpoint;
GInterface -> WpSiAdapter;
GInterface -> WpSiLinkable;
GInterface -> WpSiLink;
GInterface -> WpSiAcquisition;
}
.. doxygenstruct:: WpSiEndpoint
.. doxygenstruct:: _WpSiEndpointInterface
.. doxygenstruct:: WpSiAdapter
.. doxygenstruct:: _WpSiAdapterInterface

View file

@ -1,698 +0,0 @@
/* WirePlumber
*
* Copyright © 2019 Collabora Ltd.
* @author George Kiagiadakis <george.kiagiadakis@collabora.com>
*
* SPDX-License-Identifier: MIT
*/
#define G_LOG_DOMAIN "wp-endpoint"
#include "endpoint.h"
#include "node.h"
#include "object-manager.h"
#include "error.h"
#include "log.h"
#include "wpenums.h"
#include "spa-type.h"
#include "si-factory.h"
#include "private/pipewire-object-mixin.h"
#include <pipewire/extensions/session-manager.h>
#include <pipewire/extensions/session-manager/introspect-funcs.h>
#include <spa/utils/result.h>
/*! \defgroup wpendpoint WpEndpoint */
/*!
* \struct WpEndpoint
*
* The WpEndpoint class allows accessing the properties and methods of a
* PipeWire endpoint object (`struct pw_endpoint` from the session-manager extension).
*
* A WpEndpoint is constructed internally when a new endpoint appears on the
* PipeWire registry and it is made available through the WpObjectManager API.
*
* \gproperties
*
* \gproperty{name, gchar *, G_PARAM_READABLE, The name of the endpoint}
*
* \gproperty{media-class, gchar *, G_PARAM_READABLE,
* The media class of the endpoint (ex. "Audio/Sink")}
*
* \gproperty{direction, WpDirection, G_PARAM_READABLE,
* The direction of the endpoint}
*/
enum {
PROP_NAME = WP_PW_OBJECT_MIXIN_PROP_CUSTOM_START,
PROP_MEDIA_CLASS,
PROP_DIRECTION,
};
typedef struct _WpEndpointPrivate WpEndpointPrivate;
struct _WpEndpointPrivate
{
gint place_holder;
};
static void wp_endpoint_pw_object_mixin_priv_interface_init (
WpPwObjectMixinPrivInterface * iface);
G_DEFINE_TYPE_WITH_CODE (WpEndpoint, wp_endpoint, WP_TYPE_GLOBAL_PROXY,
G_ADD_PRIVATE (WpEndpoint)
G_IMPLEMENT_INTERFACE (WP_TYPE_PIPEWIRE_OBJECT,
wp_pw_object_mixin_object_interface_init)
G_IMPLEMENT_INTERFACE (WP_TYPE_PW_OBJECT_MIXIN_PRIV,
wp_endpoint_pw_object_mixin_priv_interface_init))
static void
wp_endpoint_init (WpEndpoint * self)
{
}
static void
wp_endpoint_get_property (GObject * object, guint property_id,
GValue * value, GParamSpec * pspec)
{
WpPwObjectMixinData *d = wp_pw_object_mixin_get_data (object);
switch (property_id) {
case PROP_NAME:
g_value_set_string (value, d->info ?
((struct pw_endpoint_info *) d->info)->name : NULL);
break;
case PROP_MEDIA_CLASS:
g_value_set_string (value, d->info ?
((struct pw_endpoint_info *) d->info)->media_class : NULL);
break;
case PROP_DIRECTION:
g_value_set_enum (value, d->info ?
((struct pw_endpoint_info *) d->info)->direction : 0);
break;
default:
wp_pw_object_mixin_get_property (object, property_id, value, pspec);
break;
}
}
static WpObjectFeatures
wp_endpoint_get_supported_features (WpObject * object)
{
return wp_pw_object_mixin_get_supported_features(object);
}
static void
wp_endpoint_activate_execute_step (WpObject * object,
WpFeatureActivationTransition * transition, guint step,
WpObjectFeatures missing)
{
switch (step) {
case WP_PW_OBJECT_MIXIN_STEP_BIND:
case WP_TRANSITION_STEP_ERROR:
/* base class can handle BIND and ERROR */
WP_OBJECT_CLASS (wp_endpoint_parent_class)->
activate_execute_step (object, transition, step, missing);
break;
case WP_PW_OBJECT_MIXIN_STEP_WAIT_INFO:
/* just wait, info will be emitted anyway after binding */
break;
case WP_PW_OBJECT_MIXIN_STEP_CACHE_PARAMS:
wp_pw_object_mixin_cache_params (object, missing);
break;
default:
g_assert_not_reached ();
}
}
static void
wp_endpoint_deactivate (WpObject * object, WpObjectFeatures features)
{
wp_pw_object_mixin_deactivate (object, features);
WP_OBJECT_CLASS (wp_endpoint_parent_class)->deactivate (object, features);
}
static const struct pw_endpoint_events endpoint_events = {
PW_VERSION_ENDPOINT_EVENTS,
.info = (HandleEventInfoFunc(endpoint)) wp_pw_object_mixin_handle_event_info,
.param = wp_pw_object_mixin_handle_event_param,
};
static void
wp_endpoint_pw_proxy_created (WpProxy * proxy, struct pw_proxy * pw_proxy)
{
wp_pw_object_mixin_handle_pw_proxy_created (proxy, pw_proxy,
endpoint, &endpoint_events);
}
static void
wp_endpoint_pw_proxy_destroyed (WpProxy * proxy)
{
wp_pw_object_mixin_handle_pw_proxy_destroyed (proxy);
WP_PROXY_CLASS (wp_endpoint_parent_class)->pw_proxy_destroyed (proxy);
}
static void
wp_endpoint_class_init (WpEndpointClass * klass)
{
GObjectClass *object_class = (GObjectClass *) klass;
WpObjectClass *wpobject_class = (WpObjectClass *) klass;
WpProxyClass *proxy_class = (WpProxyClass *) klass;
object_class->get_property = wp_endpoint_get_property;
wpobject_class->get_supported_features = wp_endpoint_get_supported_features;
wpobject_class->activate_get_next_step =
wp_pw_object_mixin_activate_get_next_step;
wpobject_class->activate_execute_step = wp_endpoint_activate_execute_step;
wpobject_class->deactivate = wp_endpoint_deactivate;
proxy_class->pw_iface_type = PW_TYPE_INTERFACE_Endpoint;
proxy_class->pw_iface_version = PW_VERSION_ENDPOINT;
proxy_class->pw_proxy_created = wp_endpoint_pw_proxy_created;
proxy_class->pw_proxy_destroyed = wp_endpoint_pw_proxy_destroyed;
wp_pw_object_mixin_class_override_properties (object_class);
g_object_class_install_property (object_class, PROP_NAME,
g_param_spec_string ("name", "name", "name", NULL,
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (object_class, PROP_MEDIA_CLASS,
g_param_spec_string ("media-class", "media-class", "media-class", NULL,
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (object_class, PROP_DIRECTION,
g_param_spec_enum ("direction", "direction", "direction",
WP_TYPE_DIRECTION, 0, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
}
static gint
wp_endpoint_enum_params (gpointer instance, guint32 id,
guint32 start, guint32 num, WpSpaPod *filter)
{
WpPwObjectMixinData *d = wp_pw_object_mixin_get_data (instance);
return pw_endpoint_enum_params (d->iface, 0, id, start, num,
filter ? wp_spa_pod_get_spa_pod (filter) : NULL);
}
static gint
wp_endpoint_set_param (gpointer instance, guint32 id, guint32 flags,
WpSpaPod * param)
{
WpPwObjectMixinData *d = wp_pw_object_mixin_get_data (instance);
g_autoptr (WpSpaPod) p = param;
return pw_endpoint_set_param (d->iface, id, flags,
wp_spa_pod_get_spa_pod (p));
}
static void
wp_endpoint_pw_object_mixin_priv_interface_init (
WpPwObjectMixinPrivInterface * iface)
{
wp_pw_object_mixin_priv_interface_info_init (iface, endpoint, ENDPOINT);
iface->enum_params = wp_endpoint_enum_params;
iface->set_param = wp_endpoint_set_param;
}
/*!
* \brief Gets the name of the endpoint
* \remarks Requires WP_PIPEWIRE_OBJECT_FEATURE_INFO
* \ingroup wpendpoint
* \param self the endpoint
* \returns the name of the endpoint
*/
const gchar *
wp_endpoint_get_name (WpEndpoint * self)
{
g_return_val_if_fail (WP_IS_ENDPOINT (self), NULL);
g_return_val_if_fail (wp_object_get_active_features (WP_OBJECT (self)) &
WP_PIPEWIRE_OBJECT_FEATURE_INFO, NULL);
WpPwObjectMixinData *d = wp_pw_object_mixin_get_data (self);
return ((struct pw_endpoint_info *) d->info)->name;
}
/*!
* \brief Gets the media class of the endpoint (ex. "Audio/Sink")
* \remarks Requires WP_PIPEWIRE_OBJECT_FEATURE_INFO
* \ingroup wpendpoint
* \param self the endpoint
* \returns the media class of the endpoint
*/
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_object_get_active_features (WP_OBJECT (self)) &
WP_PIPEWIRE_OBJECT_FEATURE_INFO, NULL);
WpPwObjectMixinData *d = wp_pw_object_mixin_get_data (self);
return ((struct pw_endpoint_info *) d->info)->media_class;
}
/*!
* \brief Gets the direction of the endpoint
* \remarks Requires WP_PIPEWIRE_OBJECT_FEATURE_INFO
* \ingroup wpendpoint
* \param self the endpoint
* \returns the direction of this endpoint
*/
WpDirection
wp_endpoint_get_direction (WpEndpoint * self)
{
g_return_val_if_fail (WP_IS_ENDPOINT (self), 0);
g_return_val_if_fail (wp_object_get_active_features (WP_OBJECT (self)) &
WP_PIPEWIRE_OBJECT_FEATURE_INFO, 0);
WpPwObjectMixinData *d = wp_pw_object_mixin_get_data (self);
return (WpDirection) ((struct pw_endpoint_info *) d->info)->direction;
}
/* WpImplEndpoint */
enum {
IMPL_PROP_0,
IMPL_PROP_ITEM,
};
struct _WpImplEndpoint
{
WpEndpoint parent;
struct spa_interface iface;
struct pw_endpoint_info info;
WpProperties *immutable_props;
WpSiEndpoint *item;
};
static void wp_endpoint_impl_pw_object_mixin_priv_interface_init (
WpPwObjectMixinPrivInterface * iface);
G_DEFINE_TYPE_WITH_CODE (WpImplEndpoint, wp_impl_endpoint, WP_TYPE_ENDPOINT,
G_IMPLEMENT_INTERFACE (WP_TYPE_PW_OBJECT_MIXIN_PRIV,
wp_endpoint_impl_pw_object_mixin_priv_interface_init))
static struct spa_param_info impl_param_info[] = {
SPA_PARAM_INFO (SPA_PARAM_Props, SPA_PARAM_INFO_READWRITE),
SPA_PARAM_INFO (SPA_PARAM_PropInfo, SPA_PARAM_INFO_READ)
};
static int
impl_create_link (void *object, const struct spa_dict *props)
{
/* not implemented */
return -EINVAL;
}
static const struct pw_endpoint_methods impl_endpoint = {
PW_VERSION_ENDPOINT_METHODS,
.add_listener =
(ImplAddListenerFunc(endpoint)) wp_pw_object_mixin_impl_add_listener,
.subscribe_params = wp_pw_object_mixin_impl_subscribe_params,
.enum_params = wp_pw_object_mixin_impl_enum_params,
.set_param = wp_pw_object_mixin_impl_set_param,
.create_link = impl_create_link,
};
static void
wp_impl_endpoint_init (WpImplEndpoint * self)
{
WpPwObjectMixinData *d = wp_pw_object_mixin_get_data (self);
self->iface = SPA_INTERFACE_INIT (
PW_TYPE_INTERFACE_Endpoint,
PW_VERSION_ENDPOINT,
&impl_endpoint, self);
d->info = &self->info;
d->iface = &self->iface;
}
static void
populate_properties (WpImplEndpoint * self)
{
WpPwObjectMixinData *d = wp_pw_object_mixin_get_data (self);
g_clear_pointer (&d->properties, wp_properties_unref);
d->properties = wp_si_endpoint_get_properties (self->item);
if (!d->properties)
d->properties = wp_properties_new_empty ();
d->properties = wp_properties_ensure_unique_owner (d->properties);
wp_properties_update (d->properties, self->immutable_props);
self->info.props = (struct spa_dict *) wp_properties_peek_dict (d->properties);
}
static void
on_si_endpoint_properties_changed (WpSiEndpoint * item, WpImplEndpoint * self)
{
populate_properties (self);
wp_pw_object_mixin_notify_info (self, PW_ENDPOINT_CHANGE_MASK_PROPS);
}
static void
on_node_params_changed (WpNode * node, const gchar *param_name,
WpImplEndpoint * self)
{
if (!g_strcmp0 (param_name, "PropInfo") || !g_strcmp0 (param_name, "Props")) {
guint32 param_id = wp_spa_id_value_number (
wp_spa_id_value_from_short_name ("Spa:Enum:ParamId", param_name));
wp_pw_object_mixin_notify_params_changed (self, param_id);
}
}
static void
wp_impl_endpoint_constructed (GObject * object)
{
WpImplEndpoint *self = WP_IMPL_ENDPOINT (object);
g_autoptr (GVariant) info = NULL;
g_autoptr (GVariantIter) immutable_props = NULL;
g_autoptr (WpObject) node = NULL;
const gchar *key, *value;
guchar direction;
self->info.version = PW_VERSION_ENDPOINT_INFO;
info = wp_si_endpoint_get_registration_info (self->item);
g_variant_get (info, "(ssya{ss})", &self->info.name,
&self->info.media_class, &direction, &immutable_props);
self->info.direction = (enum pw_direction) direction;
/* associate with the session (no session anymore, use -1) */
self->info.session_id = SPA_ID_INVALID;
/* construct export properties (these will come back through
the registry and appear in wp_proxy_get_global_properties) */
self->immutable_props = wp_properties_new (
PW_KEY_ENDPOINT_NAME, self->info.name,
PW_KEY_MEDIA_CLASS, self->info.media_class,
NULL);
wp_properties_setf (self->immutable_props, PW_KEY_SESSION_ID,
"%d", self->info.session_id);
/* populate immutable (global) properties */
while (g_variant_iter_next (immutable_props, "{&s&s}", &key, &value))
wp_properties_set (self->immutable_props, key, value);
/* populate standard properties */
populate_properties (self);
/* subscribe to changes */
g_signal_connect_object (self->item, "endpoint-properties-changed",
G_CALLBACK (on_si_endpoint_properties_changed), self, 0);
/* if the item has a node, proxy its ParamProps */
node = wp_session_item_get_associated_proxy (
WP_SESSION_ITEM (self->item), WP_TYPE_NODE);
if (node && (wp_object_get_active_features (node) &
WP_PIPEWIRE_OBJECT_FEATURE_PARAM_PROPS)) {
self->info.params = impl_param_info;
self->info.n_params = G_N_ELEMENTS (impl_param_info);
g_signal_connect_object (node, "params-changed",
G_CALLBACK (on_node_params_changed), self, 0);
wp_object_update_features (WP_OBJECT (self),
WP_PIPEWIRE_OBJECT_FEATURE_PARAM_PROPS, 0);
} else {
self->info.params = NULL;
self->info.n_params = 0;
}
wp_object_update_features (WP_OBJECT (self),
WP_PIPEWIRE_OBJECT_FEATURE_INFO, 0);
G_OBJECT_CLASS (wp_impl_endpoint_parent_class)->constructed (object);
}
static void
wp_impl_endpoint_dispose (GObject * object)
{
WpImplEndpoint *self = WP_IMPL_ENDPOINT (object);
g_clear_pointer (&self->immutable_props, wp_properties_unref);
g_clear_pointer (&self->info.name, g_free);
g_clear_pointer (&self->info.media_class, g_free);
wp_object_update_features (WP_OBJECT (self), 0,
WP_PIPEWIRE_OBJECT_FEATURE_INFO |
WP_PIPEWIRE_OBJECT_FEATURE_PARAM_PROPS);
G_OBJECT_CLASS (wp_impl_endpoint_parent_class)->dispose (object);
}
static void
wp_impl_endpoint_set_property (GObject * object, guint property_id,
const GValue * value, GParamSpec * pspec)
{
WpImplEndpoint *self = WP_IMPL_ENDPOINT (object);
switch (property_id) {
case IMPL_PROP_ITEM:
self->item = g_value_get_object (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
wp_impl_endpoint_get_property (GObject * object, guint property_id,
GValue * value, GParamSpec * pspec)
{
WpImplEndpoint *self = WP_IMPL_ENDPOINT (object);
switch (property_id) {
case IMPL_PROP_ITEM:
g_value_set_object (value, self->item);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
enum {
STEP_ACTIVATE_NODE = WP_PW_OBJECT_MIXIN_STEP_CUSTOM_START + 1,
};
static guint
wp_impl_endpoint_activate_get_next_step (WpObject * object,
WpFeatureActivationTransition * transition, guint step,
WpObjectFeatures missing)
{
WpImplEndpoint *self = WP_IMPL_ENDPOINT (object);
/* before anything else, if the item has a node,
cache its props so that enum_params works */
if (missing & WP_PIPEWIRE_OBJECT_FEATURES_ALL) {
g_autoptr (WpObject) node = wp_session_item_get_associated_proxy (
WP_SESSION_ITEM (self->item), WP_TYPE_NODE);
if (node && (wp_object_get_supported_features (node) &
WP_PIPEWIRE_OBJECT_FEATURE_PARAM_PROPS) &&
!(wp_object_get_active_features (node) &
WP_PIPEWIRE_OBJECT_FEATURE_PARAM_PROPS))
return STEP_ACTIVATE_NODE;
}
return WP_OBJECT_CLASS (wp_impl_endpoint_parent_class)->
activate_get_next_step (object, transition, step, missing);
}
static void
wp_impl_endpoint_node_activated (WpObject * node,
GAsyncResult * res, WpTransition * transition)
{
WpImplEndpoint *self = wp_transition_get_source_object (transition);
g_autoptr (GError) error = NULL;
if (!wp_object_activate_finish (node, res, &error)) {
wp_transition_return_error (transition, g_steal_pointer (&error));
return;
}
self->info.params = impl_param_info;
self->info.n_params = G_N_ELEMENTS (impl_param_info);
g_signal_connect_object (node, "params-changed",
G_CALLBACK (on_node_params_changed), self, 0);
wp_object_update_features (WP_OBJECT (self),
WP_PIPEWIRE_OBJECT_FEATURE_PARAM_PROPS, 0);
wp_pw_object_mixin_notify_info (self, PW_ENDPOINT_CHANGE_MASK_PARAMS);
}
static void
wp_impl_endpoint_activate_execute_step (WpObject * object,
WpFeatureActivationTransition * transition, guint step,
WpObjectFeatures missing)
{
WpImplEndpoint *self = WP_IMPL_ENDPOINT (object);
switch (step) {
case STEP_ACTIVATE_NODE: {
g_autoptr (WpObject) node = wp_session_item_get_associated_proxy (
WP_SESSION_ITEM (self->item), WP_TYPE_NODE);
wp_object_activate (node,
WP_PROXY_FEATURE_BOUND | WP_PIPEWIRE_OBJECT_FEATURE_PARAM_PROPS,
NULL, (GAsyncReadyCallback) wp_impl_endpoint_node_activated,
transition);
break;
}
case WP_PW_OBJECT_MIXIN_STEP_BIND: {
g_autoptr (WpCore) core = wp_object_get_core (WP_OBJECT (self));
struct pw_core *pw_core = wp_core_get_pw_core (core);
/* no pw_core -> we are not connected */
if (!pw_core) {
wp_transition_return_error (WP_TRANSITION (transition), g_error_new (
WP_DOMAIN_LIBRARY, WP_LIBRARY_ERROR_OPERATION_FAILED,
"The WirePlumber core is not connected; "
"object cannot be exported to PipeWire"));
return;
}
/* bind */
wp_proxy_set_pw_proxy (WP_PROXY (self), pw_core_export (pw_core,
PW_TYPE_INTERFACE_Endpoint,
wp_properties_peek_dict (self->immutable_props),
&self->iface, 0));
break;
}
default:
WP_OBJECT_CLASS (wp_impl_endpoint_parent_class)->
activate_execute_step (object, transition, step, missing);
break;
}
}
/*!
* \struct WpImplEndpoint
*
* \gproperties
*
* \gproperty{item, WpSiEndpoint *, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY,
* The session item that implements this endpoint}
*/
static void
wp_impl_endpoint_class_init (WpImplEndpointClass * klass)
{
GObjectClass *object_class = (GObjectClass *) klass;
WpObjectClass *wpobject_class = (WpObjectClass *) klass;
WpProxyClass *proxy_class = (WpProxyClass *) klass;
object_class->constructed = wp_impl_endpoint_constructed;
object_class->dispose = wp_impl_endpoint_dispose;
object_class->set_property = wp_impl_endpoint_set_property;
object_class->get_property = wp_impl_endpoint_get_property;
wpobject_class->activate_get_next_step =
wp_impl_endpoint_activate_get_next_step;
wpobject_class->activate_execute_step =
wp_impl_endpoint_activate_execute_step;
proxy_class->pw_proxy_created = NULL;
proxy_class->pw_proxy_destroyed = NULL;
g_object_class_install_property (object_class, IMPL_PROP_ITEM,
g_param_spec_object ("item", "item", "item", WP_TYPE_SI_ENDPOINT,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
}
static GPtrArray *
wp_impl_endpoint_enum_params_sync (gpointer instance, guint32 id,
guint32 start, guint32 num, WpSpaPod *filter)
{
WpImplEndpoint *self = WP_IMPL_ENDPOINT (instance);
g_autoptr (WpPipewireObject) node = wp_session_item_get_associated_proxy (
WP_SESSION_ITEM (self->item), WP_TYPE_NODE);
if (!node) {
wp_warning_object (self, "associated node is no longer available");
return NULL;
}
/* bypass a few things, knowing that the node
caches params in the mixin param store */
WpPwObjectMixinData *data = wp_pw_object_mixin_get_data (node);
GPtrArray *params = wp_pw_object_mixin_get_stored_params (data, id);
/* TODO filter */
return params;
}
static gint
wp_impl_endpoint_set_param (gpointer instance, guint32 id, guint32 flags,
WpSpaPod * param)
{
WpImplEndpoint *self = WP_IMPL_ENDPOINT (instance);
g_autoptr (WpPipewireObject) node = wp_session_item_get_associated_proxy (
WP_SESSION_ITEM (self->item), WP_TYPE_NODE);
if (!node) {
wp_warning_object (self, "associated node is no longer available");
return -EPIPE;
}
WpSpaIdValue idval = wp_spa_id_value_from_number ("Spa:Enum:ParamId", id);
if (!idval) {
wp_critical_object (self, "invalid param id: %u", id);
return -EINVAL;
}
return wp_pipewire_object_set_param (node, wp_spa_id_value_short_name (idval),
flags, param) ? 0 : -EIO;
}
#define pw_endpoint_emit(hooks,method,version,...) \
spa_hook_list_call_simple(hooks, struct pw_endpoint_events, \
method, version, ##__VA_ARGS__)
static void
wp_impl_endpoint_emit_info (struct spa_hook_list * hooks, gconstpointer info)
{
pw_endpoint_emit (hooks, info, 0, info);
}
static void
wp_impl_endpoint_emit_param (struct spa_hook_list * hooks, int seq,
guint32 id, guint32 index, guint32 next, const struct spa_pod *param)
{
pw_endpoint_emit (hooks, param, 0, seq, id, index, next, param);
}
static void
wp_endpoint_impl_pw_object_mixin_priv_interface_init (
WpPwObjectMixinPrivInterface * iface)
{
iface->flags |= WP_PW_OBJECT_MIXIN_PRIV_NO_PARAM_CACHE;
iface->enum_params_sync = wp_impl_endpoint_enum_params_sync;
iface->set_param = wp_impl_endpoint_set_param;
iface->emit_info = wp_impl_endpoint_emit_info;
iface->emit_param = wp_impl_endpoint_emit_param;
}
/*!
* \brief Creates a new endpoint implementation
*
* \ingroup wpendpoint
* \param core the core
* \param item the session item that implements the endpoint
* \returns (transfer full): a new WpImplEndpoint
*/
WpImplEndpoint *
wp_impl_endpoint_new (WpCore * core, WpSiEndpoint * item)
{
g_return_val_if_fail (WP_IS_CORE (core), NULL);
return g_object_new (WP_TYPE_IMPL_ENDPOINT,
"core", core,
"item", item,
NULL);
}

View file

@ -1,59 +0,0 @@
/* 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 "global-proxy.h"
#include "port.h"
#include "iterator.h"
#include "object-interest.h"
#include "si-interfaces.h"
G_BEGIN_DECLS
/*!
* \brief The WpEndpoint GType
* \ingroup wpendpoint
*/
#define WP_TYPE_ENDPOINT (wp_endpoint_get_type ())
WP_API
G_DECLARE_DERIVABLE_TYPE (WpEndpoint, wp_endpoint, WP, ENDPOINT, WpGlobalProxy)
struct _WpEndpointClass
{
WpGlobalProxyClass parent_class;
/*< private >*/
WP_PADDING(4)
};
WP_API
const gchar * wp_endpoint_get_name (WpEndpoint * self);
WP_API
const gchar * wp_endpoint_get_media_class (WpEndpoint * self);
WP_API
WpDirection wp_endpoint_get_direction (WpEndpoint * self);
/*!
* \brief The WpImplEndpoint GType
* \ingroup wpendpoint
*/
#define WP_TYPE_IMPL_ENDPOINT (wp_impl_endpoint_get_type ())
WP_API
G_DECLARE_FINAL_TYPE (WpImplEndpoint, wp_impl_endpoint,
WP, IMPL_ENDPOINT, WpEndpoint)
WP_API
WpImplEndpoint * wp_impl_endpoint_new (WpCore * core, WpSiEndpoint * item);
G_END_DECLS
#endif

View file

@ -5,7 +5,6 @@ wp_lib_sources = files(
'core.c',
'dbus.c',
'device.c',
'endpoint.c',
'error.c',
'event.c',
'event-dispatcher.c',
@ -50,7 +49,6 @@ wp_lib_headers = files(
'dbus.h',
'defs.h',
'device.h',
'endpoint.h',
'error.h',
'event.h',
'event-dispatcher.h',

View file

@ -33,7 +33,7 @@
* instance that created them appears in the WpObjectManager (as soon as
* its WP_PROXY_FEATURE_BOUND is enabled)
* * local PipeWire objects that are being exported to PipeWire
* (WpImplMetadata, WpImplEndpoint, etc); these appear in the
* (WpImplMetadata, WpImplNode, etc); these appear in the
* WpObjectManager as soon as they are exported (so, when their
* WP_PROXY_FEATURE_BOUND is enabled)
* * WirePlumber-specific objects, such as plugins, factories and session items

View file

@ -122,7 +122,7 @@ typedef struct _WpPwObjectMixinData WpPwObjectMixinData;
struct _WpPwObjectMixinData
{
gpointer info; /* pointer to the info struct */
gpointer iface; /* pointer to the interface (ex. pw_endpoint) */
gpointer iface; /* pointer to the interface (ex. pw_node) */
struct spa_hook listener;
struct spa_hook_list hooks;
WpProperties *properties;

View file

@ -256,12 +256,7 @@ wp_session_item_is_configured (WpSessionItem * self)
/*!
* \brief An associated proxy is a WpProxy subclass instance that
* is somehow related to this item. For example:
* - An exported WpSiEndpoint should have at least:
* - an associated WpSiEndpoint
* - an associated WpSession
* - In cases where the item wraps a single PipeWire node, it should also
* have an associated WpNode
* is somehow related to this item.
*
* \ingroup wpsessionitem
* \param self the session item

View file

@ -13,82 +13,6 @@
/*! \defgroup wpsiinterfaces Session Item Interfaces */
/*!
* \struct WpSiEndpoint
*
* An interface for session items that implement a PipeWire endpoint.
*
* \gsignals
*
* \par endpoint-properties-changed
* \parblock
* \code
* void
* endpoint_properties_changed_callback (WpSiEndpoint * self,
* gpointer user_data)
* \endcode
* Emitted when the endpoint properties change
*
* Flags: G_SIGNAL_RUN_LAST
* \endparblock
*/
G_DEFINE_INTERFACE (WpSiEndpoint, wp_si_endpoint, WP_TYPE_SESSION_ITEM)
static WpProperties *
wp_si_endpoint_default_get_properties (WpSiEndpoint * self)
{
return NULL;
}
static void
wp_si_endpoint_default_init (WpSiEndpointInterface * iface)
{
iface->get_properties = wp_si_endpoint_default_get_properties;
g_signal_new ("endpoint-properties-changed", G_TYPE_FROM_INTERFACE (iface),
G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0);
}
/*!
* \brief This should return information that is used for registering the
* endpoint.
*
* The return value should be a GVariant tuple of type (ssya{ss}) that contains,
* in order:
* - s: the endpoint's name
* - s: the media class
* - y: the direction
* - a{ss}: additional properties to be added to the list of global properties
*
* \ingroup wpsiinterfaces
* \param self the session item
* \returns (transfer full): registration info for the endpoint
*/
GVariant *
wp_si_endpoint_get_registration_info (WpSiEndpoint * self)
{
g_return_val_if_fail (WP_IS_SI_ENDPOINT (self), NULL);
g_return_val_if_fail (WP_SI_ENDPOINT_GET_IFACE (self)->get_registration_info, NULL);
return WP_SI_ENDPOINT_GET_IFACE (self)->get_registration_info (self);
}
/*!
* \brief Gets the properties of the endpoint
*
* \ingroup wpsiinterfaces
* \param self the session item
* \returns (transfer full) (nullable): the properties of the endpoint
*/
WpProperties *
wp_si_endpoint_get_properties (WpSiEndpoint * self)
{
g_return_val_if_fail (WP_IS_SI_ENDPOINT (self), NULL);
g_return_val_if_fail (WP_SI_ENDPOINT_GET_IFACE (self)->get_properties, NULL);
return WP_SI_ENDPOINT_GET_IFACE (self)->get_properties (self);
}
/*!
* \struct WpSiAdapter
* An interface for port adapters
@ -210,10 +134,6 @@ wp_si_adapter_set_ports_format_finish (WpSiAdapter * self, GAsyncResult * res,
*
* An interface for retrieving PipeWire port information from a session item.
* This information is used to create links in the nodes graph.
*
* This is normally implemented by the same session items that implement
* WpSiEndpoint. The standard link implementation expects to be able to cast
* a WpSiEndpoint into a WpSiLinkable.
*/
G_DEFINE_INTERFACE (WpSiLinkable, wp_si_linkable, WP_TYPE_SESSION_ITEM)
@ -303,7 +223,7 @@ wp_si_linkable_get_acquisition (WpSiLinkable * self)
/*!
* \struct WpSiLink
*
* An interface for session items that provide a PipeWire endpoint link.
* An interface for session items that provide a PipeWire link.
*
* \gsignals
*

View file

@ -17,32 +17,6 @@ G_BEGIN_DECLS
typedef struct _WpSiAcquisition WpSiAcquisition;
/*!
* \brief The WpSiEndpoint GType
* \ingroup wpsiinterfaces
*/
#define WP_TYPE_SI_ENDPOINT (wp_si_endpoint_get_type ())
WP_API
G_DECLARE_INTERFACE (WpSiEndpoint, wp_si_endpoint,
WP, SI_ENDPOINT, WpSessionItem)
struct _WpSiEndpointInterface
{
GTypeInterface interface;
GVariant * (*get_registration_info) (WpSiEndpoint * self);
WpProperties * (*get_properties) (WpSiEndpoint * self);
/*< private >*/
WP_PADDING(6)
};
WP_API
GVariant * wp_si_endpoint_get_registration_info (WpSiEndpoint * self);
WP_API
WpProperties * wp_si_endpoint_get_properties (WpSiEndpoint * self);
/*!
* \brief The WpSiAdapter GType
* \ingroup wpsiinterfaces

View file

@ -59,7 +59,6 @@ wp_init (WpInitFlags flags)
to autodetect the GType of proxies created through wp_proxy_new_global() */
g_type_ensure (WP_TYPE_CLIENT);
g_type_ensure (WP_TYPE_DEVICE);
g_type_ensure (WP_TYPE_ENDPOINT);
g_type_ensure (WP_TYPE_LINK);
g_type_ensure (WP_TYPE_METADATA);
g_type_ensure (WP_TYPE_NODE);

View file

@ -15,7 +15,6 @@
#include "core.h"
#include "dbus.h"
#include "device.h"
#include "endpoint.h"
#include "error.h"
#include "event-dispatcher.h"
#include "event-hook.h"

View file

@ -875,12 +875,6 @@ impl_metadata_new (lua_State *L)
return m ? 1 : 0;
}
/* WpEndpoint */
static const luaL_Reg endpoint_methods[] = {
{ NULL, NULL }
};
/* Device */
static int
@ -2340,8 +2334,6 @@ wp_lua_scripting_api_init (lua_State *L)
NULL, metadata_methods);
wplua_register_type_methods (L, WP_TYPE_IMPL_METADATA,
impl_metadata_new, NULL);
wplua_register_type_methods (L, WP_TYPE_ENDPOINT,
NULL, endpoint_methods);
wplua_register_type_methods (L, WP_TYPE_DEVICE,
device_new, NULL);
wplua_register_type_methods (L, WP_TYPE_SPA_DEVICE,

View file

@ -29,12 +29,8 @@ struct _WpSiAudioEndpoint
/* activation */
WpNode *node;
WpSiAdapter *adapter;
/* export */
WpImplEndpoint *impl_endpoint;
};
static void si_audio_endpoint_endpoint_init (WpSiEndpointInterface * iface);
static void si_audio_endpoint_linkable_init (WpSiLinkableInterface * iface);
static void si_audio_endpoint_adapter_init (WpSiAdapterInterface * iface);
@ -42,7 +38,6 @@ G_DECLARE_FINAL_TYPE(WpSiAudioEndpoint, si_audio_endpoint, WP,
SI_AUDIO_ENDPOINT, WpSessionItem)
G_DEFINE_TYPE_WITH_CODE (WpSiAudioEndpoint, si_audio_endpoint,
WP_TYPE_SESSION_ITEM,
G_IMPLEMENT_INTERFACE (WP_TYPE_SI_ENDPOINT, si_audio_endpoint_endpoint_init)
G_IMPLEMENT_INTERFACE (WP_TYPE_SI_LINKABLE,
si_audio_endpoint_linkable_init)
G_IMPLEMENT_INTERFACE (WP_TYPE_SI_ADAPTER, si_audio_endpoint_adapter_init))
@ -125,9 +120,6 @@ si_audio_endpoint_get_associated_proxy (WpSessionItem * item, GType proxy_type)
{
WpSiAudioEndpoint *self = WP_SI_AUDIO_ENDPOINT (item);
if (proxy_type == WP_TYPE_ENDPOINT)
return self->impl_endpoint ? g_object_ref (self->impl_endpoint) : NULL;
return wp_session_item_get_associated_proxy (
WP_SESSION_ITEM (self->adapter), proxy_type);
}
@ -148,7 +140,6 @@ si_audio_endpoint_disable_exported (WpSessionItem *si)
{
WpSiAudioEndpoint *self = WP_SI_AUDIO_ENDPOINT (si);
g_clear_object (&self->impl_endpoint);
wp_object_update_features (WP_OBJECT (self), 0,
WP_SESSION_ITEM_FEATURE_EXPORTED);
}
@ -268,36 +259,13 @@ si_audio_endpoint_enable_active (WpSessionItem *si, WpTransition *transition)
(GAsyncReadyCallback) on_node_activate_done, transition);
}
static void
on_impl_endpoint_activated (WpObject * object, GAsyncResult * res,
WpTransition * transition)
{
WpSiAudioEndpoint *self = wp_transition_get_source_object (transition);
g_autoptr (GError) error = NULL;
if (!wp_object_activate_finish (object, res, &error)) {
wp_transition_return_error (transition, g_steal_pointer (&error));
return;
}
wp_object_update_features (WP_OBJECT (self),
WP_SESSION_ITEM_FEATURE_EXPORTED, 0);
}
static void
si_audio_endpoint_enable_exported (WpSessionItem *si, WpTransition *transition)
{
WpSiAudioEndpoint *self = WP_SI_AUDIO_ENDPOINT (si);
g_autoptr (WpCore) core = wp_object_get_core (WP_OBJECT (self));
self->impl_endpoint = wp_impl_endpoint_new (core, WP_SI_ENDPOINT (self));
g_signal_connect_object (self->impl_endpoint, "pw-proxy-destroyed",
G_CALLBACK (wp_session_item_handle_proxy_destroyed), self, 0);
wp_object_activate (WP_OBJECT (self->impl_endpoint),
WP_OBJECT_FEATURES_ALL, NULL,
(GAsyncReadyCallback) on_impl_endpoint_activated, transition);
wp_object_update_features (WP_OBJECT (self),
WP_SESSION_ITEM_FEATURE_EXPORTED, 0);
}
static void
@ -314,51 +282,6 @@ si_audio_endpoint_class_init (WpSiAudioEndpointClass * klass)
si_class->enable_exported = si_audio_endpoint_enable_exported;
}
static GVariant *
si_audio_endpoint_get_registration_info (WpSiEndpoint * item)
{
WpSiAudioEndpoint *self = WP_SI_AUDIO_ENDPOINT (item);
GVariantBuilder b;
g_variant_builder_init (&b, G_VARIANT_TYPE ("(ssya{ss})"));
g_variant_builder_add (&b, "s", self->name);
g_variant_builder_add (&b, "s", self->media_class);
g_variant_builder_add (&b, "y", self->direction);
g_variant_builder_add (&b, "a{ss}", NULL);
return g_variant_builder_end (&b);
}
static WpProperties *
si_audio_endpoint_get_properties (WpSiEndpoint * item)
{
WpSiAudioEndpoint *self = WP_SI_AUDIO_ENDPOINT (item);
g_autoptr (WpNode) node = wp_session_item_get_associated_proxy (
WP_SESSION_ITEM (self->adapter), WP_TYPE_NODE);
WpProperties *result = wp_properties_new_empty ();
wp_properties_set (result, "endpoint.name", self->name);
wp_properties_setf (result, "endpoint.priority", "%u", self->priority);
wp_properties_setf (result, "endpoint.description", "%s: %s",
(self->direction == WP_DIRECTION_OUTPUT) ? "Capture" : "Playback",
self->role);
wp_properties_set (result, "media.role", self->role);
/* associate with the node */
g_return_val_if_fail (node, NULL);
wp_properties_setf (result, PW_KEY_NODE_ID, "%d",
wp_proxy_get_bound_id (WP_PROXY (node)));
return result;
}
static void
si_audio_endpoint_endpoint_init (WpSiEndpointInterface * iface)
{
iface->get_registration_info = si_audio_endpoint_get_registration_info;
iface->get_properties = si_audio_endpoint_get_properties;
}
static GVariant *
si_audio_endpoint_get_ports (WpSiLinkable * item, const gchar * context)
{

View file

@ -26,7 +26,6 @@ typedef enum {
OBJECT_TYPE_LINK,
OBJECT_TYPE_NODE,
OBJECT_TYPE_SESSION_ITEM,
OBJECT_TYPE_ENDPOINT,
OBJECT_TYPE_CLIENT,
OBJECT_TYPE_DEVICE,
OBJECT_TYPE_METADATA,
@ -87,7 +86,6 @@ object_type_to_gtype (ObjectType type)
case OBJECT_TYPE_LINK: return WP_TYPE_LINK;
case OBJECT_TYPE_NODE: return WP_TYPE_NODE;
case OBJECT_TYPE_SESSION_ITEM: return WP_TYPE_SESSION_ITEM;
case OBJECT_TYPE_ENDPOINT: return WP_TYPE_ENDPOINT;
case OBJECT_TYPE_CLIENT: return WP_TYPE_CLIENT;
case OBJECT_TYPE_DEVICE: return WP_TYPE_DEVICE;
case OBJECT_TYPE_METADATA: return WP_TYPE_METADATA;
@ -107,8 +105,6 @@ type_str_to_object_type (const gchar * type_str)
return OBJECT_TYPE_NODE;
else if (!g_strcmp0 (type_str, "session-item"))
return OBJECT_TYPE_SESSION_ITEM;
else if (!g_strcmp0 (type_str, "endpoint"))
return OBJECT_TYPE_ENDPOINT;
else if (!g_strcmp0 (type_str, "client"))
return OBJECT_TYPE_CLIENT;
else if (!g_strcmp0 (type_str, "device"))
@ -142,8 +138,6 @@ get_object_type (gpointer obj, WpProperties **properties)
}
return "session-item";
}
else if (WP_IS_ENDPOINT (obj))
return "endpoint";
else if (WP_IS_CLIENT (obj))
return "client";
else if (WP_IS_DEVICE (obj))

View file

@ -189,7 +189,6 @@ status_prepare (WpCtl * self, GError ** error)
{
wp_object_manager_add_interest (self->om, WP_TYPE_CLIENT, NULL);
wp_object_manager_add_interest (self->om, WP_TYPE_DEVICE, NULL);
wp_object_manager_add_interest (self->om, WP_TYPE_ENDPOINT, NULL);
wp_object_manager_add_interest (self->om, WP_TYPE_NODE, NULL);
wp_object_manager_add_interest (self->om, WP_TYPE_PORT, NULL);
wp_object_manager_add_interest (self->om, WP_TYPE_LINK, NULL);
@ -259,27 +258,6 @@ print_dev_node (const GValue *item, gpointer data)
print_controls (id, context);
}
static void
print_endpoint (const GValue *item, gpointer data)
{
WpPipewireObject *obj = g_value_get_object (item);
struct print_context *context = data;
guint32 id = wp_proxy_get_bound_id (WP_PROXY (obj));
guint32 node_id = -1;
gboolean is_default = (context->default_node == id);
const gchar *str, *name;
if ((str = wp_pipewire_object_get_property (obj, "node.id")))
node_id = atoi (str);
name = wp_pipewire_object_get_property (obj, "endpoint.description");
if (!name)
name = wp_pipewire_object_get_property (obj, "endpoint.name");
printf (TREE_INDENT_LINE "%c %4u. %-35s", is_default ? '*' : ' ', id, name);
print_controls (node_id, context);
}
static void
print_stream_node (const GValue *item, gpointer data)
{
@ -410,17 +388,6 @@ status_run (WpCtl * self)
printf (TREE_INDENT_LINE "\n");
printf (TREE_INDENT_NODE "Sink endpoints:\n");
child_it = wp_object_manager_new_filtered_iterator (self->om,
WP_TYPE_ENDPOINT,
WP_CONSTRAINT_TYPE_PW_PROPERTY, PW_KEY_MEDIA_CLASS, "#s", "*/Sink*",
WP_CONSTRAINT_TYPE_PW_PROPERTY, PW_KEY_MEDIA_CLASS, "#s", media_type_glob,
NULL);
wp_iterator_foreach (child_it, print_endpoint, (gpointer) &context);
g_clear_pointer (&child_it, wp_iterator_unref);
printf (TREE_INDENT_LINE "\n");
printf (TREE_INDENT_NODE "Sources:\n");
g_snprintf (media_class, sizeof(media_class), "%s/Source", media_type);
context.default_node = -1;
@ -437,17 +404,6 @@ status_run (WpCtl * self)
printf (TREE_INDENT_LINE "\n");
printf (TREE_INDENT_NODE "Source endpoints:\n");
child_it = wp_object_manager_new_filtered_iterator (self->om,
WP_TYPE_ENDPOINT,
WP_CONSTRAINT_TYPE_PW_PROPERTY, PW_KEY_MEDIA_CLASS, "#s", "*/Source*",
WP_CONSTRAINT_TYPE_PW_PROPERTY, PW_KEY_MEDIA_CLASS, "#s", media_type_glob,
NULL);
wp_iterator_foreach (child_it, print_endpoint, (gpointer) &context);
g_clear_pointer (&child_it, wp_iterator_unref);
printf (TREE_INDENT_LINE "\n");
printf (TREE_INDENT_END "Streams:\n");
child_it = wp_object_manager_new_filtered_iterator (self->om,
WP_TYPE_NODE,
@ -590,14 +546,6 @@ struct {
} assoc_keys[] = {
{ PW_KEY_CLIENT_ID, "Client" },
{ PW_KEY_DEVICE_ID, "Device" },
{ PW_KEY_ENDPOINT_CLIENT_ID, NULL },
{ "endpoint-link.id", "EndpointLink" },
{ PW_KEY_ENDPOINT_STREAM_ID, "EndpointStream" },
{ PW_KEY_ENDPOINT_LINK_OUTPUT_ENDPOINT, NULL },
{ PW_KEY_ENDPOINT_LINK_OUTPUT_STREAM, NULL },
{ PW_KEY_ENDPOINT_LINK_INPUT_ENDPOINT, NULL },
{ PW_KEY_ENDPOINT_LINK_INPUT_STREAM, NULL },
{ PW_KEY_ENDPOINT_ID, "Endpoint" },
{ PW_KEY_LINK_INPUT_NODE, NULL },
{ PW_KEY_LINK_INPUT_PORT, NULL },
{ PW_KEY_LINK_OUTPUT_NODE, NULL },
@ -894,7 +842,6 @@ set_volume_parse_positional (gint argc, gchar ** argv, GError **error)
static gboolean
set_volume_prepare (WpCtl * self, GError ** error)
{
wp_object_manager_add_interest (self->om, WP_TYPE_ENDPOINT, NULL);
wp_object_manager_add_interest (self->om, WP_TYPE_NODE, NULL);
wp_object_manager_add_interest (self->om, WP_TYPE_CLIENT, NULL);
wp_object_manager_request_object_features (self->om, WP_TYPE_GLOBAL_PROXY,
@ -912,15 +859,6 @@ do_set_volume (WpCtl * self, WpPipewireObject *proxy)
gdouble curr_volume = 1.0;
guint32 id = wp_proxy_get_bound_id (WP_PROXY (proxy));
if (WP_IS_ENDPOINT (proxy)) {
const gchar *str = wp_pipewire_object_get_property (proxy, "node.id");
if (!str) {
fprintf (stderr, "Endpoint '%d' does not have an associated node\n", id);
return FALSE;
}
id = atoi (str);
}
if (cmdline.set_volume.type == 's') {
g_signal_emit_by_name (mixer_api, "get-volume", id, &variant);
if (!variant) {
@ -1030,7 +968,6 @@ set_mute_parse_positional (gint argc, gchar ** argv, GError **error)
static gboolean
set_mute_prepare (WpCtl * self, GError ** error)
{
wp_object_manager_add_interest (self->om, WP_TYPE_ENDPOINT, NULL);
wp_object_manager_add_interest (self->om, WP_TYPE_NODE, NULL);
wp_object_manager_add_interest (self->om, WP_TYPE_CLIENT, NULL);
wp_object_manager_request_object_features (self->om, WP_TYPE_GLOBAL_PROXY,
@ -1048,15 +985,6 @@ do_set_mute (WpCtl * self, WpPipewireObject *proxy)
gboolean mute = FALSE;
guint32 id = wp_proxy_get_bound_id (WP_PROXY (proxy));
if (WP_IS_ENDPOINT (proxy)) {
const gchar *str = wp_pipewire_object_get_property (proxy, "node.id");
if (!str) {
fprintf (stderr, "Endpoint '%d' does not have an associated node\n", id);
return FALSE;
}
id = atoi (str);
}
g_signal_emit_by_name (mixer_api, "get-volume", id, &variant);
if (!variant) {
fprintf (stderr, "Node %d does not support mute\n", id);
@ -1309,7 +1237,7 @@ static const struct subcommand {
{
.name = "set-default",
.positional_args = "ID",
.summary = "Sets ID to be the default endpoint of its kind "
.summary = "Sets ID to be the default object of its kind "
"(capture/playback) in its session",
.description = NULL,
.entries = { { NULL } },

View file

@ -63,7 +63,6 @@ test_si_audio_endpoint_configure_activate (TestFixture * f,
endpoint = wp_session_item_make (f->base.core, "si-audio-endpoint");
g_assert_nonnull (endpoint);
g_assert_true (WP_IS_SI_ENDPOINT (endpoint));
/* configure endpoint */
@ -155,20 +154,14 @@ test_si_audio_endpoint_export (TestFixture * f, gconstpointer user_data)
}
{
g_autoptr (WpEndpoint) ep = NULL;
g_autoptr (WpNode) n = NULL;
g_autoptr (WpProperties) props = NULL;
ep = wp_session_item_get_associated_proxy (endpoint, WP_TYPE_ENDPOINT);
g_assert_nonnull (ep);
props = wp_pipewire_object_get_properties (WP_PIPEWIRE_OBJECT (ep));
n = wp_session_item_get_associated_proxy (endpoint, WP_TYPE_NODE);
g_assert_nonnull (n);
props = wp_pipewire_object_get_properties (WP_PIPEWIRE_OBJECT (n));
g_assert_nonnull (props);
g_assert_cmpstr (wp_endpoint_get_name (ep), ==, "endpoint");
g_assert_cmpstr (wp_endpoint_get_media_class (ep), ==,
"Audio/Source");
g_assert_cmpint (wp_endpoint_get_direction (ep), ==, WP_DIRECTION_OUTPUT);
g_assert_cmpstr (wp_properties_get (props, "endpoint.name"), ==,
"endpoint");
g_assert_cmpstr (wp_properties_get (props, "media.class"), ==,
"Audio/Source");
}

View file

@ -1,780 +0,0 @@
/* WirePlumber
*
* Copyright © 2019-2020 Collabora Ltd.
* @author George Kiagiadakis <george.kiagiadakis@collabora.com>
*
* SPDX-License-Identifier: MIT
*/
#include "../common/base-test-fixture.h"
#include <pipewire/extensions/session-manager/keys.h>
struct _TestSiEndpoint
{
WpSessionItem parent;
const gchar *name;
const gchar *media_class;
WpNode *node;
WpDirection direction;
gboolean changed_properties;
WpProxy *impl_endpoint;
};
G_DECLARE_FINAL_TYPE (TestSiEndpoint, test_si_endpoint,
TEST, SI_ENDPOINT, WpSessionItem)
static GVariant *
test_si_endpoint_get_registration_info (WpSiEndpoint * item)
{
TestSiEndpoint *self = TEST_SI_ENDPOINT (item);
GVariantBuilder b;
g_variant_builder_init (&b, G_VARIANT_TYPE ("(ssya{ss})"));
g_variant_builder_add (&b, "s", self->name);
g_variant_builder_add (&b, "s", self->media_class);
g_variant_builder_add (&b, "y", (guchar) self->direction);
g_variant_builder_add (&b, "a{ss}", NULL);
return g_variant_builder_end (&b);
}
static WpProperties *
test_si_endpoint_get_properties (WpSiEndpoint * item)
{
TestSiEndpoint *self = TEST_SI_ENDPOINT (item);
if (self->changed_properties)
return wp_properties_new ("test.property", "changed-value", NULL);
else
return wp_properties_new ("test.property", "test-value", NULL);
}
static void
test_si_endpoint_endpoint_init (WpSiEndpointInterface * iface)
{
iface->get_registration_info = test_si_endpoint_get_registration_info;
iface->get_properties = test_si_endpoint_get_properties;
}
G_DEFINE_TYPE_WITH_CODE (TestSiEndpoint, test_si_endpoint, WP_TYPE_SESSION_ITEM,
G_IMPLEMENT_INTERFACE (WP_TYPE_SI_ENDPOINT, test_si_endpoint_endpoint_init))
static void
test_si_endpoint_init (TestSiEndpoint * self)
{
}
static gpointer
si_endpoint_get_associated_proxy (WpSessionItem * item, GType proxy_type)
{
TestSiEndpoint * self = TEST_SI_ENDPOINT (item);
if (proxy_type == WP_TYPE_NODE)
return self->node ? g_object_ref (self->node) : NULL;
return NULL;
}
static void
si_endpoint_reset (WpSessionItem * item)
{
TestSiEndpoint * self = TEST_SI_ENDPOINT (item);
wp_object_deactivate (WP_OBJECT (self),
WP_SESSION_ITEM_FEATURE_ACTIVE | WP_SESSION_ITEM_FEATURE_EXPORTED);
g_clear_object (&self->node);
WP_SESSION_ITEM_CLASS (test_si_endpoint_parent_class)->reset (item);
}
static void
si_endpoint_disable_active (WpSessionItem *si)
{
TestSiEndpoint * self = TEST_SI_ENDPOINT (si);
wp_object_update_features (WP_OBJECT (self), 0,
WP_SESSION_ITEM_FEATURE_ACTIVE);
}
static void
si_endpoint_disable_exported (WpSessionItem *si)
{
TestSiEndpoint * self = TEST_SI_ENDPOINT (si);
g_clear_object (&self->impl_endpoint);
wp_object_update_features (WP_OBJECT (self), 0,
WP_SESSION_ITEM_FEATURE_EXPORTED);
}
static void
si_endpoint_enable_active (WpSessionItem *si, WpTransition *transition)
{
TestSiEndpoint * self = TEST_SI_ENDPOINT (si);
wp_object_update_features (WP_OBJECT (self),
WP_SESSION_ITEM_FEATURE_ACTIVE, 0);
}
static void
on_impl_endpoint_activated (WpObject * object, GAsyncResult * res,
WpTransition * transition)
{
TestSiEndpoint *self = wp_transition_get_source_object (transition);
g_autoptr (GError) error = NULL;
if (!wp_object_activate_finish (object, res, &error)) {
wp_transition_return_error (transition, g_steal_pointer (&error));
return;
}
wp_object_update_features (WP_OBJECT (self),
WP_SESSION_ITEM_FEATURE_EXPORTED, 0);
}
static void
si_endpoint_enable_exported (WpSessionItem *si, WpTransition *transition)
{
TestSiEndpoint * self = TEST_SI_ENDPOINT (si);
g_autoptr (WpCore) core = wp_object_get_core (WP_OBJECT (self));
self->impl_endpoint = WP_PROXY (wp_impl_endpoint_new (core,
WP_SI_ENDPOINT (self)));
g_signal_connect_object (self->impl_endpoint, "pw-proxy-destroyed",
G_CALLBACK (wp_session_item_handle_proxy_destroyed), self, 0);
wp_object_activate (WP_OBJECT (self->impl_endpoint),
WP_OBJECT_FEATURES_ALL, NULL,
(GAsyncReadyCallback) on_impl_endpoint_activated, transition);
}
static void
test_si_endpoint_class_init (TestSiEndpointClass * klass)
{
WpSessionItemClass *item_class = (WpSessionItemClass *) klass;
item_class->reset = si_endpoint_reset;
item_class->get_associated_proxy = si_endpoint_get_associated_proxy;
item_class->disable_active = si_endpoint_disable_active;
item_class->disable_exported = si_endpoint_disable_exported;
item_class->enable_active = si_endpoint_enable_active;
item_class->enable_exported = si_endpoint_enable_exported;
}
/*******************/
typedef struct {
WpBaseTestFixture base;
WpObjectManager *export_om;
WpObjectManager *proxy_om;
WpProxy *impl_endpoint;
WpProxy *proxy_endpoint;
gint n_events;
} TestEndpointFixture;
static void
test_endpoint_setup (TestEndpointFixture *self, gconstpointer user_data)
{
wp_base_test_fixture_setup (&self->base, WP_BASE_TEST_FLAG_CLIENT_CORE);
self->export_om = wp_object_manager_new ();
self->proxy_om = wp_object_manager_new ();
}
static void
test_endpoint_teardown (TestEndpointFixture *self, gconstpointer user_data)
{
g_clear_object (&self->proxy_om);
g_clear_object (&self->export_om);
wp_base_test_fixture_teardown (&self->base);
}
static void
test_endpoint_impl_object_added (WpObjectManager *om,
WpEndpoint *endpoint, TestEndpointFixture *fixture)
{
g_debug ("impl object added");
g_assert_true (WP_IS_ENDPOINT (endpoint));
g_assert_cmpstr (G_OBJECT_TYPE_NAME (endpoint), ==, "WpImplEndpoint");
g_assert_null (fixture->impl_endpoint);
fixture->impl_endpoint = WP_PROXY (endpoint);
if (++fixture->n_events == 3)
g_main_loop_quit (fixture->base.loop);
}
static void
test_endpoint_impl_object_removed (WpObjectManager *om,
WpEndpoint *endpoint, TestEndpointFixture *fixture)
{
g_debug ("impl object removed");
g_assert_true (WP_IS_ENDPOINT (endpoint));
g_assert_cmpstr (G_OBJECT_TYPE_NAME (endpoint), ==, "WpImplEndpoint");
g_assert_nonnull (fixture->impl_endpoint);
fixture->impl_endpoint = NULL;
if (++fixture->n_events == 2)
g_main_loop_quit (fixture->base.loop);
}
static void
test_endpoint_proxy_object_added (WpObjectManager *om,
WpEndpoint *endpoint, TestEndpointFixture *fixture)
{
g_debug ("proxy object added");
g_assert_true (WP_IS_ENDPOINT (endpoint));
g_assert_cmpstr (G_OBJECT_TYPE_NAME (endpoint), ==, "WpEndpoint");
g_assert_null (fixture->proxy_endpoint);
fixture->proxy_endpoint = WP_PROXY (endpoint);
if (++fixture->n_events == 3)
g_main_loop_quit (fixture->base.loop);
}
static void
test_endpoint_proxy_object_removed (WpObjectManager *om,
WpEndpoint *endpoint, TestEndpointFixture *fixture)
{
g_debug ("proxy object removed");
g_assert_true (WP_IS_ENDPOINT (endpoint));
g_assert_cmpstr (G_OBJECT_TYPE_NAME (endpoint), ==, "WpEndpoint");
g_assert_nonnull (fixture->proxy_endpoint);
fixture->proxy_endpoint = NULL;
if (++fixture->n_events == 2)
g_main_loop_quit (fixture->base.loop);
}
static void
test_endpoint_activate_done (WpObject * object, GAsyncResult * res,
TestEndpointFixture *fixture)
{
g_autoptr (GError) error = NULL;
g_debug ("activate done");
g_assert_true (wp_object_activate_finish (object, res, &error));
g_assert_no_error (error);
if (++fixture->n_events == 3)
g_main_loop_quit (fixture->base.loop);
}
static void
test_endpoint_params_changed (WpPipewireObject * proxy,
const gchar * param_name, TestEndpointFixture *fixture)
{
wp_debug_object (proxy, "params changed: %s", param_name);
/* only count changes of id 2 (Props); PipeWire 0.3.22+git changed
behaviour and emits changes to PropInfo as well then the Props change */
if (!g_strcmp0 (param_name, "Props") && ++fixture->n_events == 3)
g_main_loop_quit (fixture->base.loop);
}
static void
test_endpoint_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->base.loop);
}
static void
test_endpoint_no_props (TestEndpointFixture *fixture, gconstpointer data)
{
g_autoptr (TestSiEndpoint) endpoint = NULL;
/* set up the export side */
g_signal_connect (fixture->export_om, "object-added",
(GCallback) test_endpoint_impl_object_added, fixture);
g_signal_connect (fixture->export_om, "object-removed",
(GCallback) test_endpoint_impl_object_removed, fixture);
wp_object_manager_add_interest (fixture->export_om, WP_TYPE_ENDPOINT, NULL);
wp_object_manager_request_object_features (fixture->export_om,
WP_TYPE_ENDPOINT, WP_OBJECT_FEATURES_ALL);
wp_core_install_object_manager (fixture->base.core, fixture->export_om);
/* set up the proxy side */
g_signal_connect (fixture->proxy_om, "object-added",
(GCallback) test_endpoint_proxy_object_added, fixture);
g_signal_connect (fixture->proxy_om, "object-removed",
(GCallback) test_endpoint_proxy_object_removed, fixture);
wp_object_manager_add_interest (fixture->proxy_om, WP_TYPE_ENDPOINT, NULL);
wp_object_manager_request_object_features (fixture->proxy_om,
WP_TYPE_ENDPOINT, WP_OBJECT_FEATURES_ALL);
wp_core_install_object_manager (fixture->base.client_core, fixture->proxy_om);
/* create endpoint */
endpoint = g_object_new (test_si_endpoint_get_type (),
"core", fixture->base.core, NULL);
endpoint->name = "test-endpoint";
endpoint->media_class = "Audio/Source";
endpoint->direction = WP_DIRECTION_OUTPUT;
wp_object_activate (WP_OBJECT (endpoint),
WP_SESSION_ITEM_FEATURE_ACTIVE | WP_SESSION_ITEM_FEATURE_EXPORTED,
NULL, (GAsyncReadyCallback) test_endpoint_activate_done, fixture);
/* run until objects are created and features are cached */
fixture->n_events = 0;
g_main_loop_run (fixture->base.loop);
g_assert_cmpint (wp_object_get_active_features (WP_OBJECT (endpoint)), ==,
WP_SESSION_ITEM_FEATURE_ACTIVE | WP_SESSION_ITEM_FEATURE_EXPORTED);
g_assert_cmpint (fixture->n_events, ==, 3);
g_assert_nonnull (fixture->impl_endpoint);
g_assert_nonnull (fixture->proxy_endpoint);
/* verify the values on the proxy */
g_assert_cmphex (
wp_object_get_active_features (WP_OBJECT (fixture->proxy_endpoint)), ==,
wp_object_get_supported_features (WP_OBJECT (fixture->proxy_endpoint)));
g_assert_cmpuint (wp_proxy_get_bound_id (fixture->proxy_endpoint), ==,
wp_proxy_get_bound_id (fixture->impl_endpoint));
g_assert_cmpstr (wp_pipewire_object_get_property (
WP_PIPEWIRE_OBJECT (fixture->proxy_endpoint), "test.property"),
==, "test-value");
{
g_autoptr (WpProperties) props = wp_global_proxy_get_global_properties (
WP_GLOBAL_PROXY (fixture->proxy_endpoint));
g_assert_cmpstr (wp_properties_get (props, PW_KEY_ENDPOINT_NAME), ==,
"test-endpoint");
g_assert_cmpstr (wp_properties_get (props, PW_KEY_MEDIA_CLASS), ==,
"Audio/Source");
}
g_assert_cmpstr ("test-endpoint", ==,
wp_endpoint_get_name (WP_ENDPOINT (fixture->proxy_endpoint)));
g_assert_cmpstr ("Audio/Source", ==,
wp_endpoint_get_media_class (WP_ENDPOINT (fixture->proxy_endpoint)));
g_assert_cmpint (WP_DIRECTION_OUTPUT, ==,
wp_endpoint_get_direction (WP_ENDPOINT (fixture->proxy_endpoint)));
/* test property changes */
g_signal_connect (fixture->proxy_endpoint, "notify::properties",
(GCallback) test_endpoint_notify_properties, fixture);
g_signal_connect (fixture->impl_endpoint, "notify::properties",
(GCallback) test_endpoint_notify_properties, fixture);
/* change a property on the impl */
fixture->n_events = 0;
endpoint->changed_properties = TRUE;
g_signal_emit_by_name (endpoint, "endpoint-properties-changed");
/* run until the change is on both sides */
g_main_loop_run (fixture->base.loop);
g_assert_cmpint (fixture->n_events, ==, 2);
/* verify the property change on both sides */
{
g_autoptr (WpProperties) props = wp_pipewire_object_get_properties (
WP_PIPEWIRE_OBJECT (fixture->impl_endpoint));
g_assert_cmpstr (wp_properties_get (props, "test.property"), ==,
"changed-value");
}
{
g_autoptr (WpProperties) props = wp_pipewire_object_get_properties (
WP_PIPEWIRE_OBJECT (fixture->proxy_endpoint));
g_assert_cmpstr (wp_properties_get (props, "test.property"), ==,
"changed-value");
}
/* destroy impl endpoint */
fixture->n_events = 0;
g_clear_object (&endpoint);
/* run until objects are destroyed */
g_main_loop_run (fixture->base.loop);
g_assert_cmpint (fixture->n_events, ==, 2);
g_assert_null (fixture->impl_endpoint);
g_assert_null (fixture->proxy_endpoint);
}
static void
test_endpoint_with_props (TestEndpointFixture *fixture, gconstpointer data)
{
g_autoptr (TestSiEndpoint) endpoint = NULL;
/* load modules */
{
g_autoptr (WpTestServerLocker) lock =
wp_test_server_locker_new (&fixture->base.server);
g_assert_cmpint (pw_context_add_spa_lib (fixture->base.server.context,
"audiotestsrc", "audiotestsrc/libspa-audiotestsrc"), ==, 0);
if (!test_is_spa_lib_installed (&fixture->base, "audiotestsrc")) {
g_test_skip ("The pipewire audiotestsrc factory was not found");
return;
}
g_assert_nonnull (pw_context_load_module (fixture->base.server.context,
"libpipewire-module-adapter", NULL, NULL));
}
/* set up the export side */
g_signal_connect (fixture->export_om, "object-added",
(GCallback) test_endpoint_impl_object_added, fixture);
g_signal_connect (fixture->export_om, "object-removed",
(GCallback) test_endpoint_impl_object_removed, fixture);
wp_object_manager_add_interest (fixture->export_om, WP_TYPE_ENDPOINT, NULL);
wp_object_manager_request_object_features (fixture->export_om,
WP_TYPE_ENDPOINT, WP_OBJECT_FEATURES_ALL);
wp_core_install_object_manager (fixture->base.core, fixture->export_om);
/* set up the proxy side */
g_signal_connect (fixture->proxy_om, "object-added",
(GCallback) test_endpoint_proxy_object_added, fixture);
g_signal_connect (fixture->proxy_om, "object-removed",
(GCallback) test_endpoint_proxy_object_removed, fixture);
wp_object_manager_add_interest (fixture->proxy_om, WP_TYPE_ENDPOINT, NULL);
wp_object_manager_request_object_features (fixture->proxy_om,
WP_TYPE_ENDPOINT, WP_OBJECT_FEATURES_ALL);
wp_core_install_object_manager (fixture->base.client_core, fixture->proxy_om);
/* create endpoint */
endpoint = g_object_new (test_si_endpoint_get_type (),
"core", fixture->base.core, NULL);
endpoint->name = "test-endpoint";
endpoint->media_class = "Audio/Source";
endpoint->direction = WP_DIRECTION_OUTPUT;
/* associate a node that has props */
endpoint->node = wp_node_new_from_factory (fixture->base.core,
"adapter",
wp_properties_new (
"factory.name", "audiotestsrc",
"node.name", "audiotestsrc.adapter",
NULL));
g_assert_nonnull (endpoint->node);
wp_object_activate (WP_OBJECT (endpoint->node),
WP_PIPEWIRE_OBJECT_FEATURES_MINIMAL, NULL,
(GAsyncReadyCallback) test_object_activate_finish_cb, fixture);
g_main_loop_run (fixture->base.loop);
g_assert_cmpint (wp_object_get_active_features (WP_OBJECT (endpoint->node)),
==, WP_PIPEWIRE_OBJECT_FEATURES_MINIMAL);
/* activate & export the endpoint */
wp_object_activate (WP_OBJECT (endpoint),
WP_SESSION_ITEM_FEATURE_ACTIVE | WP_SESSION_ITEM_FEATURE_EXPORTED,
NULL, (GAsyncReadyCallback) test_endpoint_activate_done, fixture);
/* run until objects are created and features are cached */
fixture->n_events = 0;
g_main_loop_run (fixture->base.loop);
g_assert_cmpint (wp_object_get_active_features (WP_OBJECT (endpoint)), ==,
WP_SESSION_ITEM_FEATURE_ACTIVE | WP_SESSION_ITEM_FEATURE_EXPORTED);
g_assert_cmpint (fixture->n_events, ==, 3);
g_assert_nonnull (fixture->impl_endpoint);
g_assert_nonnull (fixture->proxy_endpoint);
/* verify features; the endpoint must have also augmented the node */
g_assert_cmpint (
wp_object_get_active_features (WP_OBJECT (endpoint->node)), &,
WP_PIPEWIRE_OBJECT_FEATURES_MINIMAL | WP_PIPEWIRE_OBJECT_FEATURE_PARAM_PROPS);
g_assert_cmphex (
wp_object_get_active_features (WP_OBJECT (fixture->proxy_endpoint)), &,
WP_PIPEWIRE_OBJECT_FEATURES_MINIMAL | WP_PIPEWIRE_OBJECT_FEATURE_PARAM_PROPS);
/* verify props */
{
g_autoptr (WpIterator) iterator = NULL;
g_auto (GValue) item = G_VALUE_INIT;
g_autoptr (WpSpaPod) pod = NULL;
gfloat float_value = 0.0f;
gboolean boolean_value = TRUE;
iterator = wp_pipewire_object_enum_params_sync (
WP_PIPEWIRE_OBJECT (fixture->proxy_endpoint), "Props", NULL);
g_assert_nonnull (iterator);
g_assert_true (wp_iterator_next (iterator, &item));
g_assert_nonnull ((pod = g_value_dup_boxed (&item)));
g_assert_true (wp_spa_pod_get_object (pod, NULL,
"volume", "f", &float_value,
"mute", "b", &boolean_value,
NULL));
g_assert_cmpfloat_with_epsilon (float_value, 1.0f, 0.001);
g_assert_cmpint (boolean_value, ==, FALSE);
}
/* setup change signals */
g_signal_connect (fixture->proxy_endpoint, "params-changed",
(GCallback) test_endpoint_params_changed, fixture);
g_signal_connect (fixture->impl_endpoint, "params-changed",
(GCallback) test_endpoint_params_changed, fixture);
g_signal_connect (endpoint->node, "params-changed",
(GCallback) test_endpoint_params_changed, fixture);
/* change control on the proxy */
fixture->n_events = 0;
wp_pipewire_object_set_param (WP_PIPEWIRE_OBJECT (fixture->proxy_endpoint),
"Props", 0,
wp_spa_pod_new_object ("Spa:Pod:Object:Param:Props", "Props",
"volume", "f", 0.7f, NULL));
/* run until the change is on all sides */
g_main_loop_run (fixture->base.loop);
g_assert_cmpint (fixture->n_events, ==, 3);
/* verify the value change on all sides */
{
g_autoptr (WpIterator) iterator = NULL;
g_auto (GValue) item = G_VALUE_INIT;
g_autoptr (WpSpaPod) pod = NULL;
gfloat float_value = 0.0f;
gboolean boolean_value = TRUE;
iterator = wp_pipewire_object_enum_params_sync (
WP_PIPEWIRE_OBJECT (fixture->proxy_endpoint), "Props", NULL);
g_assert_nonnull (iterator);
g_assert_true (wp_iterator_next (iterator, &item));
pod = g_value_dup_boxed (&item);
g_assert_nonnull (pod);
g_assert_true (wp_spa_pod_get_object (pod, NULL,
"volume", "f", &float_value,
"mute", "b", &boolean_value,
NULL));
g_assert_cmpfloat_with_epsilon (float_value, 0.7f, 0.001);
g_assert_cmpint (boolean_value, ==, FALSE);
}
{
g_autoptr (WpIterator) iterator = NULL;
g_auto (GValue) item = G_VALUE_INIT;
g_autoptr (WpSpaPod) pod = NULL;
gfloat float_value = 0.0f;
gboolean boolean_value = TRUE;
iterator = wp_pipewire_object_enum_params_sync (
WP_PIPEWIRE_OBJECT (fixture->impl_endpoint), "Props", NULL);
g_assert_nonnull (iterator);
g_assert_true (wp_iterator_next (iterator, &item));
g_assert_nonnull ((pod = g_value_dup_boxed (&item)));
g_assert_true (wp_spa_pod_get_object (pod, NULL,
"volume", "f", &float_value,
"mute", "b", &boolean_value,
NULL));
g_assert_cmpfloat_with_epsilon (float_value, 0.7f, 0.001);
g_assert_cmpint (boolean_value, ==, FALSE);
}
{
g_autoptr (WpIterator) iterator = NULL;
g_auto (GValue) item = G_VALUE_INIT;
g_autoptr (WpSpaPod) pod = NULL;
gfloat float_value = 0.0f;
gboolean boolean_value = TRUE;
iterator = wp_pipewire_object_enum_params_sync (
WP_PIPEWIRE_OBJECT (endpoint->node), "Props", NULL);
g_assert_nonnull (iterator);
g_assert_true (wp_iterator_next (iterator, &item));
g_assert_nonnull ((pod = g_value_dup_boxed (&item)));
g_assert_true (wp_spa_pod_get_object (pod, NULL,
"volume", "f", &float_value,
"mute", "b", &boolean_value,
NULL));
g_assert_cmpfloat_with_epsilon (float_value, 0.7f, 0.001);
g_assert_cmpint (boolean_value, ==, FALSE);
}
/* change control on the impl */
fixture->n_events = 0;
wp_pipewire_object_set_param (WP_PIPEWIRE_OBJECT (fixture->impl_endpoint),
"Props", 0,
wp_spa_pod_new_object ("Spa:Pod:Object:Param:Props", "Props",
"mute", "b", TRUE, NULL));
/* run until the change is on all sides */
g_main_loop_run (fixture->base.loop);
g_assert_cmpint (fixture->n_events, ==, 3);
/* verify the value change on all sides */
{
g_autoptr (WpIterator) iterator = NULL;
g_auto (GValue) item = G_VALUE_INIT;
g_autoptr (WpSpaPod) pod = NULL;
gfloat float_value = 0.0f;
gboolean boolean_value = TRUE;
iterator = wp_pipewire_object_enum_params_sync (
WP_PIPEWIRE_OBJECT (fixture->proxy_endpoint), "Props", NULL);
g_assert_nonnull (iterator);
g_assert_true (wp_iterator_next (iterator, &item));
pod = g_value_dup_boxed (&item);
g_assert_nonnull (pod);
g_assert_true (wp_spa_pod_get_object (pod, NULL,
"volume", "f", &float_value,
"mute", "b", &boolean_value,
NULL));
g_assert_cmpfloat_with_epsilon (float_value, 0.7f, 0.001);
g_assert_cmpint (boolean_value, ==, TRUE);
}
{
g_autoptr (WpIterator) iterator = NULL;
g_auto (GValue) item = G_VALUE_INIT;
g_autoptr (WpSpaPod) pod = NULL;
gfloat float_value = 0.0f;
gboolean boolean_value = TRUE;
iterator = wp_pipewire_object_enum_params_sync (
WP_PIPEWIRE_OBJECT (fixture->impl_endpoint), "Props", NULL);
g_assert_nonnull (iterator);
g_assert_true (wp_iterator_next (iterator, &item));
g_assert_nonnull ((pod = g_value_dup_boxed (&item)));
g_assert_true (wp_spa_pod_get_object (pod, NULL,
"volume", "f", &float_value,
"mute", "b", &boolean_value,
NULL));
g_assert_cmpfloat_with_epsilon (float_value, 0.7f, 0.001);
g_assert_cmpint (boolean_value, ==, TRUE);
}
{
g_autoptr (WpIterator) iterator = NULL;
g_auto (GValue) item = G_VALUE_INIT;
g_autoptr (WpSpaPod) pod = NULL;
gfloat float_value = 0.0f;
gboolean boolean_value = TRUE;
iterator = wp_pipewire_object_enum_params_sync (
WP_PIPEWIRE_OBJECT (endpoint->node), "Props", NULL);
g_assert_nonnull (iterator);
g_assert_true (wp_iterator_next (iterator, &item));
g_assert_nonnull ((pod = g_value_dup_boxed (&item)));
g_assert_true (wp_spa_pod_get_object (pod, NULL,
"volume", "f", &float_value,
"mute", "b", &boolean_value,
NULL));
g_assert_cmpfloat_with_epsilon (float_value, 0.7f, 0.001);
g_assert_cmpint (boolean_value, ==, TRUE);
}
/* change control on the node */
fixture->n_events = 0;
wp_pipewire_object_set_param (WP_PIPEWIRE_OBJECT (endpoint->node),
"Props", 0,
wp_spa_pod_new_object ("Spa:Pod:Object:Param:Props", "Props",
"volume", "f", 0.2f, NULL));
/* run until the change is on all sides */
g_main_loop_run (fixture->base.loop);
g_assert_cmpint (fixture->n_events, ==, 3);
/* verify the value change on all sides */
{
g_autoptr (WpIterator) iterator = NULL;
g_auto (GValue) item = G_VALUE_INIT;
g_autoptr (WpSpaPod) pod = NULL;
gfloat float_value = 0.0f;
gboolean boolean_value = TRUE;
iterator = wp_pipewire_object_enum_params_sync (
WP_PIPEWIRE_OBJECT (fixture->proxy_endpoint), "Props", NULL);
g_assert_nonnull (iterator);
g_assert_true (wp_iterator_next (iterator, &item));
g_assert_nonnull ((pod = g_value_dup_boxed (&item)));
g_assert_true (wp_spa_pod_get_object (pod, NULL,
"volume", "f", &float_value,
"mute", "b", &boolean_value,
NULL));
g_assert_cmpfloat_with_epsilon (float_value, 0.2f, 0.001);
g_assert_cmpint (boolean_value, ==, TRUE);
}
{
g_autoptr (WpIterator) iterator = NULL;
g_auto (GValue) item = G_VALUE_INIT;
g_autoptr (WpSpaPod) pod = NULL;
gfloat float_value = 0.0f;
gboolean boolean_value = TRUE;
iterator = wp_pipewire_object_enum_params_sync (
WP_PIPEWIRE_OBJECT (fixture->impl_endpoint), "Props", NULL);
g_assert_nonnull (iterator);
g_assert_true (wp_iterator_next (iterator, &item));
g_assert_nonnull ((pod = g_value_dup_boxed (&item)));
g_assert_true (wp_spa_pod_get_object (pod, NULL,
"volume", "f", &float_value,
"mute", "b", &boolean_value,
NULL));
g_assert_cmpfloat_with_epsilon (float_value, 0.2f, 0.001);
g_assert_cmpint (boolean_value, ==, TRUE);
}
{
g_autoptr (WpIterator) iterator = NULL;
g_auto (GValue) item = G_VALUE_INIT;
g_autoptr (WpSpaPod) pod = NULL;
gfloat float_value = 0.0f;
gboolean boolean_value = TRUE;
iterator = wp_pipewire_object_enum_params_sync (
WP_PIPEWIRE_OBJECT (endpoint->node), "Props", NULL);
g_assert_nonnull (iterator);
g_assert_true (wp_iterator_next (iterator, &item));
g_assert_nonnull ((pod = g_value_dup_boxed (&item)));
g_assert_true (wp_spa_pod_get_object (pod, NULL,
"volume", "f", &float_value,
"mute", "b", &boolean_value,
NULL));
g_assert_cmpfloat_with_epsilon (float_value, 0.2f, 0.001);
g_assert_cmpint (boolean_value, ==, TRUE);
}
/* destroy impl endpoint */
fixture->n_events = 0;
g_clear_object (&endpoint);
/* run until objects are destroyed */
g_main_loop_run (fixture->base.loop);
g_assert_cmpint (fixture->n_events, ==, 2);
g_assert_null (fixture->impl_endpoint);
g_assert_null (fixture->proxy_endpoint);
}
gint
main (gint argc, gchar *argv[])
{
g_test_init (&argc, &argv, NULL);
wp_init (WP_INIT_ALL);
g_test_add ("/wp/endpoint/no-props", TestEndpointFixture, NULL,
test_endpoint_setup, test_endpoint_no_props, test_endpoint_teardown);
g_test_add ("/wp/endpoint/with-props", TestEndpointFixture, NULL,
test_endpoint_setup, test_endpoint_with_props, test_endpoint_teardown);
return g_test_run ();
}

View file

@ -14,13 +14,6 @@ test(
env: common_env,
)
test(
'test-endpoint',
executable('test-endpoint', 'endpoint.c',
dependencies: common_deps, c_args: common_args),
env: common_env,
)
test(
'test-events',
executable('test-events', 'events.c',