mirror of
https://gitlab.freedesktop.org/pipewire/wireplumber.git
synced 2026-05-08 12:48:03 +02:00
session-item / endpoint: implement exporting a WpSiEndpoint
* introduces API to export session items * introduces small changes in the WpSiEndpoint & WpSiStream interfaces to make it nicer to work with * ports WpImplEndpoint to use PW_TYPE_INTERFACE_Endpoint to export. Depends on: https://gitlab.freedesktop.org/pipewire/pipewire/-/merge_requests/246 (was merged after 0.3.2)
This commit is contained in:
parent
763156e9f8
commit
405e8ba0d5
10 changed files with 800 additions and 574 deletions
|
|
@ -22,14 +22,18 @@
|
|||
*/
|
||||
|
||||
#include "endpoint.h"
|
||||
#include "session.h"
|
||||
#include "private.h"
|
||||
#include "error.h"
|
||||
#include "wpenums.h"
|
||||
|
||||
#include <pipewire/pipewire.h>
|
||||
#include <pipewire/extensions/session-manager.h>
|
||||
#include <pipewire/extensions/session-manager/introspect-funcs.h>
|
||||
|
||||
#include <spa/pod/builder.h>
|
||||
#include <spa/pod/parser.h>
|
||||
#include <spa/pod/filter.h>
|
||||
|
||||
enum {
|
||||
SIGNAL_CONTROL_CHANGED,
|
||||
|
|
@ -38,64 +42,6 @@ enum {
|
|||
|
||||
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);
|
||||
}
|
||||
|
||||
/* WpEndpoint */
|
||||
|
||||
typedef struct _WpEndpointPrivate WpEndpointPrivate;
|
||||
|
|
@ -104,6 +50,7 @@ struct _WpEndpointPrivate
|
|||
WpProperties *properties;
|
||||
WpSpaProps spa_props;
|
||||
struct pw_endpoint_info *info;
|
||||
struct pw_endpoint *iface;
|
||||
struct spa_hook listener;
|
||||
};
|
||||
|
||||
|
|
@ -120,8 +67,8 @@ wp_endpoint_finalize (GObject * object)
|
|||
WpEndpoint *self = WP_ENDPOINT (object);
|
||||
WpEndpointPrivate *priv = wp_endpoint_get_instance_private (self);
|
||||
|
||||
g_clear_pointer (&priv->info, endpoint_info_free);
|
||||
g_clear_pointer (&priv->properties, wp_properties_unref);
|
||||
g_clear_pointer (&priv->info, pw_endpoint_info_free);
|
||||
wp_spa_props_clear (&priv->spa_props);
|
||||
|
||||
G_OBJECT_CLASS (wp_endpoint_parent_class)->finalize (object);
|
||||
|
|
@ -168,12 +115,12 @@ static gint
|
|||
wp_endpoint_enum_params (WpProxy * self, guint32 id, guint32 start,
|
||||
guint32 num, const struct spa_pod *filter)
|
||||
{
|
||||
struct pw_endpoint *pwp;
|
||||
WpEndpointPrivate *priv =
|
||||
wp_endpoint_get_instance_private (WP_ENDPOINT (self));
|
||||
int endpoint_enum_params_result;
|
||||
|
||||
pwp = (struct pw_endpoint *) wp_proxy_get_pw_proxy (self);
|
||||
endpoint_enum_params_result = pw_endpoint_enum_params (pwp, 0, id, start, num,
|
||||
filter);
|
||||
endpoint_enum_params_result = pw_endpoint_enum_params (priv->iface, 0, id,
|
||||
start, num, filter);
|
||||
g_warn_if_fail (endpoint_enum_params_result >= 0);
|
||||
|
||||
return endpoint_enum_params_result;
|
||||
|
|
@ -182,12 +129,12 @@ wp_endpoint_enum_params (WpProxy * self, guint32 id, guint32 start,
|
|||
static gint
|
||||
wp_endpoint_subscribe_params (WpProxy * self, guint32 n_ids, guint32 *ids)
|
||||
{
|
||||
struct pw_endpoint *pwp;
|
||||
WpEndpointPrivate *priv =
|
||||
wp_endpoint_get_instance_private (WP_ENDPOINT (self));
|
||||
int endpoint_subscribe_params_result;
|
||||
|
||||
pwp = (struct pw_endpoint *) wp_proxy_get_pw_proxy (self);
|
||||
endpoint_subscribe_params_result = pw_endpoint_subscribe_params (pwp, ids,
|
||||
n_ids);
|
||||
endpoint_subscribe_params_result = pw_endpoint_subscribe_params (priv->iface,
|
||||
ids, n_ids);
|
||||
g_warn_if_fail (endpoint_subscribe_params_result >= 0);
|
||||
|
||||
return endpoint_subscribe_params_result;
|
||||
|
|
@ -197,11 +144,12 @@ static gint
|
|||
wp_endpoint_set_param (WpProxy * self, guint32 id, guint32 flags,
|
||||
const struct spa_pod *param)
|
||||
{
|
||||
struct pw_endpoint *pwp;
|
||||
WpEndpointPrivate *priv =
|
||||
wp_endpoint_get_instance_private (WP_ENDPOINT (self));
|
||||
int endpoint_set_param_result;
|
||||
|
||||
pwp = (struct pw_endpoint *) wp_proxy_get_pw_proxy (self);
|
||||
endpoint_set_param_result = pw_endpoint_set_param (pwp, id, flags, param);
|
||||
endpoint_set_param_result = pw_endpoint_set_param (priv->iface, id, flags,
|
||||
param);
|
||||
g_warn_if_fail (endpoint_set_param_result >= 0);
|
||||
|
||||
return endpoint_set_param_result;
|
||||
|
|
@ -213,9 +161,14 @@ endpoint_event_info (void *data, const struct pw_endpoint_info *info)
|
|||
WpEndpoint *self = WP_ENDPOINT (data);
|
||||
WpEndpointPrivate *priv = wp_endpoint_get_instance_private (self);
|
||||
|
||||
priv->info = endpoint_info_update (priv->info, &priv->properties, info);
|
||||
wp_proxy_set_feature_ready (WP_PROXY (self), WP_PROXY_FEATURE_INFO);
|
||||
priv->info = pw_endpoint_info_update (priv->info, info);
|
||||
|
||||
if (info->change_mask & PW_ENDPOINT_CHANGE_MASK_PROPS) {
|
||||
g_clear_pointer (&priv->properties, wp_properties_unref);
|
||||
priv->properties = wp_properties_new_wrap_dict (priv->info->props);
|
||||
}
|
||||
|
||||
wp_proxy_set_feature_ready (WP_PROXY (self), WP_PROXY_FEATURE_INFO);
|
||||
g_object_notify (G_OBJECT (self), "info");
|
||||
|
||||
if (info->change_mask & PW_ENDPOINT_CHANGE_MASK_PROPS)
|
||||
|
|
@ -234,8 +187,9 @@ wp_endpoint_pw_proxy_created (WpProxy * proxy, struct pw_proxy * pw_proxy)
|
|||
WpEndpoint *self = WP_ENDPOINT (proxy);
|
||||
WpEndpointPrivate *priv = wp_endpoint_get_instance_private (self);
|
||||
|
||||
pw_endpoint_add_listener ((struct pw_endpoint *) pw_proxy,
|
||||
&priv->listener, &endpoint_events, self);
|
||||
priv->iface = (struct pw_endpoint *) pw_proxy;
|
||||
pw_endpoint_add_listener (priv->iface, &priv->listener, &endpoint_events,
|
||||
self);
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
@ -300,17 +254,10 @@ set_control (WpEndpoint * self, guint32 control_id,
|
|||
{
|
||||
char buf[1024];
|
||||
struct spa_pod_builder b = SPA_POD_BUILDER_INIT (buf, sizeof (buf));
|
||||
struct pw_endpoint *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 */
|
||||
/* our spa_props will be updated by the param event */
|
||||
|
||||
pw_proxy = (struct pw_endpoint *) wp_proxy_get_pw_proxy (WP_PROXY (self));
|
||||
if (!pw_proxy)
|
||||
return FALSE;
|
||||
|
||||
pw_endpoint_set_param (pw_proxy,
|
||||
SPA_PARAM_Props, 0,
|
||||
WP_PROXY_GET_CLASS (self)->set_param (WP_PROXY (self), SPA_PARAM_Props, 0,
|
||||
spa_pod_builder_add_object (&b,
|
||||
SPA_TYPE_OBJECT_Props, SPA_PARAM_Props,
|
||||
control_id, SPA_POD_Pod (pod)));
|
||||
|
|
@ -546,131 +493,323 @@ wp_endpoint_set_control_float (WpEndpoint * self, guint32 control_id,
|
|||
|
||||
/* WpImplEndpoint */
|
||||
|
||||
typedef struct _WpImplEndpointPrivate WpImplEndpointPrivate;
|
||||
struct _WpImplEndpointPrivate
|
||||
{
|
||||
WpEndpointPrivate *pp;
|
||||
struct pw_endpoint_info info;
|
||||
struct spa_param_info param_info[2];
|
||||
enum {
|
||||
IMPL_PROP_0,
|
||||
IMPL_PROP_ITEM,
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE_WITH_PRIVATE (WpImplEndpoint, wp_impl_endpoint, WP_TYPE_ENDPOINT)
|
||||
struct _WpImplEndpoint
|
||||
{
|
||||
WpEndpoint parent;
|
||||
|
||||
struct spa_interface iface;
|
||||
struct spa_hook_list hooks;
|
||||
struct pw_endpoint_info info;
|
||||
gboolean subscribed;
|
||||
|
||||
WpSiEndpoint *item;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE (WpImplEndpoint, wp_impl_endpoint, WP_TYPE_ENDPOINT)
|
||||
|
||||
#define pw_endpoint_emit(hooks,method,version,...) \
|
||||
spa_hook_list_call_simple(hooks, struct pw_endpoint_events, \
|
||||
method, version, ##__VA_ARGS__)
|
||||
|
||||
#define pw_endpoint_emit_info(hooks,...) pw_endpoint_emit(hooks, info, 0, ##__VA_ARGS__)
|
||||
#define pw_endpoint_emit_param(hooks,...) pw_endpoint_emit(hooks, param, 0, ##__VA_ARGS__)
|
||||
|
||||
static int
|
||||
impl_add_listener(void *object,
|
||||
struct spa_hook *listener,
|
||||
const struct pw_endpoint_events *events,
|
||||
void *data)
|
||||
{
|
||||
WpImplEndpoint *self = WP_IMPL_ENDPOINT (object);
|
||||
struct spa_hook_list save;
|
||||
|
||||
spa_hook_list_isolate (&self->hooks, &save, listener, events, data);
|
||||
pw_endpoint_emit_info (&self->hooks, &self->info);
|
||||
spa_hook_list_join (&self->hooks, &save);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
impl_enum_params (void *object, int seq,
|
||||
uint32_t id, uint32_t start, uint32_t num,
|
||||
const struct spa_pod *filter)
|
||||
{
|
||||
WpImplEndpoint *self = WP_IMPL_ENDPOINT (object);
|
||||
WpEndpointPrivate *priv =
|
||||
wp_endpoint_get_instance_private (WP_ENDPOINT (self));
|
||||
char buf[1024];
|
||||
struct spa_pod_builder b = SPA_POD_BUILDER_INIT (buf, sizeof (buf));
|
||||
struct spa_pod *result;
|
||||
guint count = 0;
|
||||
|
||||
switch (id) {
|
||||
case SPA_PARAM_PropInfo: {
|
||||
g_autoptr (GPtrArray) params =
|
||||
wp_spa_props_build_propinfo (&priv->spa_props, &b);
|
||||
|
||||
for (guint i = start; i < params->len; i++) {
|
||||
struct spa_pod *param = g_ptr_array_index (params, i);
|
||||
|
||||
if (spa_pod_filter (&b, &result, param, filter) == 0) {
|
||||
pw_endpoint_emit_param (&self->hooks, seq, id, i, i+1, result);
|
||||
wp_proxy_handle_event_param (self, seq, id, i, i+1, result);
|
||||
if (++count == num)
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SPA_PARAM_Props: {
|
||||
if (start == 0) {
|
||||
struct spa_pod *param = wp_spa_props_build_props (&priv->spa_props, &b);
|
||||
if (spa_pod_filter (&b, &result, param, filter) == 0) {
|
||||
pw_endpoint_emit_param (&self->hooks, seq, id, 0, 1, result);
|
||||
wp_proxy_handle_event_param (self, seq, id, 0, 1, result);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
impl_subscribe_params (void *object, uint32_t *ids, uint32_t n_ids)
|
||||
{
|
||||
WpImplEndpoint *self = WP_IMPL_ENDPOINT (object);
|
||||
|
||||
for (guint i = 0; i < n_ids; i++) {
|
||||
if (ids[i] == SPA_PARAM_Props)
|
||||
self->subscribed = TRUE;
|
||||
impl_enum_params (self, 1, ids[i], 0, UINT32_MAX, NULL);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
impl_set_param (void *object, uint32_t id, uint32_t flags,
|
||||
const struct spa_pod *param)
|
||||
{
|
||||
WpImplEndpoint *self = WP_IMPL_ENDPOINT (object);
|
||||
WpEndpointPrivate *priv =
|
||||
wp_endpoint_get_instance_private (WP_ENDPOINT (self));
|
||||
g_autoptr (GArray) changed_ids = NULL;
|
||||
|
||||
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);
|
||||
|
||||
/* notify subscribers */
|
||||
if (self->subscribed)
|
||||
impl_enum_params (self, 1, SPA_PARAM_Props, 0, UINT32_MAX, NULL);
|
||||
|
||||
/* notify controls locally */
|
||||
for (guint i = 0; i < changed_ids->len; i++) {
|
||||
guint32 prop_id = g_array_index (changed_ids, guint32, i);
|
||||
g_signal_emit (self, signals[SIGNAL_CONTROL_CHANGED], 0, prop_id);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
impl_create_link (void *object, const struct spa_dict *props)
|
||||
{
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
static const struct pw_endpoint_methods impl_endpoint = {
|
||||
PW_VERSION_ENDPOINT_METHODS,
|
||||
.add_listener = impl_add_listener,
|
||||
.subscribe_params = impl_subscribe_params,
|
||||
.enum_params = impl_enum_params,
|
||||
.set_param = impl_set_param,
|
||||
.create_link = impl_create_link,
|
||||
};
|
||||
|
||||
static void
|
||||
populate_endpoint_info (WpImplEndpoint * self, guint32 change_mask)
|
||||
{
|
||||
self->info.change_mask = change_mask & PW_ENDPOINT_CHANGE_MASK_ALL;
|
||||
|
||||
if (change_mask & PW_ENDPOINT_CHANGE_MASK_STREAMS) {
|
||||
self->info.n_streams = wp_si_endpoint_get_n_streams (self->item);
|
||||
}
|
||||
|
||||
if (change_mask & PW_ENDPOINT_CHANGE_MASK_SESSION) {
|
||||
g_autoptr (WpSession) session =
|
||||
wp_session_item_get_session (WP_SESSION_ITEM (self->item));
|
||||
self->info.session_id =
|
||||
session ? wp_proxy_get_bound_id (WP_PROXY (session)) : SPA_ID_INVALID;
|
||||
}
|
||||
|
||||
if (change_mask & PW_ENDPOINT_CHANGE_MASK_PROPS) {
|
||||
WpEndpointPrivate *priv =
|
||||
wp_endpoint_get_instance_private (WP_ENDPOINT (self));
|
||||
|
||||
g_clear_pointer (&priv->properties, wp_properties_unref);
|
||||
priv->properties = wp_si_endpoint_get_properties (self->item);
|
||||
|
||||
self->info.props = priv->properties ?
|
||||
(struct spa_dict *) wp_properties_peek_dict (priv->properties) : NULL;
|
||||
|
||||
g_object_notify (G_OBJECT (self), "properties");
|
||||
}
|
||||
|
||||
if (change_mask & PW_ENDPOINT_CHANGE_MASK_PARAMS) {
|
||||
static struct spa_param_info param_info[] = {
|
||||
SPA_PARAM_INFO (SPA_PARAM_Props, SPA_PARAM_INFO_READWRITE),
|
||||
SPA_PARAM_INFO (SPA_PARAM_PropInfo, SPA_PARAM_INFO_READ)
|
||||
};
|
||||
|
||||
self->info.params = param_info;
|
||||
self->info.n_params = SPA_N_ELEMENTS (param_info);
|
||||
}
|
||||
|
||||
g_object_notify (G_OBJECT (self), "info");
|
||||
}
|
||||
|
||||
static void
|
||||
on_si_endpoint_properties_changed (WpSiEndpoint * item, WpImplEndpoint * self)
|
||||
{
|
||||
populate_endpoint_info (self, PW_ENDPOINT_CHANGE_MASK_PROPS);
|
||||
}
|
||||
|
||||
static void
|
||||
wp_impl_endpoint_init (WpImplEndpoint * self)
|
||||
{
|
||||
WpImplEndpointPrivate *priv = wp_impl_endpoint_get_instance_private (self);
|
||||
/* reuse the parent's private to optimize memory usage and to be able
|
||||
to re-use some of the parent's methods without reimplementing them */
|
||||
WpEndpointPrivate *priv =
|
||||
wp_endpoint_get_instance_private (WP_ENDPOINT (self));
|
||||
|
||||
/* store a pointer to the parent's private; we use that structure
|
||||
as well to optimize memory usage and to be able to re-use some of the
|
||||
parent's methods without reimplementing them */
|
||||
priv->pp = wp_endpoint_get_instance_private (WP_ENDPOINT (self));
|
||||
self->iface = SPA_INTERFACE_INIT (
|
||||
PW_TYPE_INTERFACE_Endpoint,
|
||||
PW_VERSION_ENDPOINT,
|
||||
&impl_endpoint, self);
|
||||
spa_hook_list_init (&self->hooks);
|
||||
|
||||
priv->pp->properties = wp_properties_new_empty ();
|
||||
priv->iface = (struct pw_endpoint *) &self->iface;
|
||||
|
||||
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->pp->properties);
|
||||
priv->info.params = priv->param_info;
|
||||
priv->info.n_params = SPA_N_ELEMENTS (priv->param_info);
|
||||
priv->pp->info = &priv->info;
|
||||
wp_proxy_set_feature_ready (WP_PROXY (self), WP_PROXY_FEATURE_INFO);
|
||||
wp_proxy_set_feature_ready (WP_PROXY (self), WP_ENDPOINT_FEATURE_CONTROLS);
|
||||
}
|
||||
|
||||
static void
|
||||
wp_impl_endpoint_finalize (GObject * object)
|
||||
{
|
||||
WpImplEndpointPrivate *priv =
|
||||
wp_impl_endpoint_get_instance_private (WP_IMPL_ENDPOINT (object));
|
||||
WpImplEndpoint *self = WP_IMPL_ENDPOINT (object);
|
||||
WpEndpointPrivate *priv =
|
||||
wp_endpoint_get_instance_private (WP_ENDPOINT (self));
|
||||
|
||||
/* set to NULL to prevent parent's finalize from calling free() on it */
|
||||
priv->pp->info = NULL;
|
||||
g_free (priv->info.name);
|
||||
g_free (priv->info.media_class);
|
||||
g_free (self->info.name);
|
||||
g_free (self->info.media_class);
|
||||
priv->info = NULL;
|
||||
|
||||
G_OBJECT_CLASS (wp_impl_endpoint_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
client_endpoint_update (WpImplEndpoint * self, guint32 change_mask,
|
||||
guint32 info_change_mask)
|
||||
{
|
||||
WpImplEndpointPrivate *priv = wp_impl_endpoint_get_instance_private (self);
|
||||
char buf[1024];
|
||||
struct spa_pod_builder b = SPA_POD_BUILDER_INIT (buf, sizeof (buf));
|
||||
struct pw_client_endpoint *pw_proxy = NULL;
|
||||
struct pw_endpoint_info *info = NULL;
|
||||
g_autoptr (GPtrArray) params = NULL;
|
||||
|
||||
pw_proxy = (struct pw_client_endpoint *) wp_proxy_get_pw_proxy (WP_PROXY (self));
|
||||
|
||||
if (change_mask & PW_CLIENT_ENDPOINT_UPDATE_PARAMS) {
|
||||
params = wp_spa_props_build_all_pods (&priv->pp->spa_props, &b);
|
||||
}
|
||||
if (change_mask & PW_CLIENT_ENDPOINT_UPDATE_INFO) {
|
||||
info = &priv->info;
|
||||
info->change_mask = info_change_mask;
|
||||
}
|
||||
|
||||
pw_client_endpoint_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)
|
||||
wp_impl_endpoint_set_property (GObject * object, guint property_id,
|
||||
const GValue * value, GParamSpec * pspec)
|
||||
{
|
||||
WpImplEndpoint *self = WP_IMPL_ENDPOINT (object);
|
||||
WpImplEndpointPrivate *priv = wp_impl_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->pp->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);
|
||||
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;
|
||||
}
|
||||
|
||||
client_endpoint_update (self, PW_CLIENT_ENDPOINT_UPDATE_PARAMS, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct pw_client_endpoint_events client_endpoint_events = {
|
||||
PW_VERSION_CLIENT_ENDPOINT_EVENTS,
|
||||
.set_param = client_endpoint_set_param,
|
||||
};
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
wp_impl_endpoint_augment (WpProxy * proxy, WpProxyFeatures features)
|
||||
{
|
||||
WpImplEndpoint *self = WP_IMPL_ENDPOINT (proxy);
|
||||
WpImplEndpointPrivate *priv = wp_impl_endpoint_get_instance_private (self);
|
||||
WpEndpointPrivate *priv =
|
||||
wp_endpoint_get_instance_private (WP_ENDPOINT (self));
|
||||
g_autoptr (GVariant) info = NULL;
|
||||
g_autoptr (GVariantIter) immutable_props = NULL;
|
||||
g_autoptr (WpProperties) props = NULL;
|
||||
|
||||
/* if any of the default features is requested, make sure BOUND
|
||||
is also requested, as they all depend on binding the endpoint */
|
||||
if (features & WP_PROXY_FEATURES_STANDARD)
|
||||
/* PW_PROXY depends on BOUND */
|
||||
if (features & WP_PROXY_FEATURE_PW_PROXY)
|
||||
features |= WP_PROXY_FEATURE_BOUND;
|
||||
|
||||
/* BOUND depends on INFO */
|
||||
if (features & WP_PROXY_FEATURE_BOUND)
|
||||
features |= WP_PROXY_FEATURE_INFO;
|
||||
|
||||
if (features & WP_PROXY_FEATURE_INFO) {
|
||||
guchar direction;
|
||||
const gchar *key, *value;
|
||||
|
||||
/* initialize info struct */
|
||||
priv->info = &self->info;
|
||||
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;
|
||||
|
||||
populate_endpoint_info (self, PW_ENDPOINT_CHANGE_MASK_ALL);
|
||||
|
||||
/* subscribe to changes */
|
||||
g_signal_connect_object (self->item, "endpoint-properties-changed",
|
||||
G_CALLBACK (on_si_endpoint_properties_changed), self, 0);
|
||||
|
||||
/* construct export properties (these will come back through
|
||||
the registry and appear in wp_proxy_get_global_properties) */
|
||||
props = wp_properties_new (
|
||||
PW_KEY_ENDPOINT_NAME, self->info.name,
|
||||
PW_KEY_MEDIA_CLASS, self->info.media_class,
|
||||
NULL);
|
||||
if (self->info.session_id != SPA_ID_INVALID) {
|
||||
wp_properties_setf (props, PW_KEY_SESSION_ID, "%u",
|
||||
self->info.session_id);
|
||||
}
|
||||
while (g_variant_iter_next (immutable_props, "{&s&s}", &key, &value)) {
|
||||
wp_properties_set (props, key, value);
|
||||
}
|
||||
|
||||
wp_proxy_set_feature_ready (WP_PROXY (self), WP_PROXY_FEATURE_INFO);
|
||||
}
|
||||
|
||||
if (features & WP_PROXY_FEATURE_BOUND) {
|
||||
g_autoptr (WpCore) core = wp_proxy_get_core (proxy);
|
||||
struct pw_core *pw_core = wp_core_get_pw_core (core);
|
||||
struct pw_proxy *pw_proxy = NULL;
|
||||
|
||||
/* no pw_core -> we are not connected */
|
||||
if (!pw_core) {
|
||||
|
|
@ -681,223 +820,45 @@ wp_impl_endpoint_augment (WpProxy * proxy, WpProxyFeatures features)
|
|||
return;
|
||||
}
|
||||
|
||||
/* make sure these props are not present; they are added by the server */
|
||||
wp_properties_set (priv->pp->properties, PW_KEY_OBJECT_ID, NULL);
|
||||
wp_properties_set (priv->pp->properties, PW_KEY_CLIENT_ID, NULL);
|
||||
wp_properties_set (priv->pp->properties, PW_KEY_FACTORY_ID, NULL);
|
||||
|
||||
/* add must-have global properties */
|
||||
wp_properties_set (priv->pp->properties,
|
||||
PW_KEY_ENDPOINT_NAME, priv->info.name);
|
||||
wp_properties_set (priv->pp->properties,
|
||||
PW_KEY_MEDIA_CLASS, priv->info.media_class);
|
||||
|
||||
pw_proxy = pw_core_create_object (pw_core, "client-endpoint",
|
||||
PW_TYPE_INTERFACE_ClientEndpoint, PW_VERSION_CLIENT_ENDPOINT,
|
||||
wp_properties_peek_dict (priv->pp->properties), 0);
|
||||
wp_proxy_set_pw_proxy (proxy, pw_proxy);
|
||||
|
||||
pw_client_endpoint_add_listener (pw_proxy, &priv->pp->listener,
|
||||
&client_endpoint_events, self);
|
||||
|
||||
client_endpoint_update (WP_IMPL_ENDPOINT (self),
|
||||
PW_CLIENT_ENDPOINT_UPDATE_PARAMS | PW_CLIENT_ENDPOINT_UPDATE_INFO,
|
||||
PW_ENDPOINT_CHANGE_MASK_ALL);
|
||||
wp_proxy_set_pw_proxy (proxy, pw_core_export (pw_core,
|
||||
PW_TYPE_INTERFACE_Endpoint,
|
||||
wp_properties_peek_dict (props),
|
||||
priv->iface, 0));
|
||||
}
|
||||
}
|
||||
|
||||
static gint
|
||||
wp_impl_endpoint_set_param (WpProxy * self, guint32 id, guint32 flags,
|
||||
const struct spa_pod *param)
|
||||
{
|
||||
return client_endpoint_set_param (self, id, flags, param);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
wp_impl_endpoint_set_control (WpEndpoint * endpoint, guint32 control_id,
|
||||
const struct spa_pod * pod)
|
||||
{
|
||||
WpImplEndpointPrivate *priv =
|
||||
wp_impl_endpoint_get_instance_private (WP_IMPL_ENDPOINT (endpoint));
|
||||
|
||||
if (wp_spa_props_store_pod (&priv->pp->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 (wp_proxy_get_features (WP_PROXY (endpoint)) & WP_PROXY_FEATURE_BOUND) {
|
||||
client_endpoint_update (WP_IMPL_ENDPOINT (endpoint),
|
||||
PW_CLIENT_ENDPOINT_UPDATE_PARAMS, 0);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
wp_impl_endpoint_class_init (WpImplEndpointClass * klass)
|
||||
{
|
||||
GObjectClass *object_class = (GObjectClass *) klass;
|
||||
WpProxyClass *proxy_class = (WpProxyClass *) klass;
|
||||
WpEndpointClass *endpoint_class = (WpEndpointClass *) klass;
|
||||
|
||||
object_class->finalize = wp_impl_endpoint_finalize;
|
||||
object_class->set_property = wp_impl_endpoint_set_property;
|
||||
object_class->get_property = wp_impl_endpoint_get_property;
|
||||
|
||||
proxy_class->augment = wp_impl_endpoint_augment;
|
||||
proxy_class->enum_params = NULL;
|
||||
proxy_class->subscribe_params = NULL;
|
||||
proxy_class->set_param = wp_impl_endpoint_set_param;
|
||||
|
||||
proxy_class->pw_proxy_created = NULL;
|
||||
proxy_class->param = NULL;
|
||||
|
||||
endpoint_class->set_control = wp_impl_endpoint_set_control;
|
||||
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));
|
||||
}
|
||||
|
||||
/**
|
||||
* wp_impl_endpoint_new:
|
||||
* @core: the #WpCore
|
||||
*
|
||||
* Returns: (transfer full): the newly constructed endpoint implementation
|
||||
*/
|
||||
WpImplEndpoint *
|
||||
wp_impl_endpoint_new (WpCore * core)
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* wp_impl_endpoint_set_property:
|
||||
* @self: the endpoint implementation
|
||||
* @key: a property key
|
||||
* @value: a property value
|
||||
*
|
||||
* Sets the specified property on the PipeWire properties of the endpoint.
|
||||
*
|
||||
* If this property is set before exporting the endpoint, then it is also used
|
||||
* in the construction process of the endpoint object and appears as a global
|
||||
* property.
|
||||
*/
|
||||
void
|
||||
wp_impl_endpoint_set_property (WpImplEndpoint * self,
|
||||
const gchar * key, const gchar * value)
|
||||
{
|
||||
WpImplEndpointPrivate *priv;
|
||||
|
||||
g_return_if_fail (WP_IS_IMPL_ENDPOINT (self));
|
||||
priv = wp_impl_endpoint_get_instance_private (self);
|
||||
|
||||
wp_properties_set (priv->pp->properties, key, value);
|
||||
|
||||
g_object_notify (G_OBJECT (self), "properties");
|
||||
|
||||
/* update only after the endpoint has been exported */
|
||||
if (wp_proxy_get_features (WP_PROXY (self)) & WP_PROXY_FEATURE_BOUND) {
|
||||
client_endpoint_update (self, PW_CLIENT_ENDPOINT_UPDATE_INFO,
|
||||
PW_ENDPOINT_CHANGE_MASK_PROPS);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* wp_impl_endpoint_update_properties:
|
||||
* @self: the endpoint implementation
|
||||
* @updates: a set of properties to add or update in the endpoint's properties
|
||||
*
|
||||
* Adds or updates the values of the PipeWire properties of the endpoint
|
||||
* using the properties in @updates as a source.
|
||||
*
|
||||
* If the properties are set before exporting the endpoint, then they are also
|
||||
* used in the construction process of the endpoint object and appear as
|
||||
* global properties.
|
||||
*/
|
||||
void
|
||||
wp_impl_endpoint_update_properties (WpImplEndpoint * self,
|
||||
WpProperties * updates)
|
||||
{
|
||||
WpImplEndpointPrivate *priv;
|
||||
|
||||
g_return_if_fail (WP_IS_IMPL_ENDPOINT (self));
|
||||
priv = wp_impl_endpoint_get_instance_private (self);
|
||||
|
||||
wp_properties_update_from_dict (priv->pp->properties,
|
||||
wp_properties_peek_dict (updates));
|
||||
|
||||
g_object_notify (G_OBJECT (self), "properties");
|
||||
|
||||
/* update only after the endpoint has been exported */
|
||||
if (wp_proxy_get_features (WP_PROXY (self)) & WP_PROXY_FEATURE_BOUND) {
|
||||
client_endpoint_update (self, PW_CLIENT_ENDPOINT_UPDATE_INFO,
|
||||
PW_ENDPOINT_CHANGE_MASK_PROPS);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* wp_impl_endpoint_set_name:
|
||||
* @self: the endpoint implementation
|
||||
* @name: the name to set
|
||||
*
|
||||
* Sets the name of the endpoint to be @name.
|
||||
*
|
||||
* This only makes sense to set before exporting the endpoint.
|
||||
*/
|
||||
void
|
||||
wp_impl_endpoint_set_name (WpImplEndpoint * self, const gchar * name)
|
||||
{
|
||||
WpImplEndpointPrivate *priv;
|
||||
|
||||
g_return_if_fail (WP_IS_IMPL_ENDPOINT (self));
|
||||
priv = wp_impl_endpoint_get_instance_private (self);
|
||||
|
||||
g_free (priv->info.name);
|
||||
priv->info.name = g_strdup (name);
|
||||
}
|
||||
|
||||
/**
|
||||
* wp_impl_endpoint_set_media_class:
|
||||
* @self: the endpoint implementation
|
||||
* @media_class: the media class to set
|
||||
*
|
||||
* Sets the media class of the endpoint to be @media_class.
|
||||
*
|
||||
* This only makes sense to set before exporting the endpoint.
|
||||
*/
|
||||
void
|
||||
wp_impl_endpoint_set_media_class (WpImplEndpoint * self,
|
||||
const gchar * media_class)
|
||||
{
|
||||
WpImplEndpointPrivate *priv;
|
||||
|
||||
g_return_if_fail (WP_IS_IMPL_ENDPOINT (self));
|
||||
priv = wp_impl_endpoint_get_instance_private (self);
|
||||
|
||||
g_free (priv->info.media_class);
|
||||
priv->info.media_class = g_strdup (media_class);
|
||||
}
|
||||
|
||||
/**
|
||||
* wp_impl_endpoint_set_direction:
|
||||
* @self: the endpoint implementation
|
||||
* @dir: the direction to set
|
||||
*
|
||||
* Sets the direction of the endpoint to be @dir.
|
||||
*
|
||||
* This only makes sense to set before exporting the endpoint.
|
||||
*/
|
||||
void
|
||||
wp_impl_endpoint_set_direction (WpImplEndpoint * self, WpDirection dir)
|
||||
{
|
||||
WpImplEndpointPrivate *priv;
|
||||
|
||||
g_return_if_fail (WP_IS_IMPL_ENDPOINT (self));
|
||||
priv = wp_impl_endpoint_get_instance_private (self);
|
||||
|
||||
priv->info.direction = (enum pw_direction) dir;
|
||||
}
|
||||
|
||||
#if 0
|
||||
/**
|
||||
* wp_impl_endpoint_register_control:
|
||||
* @self: the endpoint implementation
|
||||
|
|
@ -936,3 +897,4 @@ wp_impl_endpoint_register_control (WpImplEndpoint * self,
|
|||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -112,48 +112,6 @@ WP_API
|
|||
gboolean wp_endpoint_set_control_float (WpEndpoint * self, guint32 control_id,
|
||||
gfloat value);
|
||||
|
||||
/**
|
||||
* WP_TYPE_IMPL_ENDPOINT:
|
||||
*
|
||||
* The #WpImplEndpoint #GType
|
||||
*/
|
||||
#define WP_TYPE_IMPL_ENDPOINT (wp_impl_endpoint_get_type ())
|
||||
WP_API
|
||||
G_DECLARE_DERIVABLE_TYPE (WpImplEndpoint, wp_impl_endpoint,
|
||||
WP, IMPL_ENDPOINT, WpEndpoint)
|
||||
|
||||
struct _WpImplEndpointClass
|
||||
{
|
||||
WpEndpointClass parent_class;
|
||||
};
|
||||
|
||||
WP_API
|
||||
WpImplEndpoint * wp_impl_endpoint_new (WpCore * core);
|
||||
|
||||
WP_API
|
||||
void wp_impl_endpoint_set_property (WpImplEndpoint * self,
|
||||
const gchar * key, const gchar * value);
|
||||
|
||||
WP_API
|
||||
void wp_impl_endpoint_update_properties (WpImplEndpoint * self,
|
||||
WpProperties * updates);
|
||||
|
||||
WP_API
|
||||
void wp_impl_endpoint_set_name (WpImplEndpoint * self,
|
||||
const gchar * name);
|
||||
|
||||
WP_API
|
||||
void wp_impl_endpoint_set_media_class (WpImplEndpoint * self,
|
||||
const gchar * media_class);
|
||||
|
||||
WP_API
|
||||
void wp_impl_endpoint_set_direction (WpImplEndpoint * self,
|
||||
WpDirection dir);
|
||||
|
||||
WP_API
|
||||
void wp_impl_endpoint_register_control (WpImplEndpoint * self,
|
||||
WpEndpointControl control);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -12,6 +12,8 @@
|
|||
#include "core.h"
|
||||
#include "object-manager.h"
|
||||
#include "proxy.h"
|
||||
#include "endpoint.h"
|
||||
#include "si-interfaces.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <pipewire/pipewire.h>
|
||||
|
|
@ -135,6 +137,10 @@ gint wp_spa_props_store_pod (WpSpaProps * self, guint32 id,
|
|||
gint wp_spa_props_store_from_props (WpSpaProps * self,
|
||||
const struct spa_pod * props, GArray * changed_ids);
|
||||
|
||||
struct spa_pod * wp_spa_props_build_props (WpSpaProps * self,
|
||||
struct spa_pod_builder * b);
|
||||
GPtrArray * wp_spa_props_build_propinfo (WpSpaProps * self,
|
||||
struct spa_pod_builder * b);
|
||||
GPtrArray * wp_spa_props_build_all_pods (WpSpaProps * self,
|
||||
struct spa_pod_builder * b);
|
||||
struct spa_pod * wp_spa_props_build_update (WpSpaProps * self, guint32 id,
|
||||
|
|
@ -168,6 +174,14 @@ wp_spa_props_build_pod (gchar * buffer, gsize size, ...)
|
|||
wp_spa_props_build_pod (b, sizeof (b), ##__VA_ARGS__, NULL)); \
|
||||
})
|
||||
|
||||
/* impl endpoint */
|
||||
|
||||
#define WP_TYPE_IMPL_ENDPOINT (wp_impl_endpoint_get_type ())
|
||||
G_DECLARE_FINAL_TYPE (WpImplEndpoint, wp_impl_endpoint,
|
||||
WP, IMPL_ENDPOINT, WpEndpoint)
|
||||
|
||||
WpImplEndpoint * wp_impl_endpoint_new (WpCore * core, WpSiEndpoint * item);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -12,6 +12,8 @@
|
|||
*/
|
||||
|
||||
#include "session-item.h"
|
||||
#include "private.h"
|
||||
#include "error.h"
|
||||
#include "wpenums.h"
|
||||
|
||||
typedef struct _WpSessionItemPrivate WpSessionItemPrivate;
|
||||
|
|
@ -19,6 +21,8 @@ struct _WpSessionItemPrivate
|
|||
{
|
||||
GWeakRef session;
|
||||
guint32 flags;
|
||||
|
||||
WpImplEndpoint *impl_endpoint;
|
||||
};
|
||||
|
||||
enum {
|
||||
|
|
@ -56,8 +60,7 @@ static void
|
|||
wp_session_item_finalize (GObject * object)
|
||||
{
|
||||
WpSessionItem * self = WP_SESSION_ITEM (object);
|
||||
WpSessionItemPrivate *priv =
|
||||
wp_session_item_get_instance_private (self);
|
||||
WpSessionItemPrivate *priv = wp_session_item_get_instance_private (self);
|
||||
|
||||
g_weak_ref_clear (&priv->session);
|
||||
|
||||
|
|
@ -76,13 +79,92 @@ wp_session_item_default_get_next_step (WpSessionItem * self,
|
|||
static void
|
||||
wp_session_item_default_reset (WpSessionItem * self)
|
||||
{
|
||||
WpSessionItemPrivate *priv =
|
||||
wp_session_item_get_instance_private (self);
|
||||
WpSessionItemPrivate *priv = wp_session_item_get_instance_private (self);
|
||||
|
||||
wp_session_item_unexport (self);
|
||||
|
||||
priv->flags &= ~(WP_SI_FLAG_ACTIVE | WP_SI_FLAG_IN_ERROR);
|
||||
g_signal_emit (self, signals[SIGNAL_FLAGS_CHANGED], 0, priv->flags);
|
||||
}
|
||||
|
||||
static void
|
||||
on_export_proxy_augmented (WpProxy * proxy, GAsyncResult * res, gpointer data)
|
||||
{
|
||||
g_autoptr (GTask) task = G_TASK (data);
|
||||
g_autoptr (GError) error = NULL;
|
||||
WpSessionItem *self = WP_SESSION_ITEM (g_task_get_source_object (task));
|
||||
WpSessionItemPrivate *priv = wp_session_item_get_instance_private (self);
|
||||
|
||||
priv->flags &= ~WP_SI_FLAG_EXPORTING;
|
||||
|
||||
if (!wp_proxy_augment_finish (proxy, res, &error)) {
|
||||
g_weak_ref_set (&priv->session, NULL);
|
||||
g_clear_object (&priv->impl_endpoint);
|
||||
g_task_return_error (task, g_steal_pointer (&error));
|
||||
return;
|
||||
}
|
||||
|
||||
priv->flags |= WP_SI_FLAG_EXPORTED;
|
||||
g_signal_emit (self, signals[SIGNAL_FLAGS_CHANGED], 0, priv->flags);
|
||||
g_task_return_boolean (task, TRUE);
|
||||
}
|
||||
|
||||
static void
|
||||
wp_session_item_default_export (WpSessionItem * self,
|
||||
WpSession * session, GCancellable * cancellable,
|
||||
GAsyncReadyCallback callback, gpointer callback_data)
|
||||
{
|
||||
WpSessionItemPrivate *priv = wp_session_item_get_instance_private (self);
|
||||
g_autoptr (WpCore) core = wp_proxy_get_core (WP_PROXY (session));
|
||||
g_autoptr (GTask) task = NULL;
|
||||
|
||||
g_return_if_fail (priv->flags & WP_SI_FLAG_ACTIVE);
|
||||
g_return_if_fail (!(priv->flags & (WP_SI_FLAG_EXPORTING | WP_SI_FLAG_EXPORTED)));
|
||||
|
||||
task = g_task_new (self, cancellable, callback, callback_data);
|
||||
g_task_set_source_tag (task, wp_session_item_default_export);
|
||||
|
||||
if (WP_IS_SI_ENDPOINT (self)) {
|
||||
g_weak_ref_set (&priv->session, session);
|
||||
priv->flags |= WP_SI_FLAG_EXPORTING;
|
||||
priv->impl_endpoint = wp_impl_endpoint_new (core, WP_SI_ENDPOINT (self));
|
||||
|
||||
g_signal_emit (self, signals[SIGNAL_FLAGS_CHANGED], 0, priv->flags);
|
||||
|
||||
wp_proxy_augment (WP_PROXY (priv->impl_endpoint),
|
||||
WP_PROXY_FEATURES_STANDARD, NULL,
|
||||
(GAsyncReadyCallback) on_export_proxy_augmented,
|
||||
g_steal_pointer (&task));
|
||||
}
|
||||
else {
|
||||
g_task_return_new_error (task, WP_DOMAIN_LIBRARY,
|
||||
WP_LIBRARY_ERROR_INVALID_ARGUMENT,
|
||||
"Cannot export WpSessionItem of unknown type (%s:%p)",
|
||||
G_OBJECT_TYPE_NAME (self), self);
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean
|
||||
wp_session_item_default_export_finish (WpSessionItem * self,
|
||||
GAsyncResult * res, GError ** error)
|
||||
{
|
||||
g_return_val_if_fail (
|
||||
g_async_result_is_tagged (res, wp_session_item_default_export), FALSE);
|
||||
|
||||
return g_task_propagate_boolean (G_TASK (res), error);
|
||||
}
|
||||
|
||||
static void
|
||||
wp_session_item_default_unexport (WpSessionItem * self)
|
||||
{
|
||||
WpSessionItemPrivate *priv = wp_session_item_get_instance_private (self);
|
||||
|
||||
//TODO cancel job if EXPORTING
|
||||
|
||||
g_clear_object (&priv->impl_endpoint);
|
||||
priv->flags &= ~(WP_SI_FLAG_EXPORTING | WP_SI_FLAG_EXPORTED);
|
||||
}
|
||||
|
||||
static void
|
||||
wp_session_item_class_init (WpSessionItemClass * klass)
|
||||
{
|
||||
|
|
@ -94,6 +176,9 @@ wp_session_item_class_init (WpSessionItemClass * klass)
|
|||
klass->reset = wp_session_item_default_reset;
|
||||
|
||||
klass->get_next_step = wp_session_item_default_get_next_step;
|
||||
klass->export = wp_session_item_default_export;
|
||||
klass->export_finish = wp_session_item_default_export_finish;
|
||||
klass->unexport = wp_session_item_default_unexport;
|
||||
|
||||
/**
|
||||
* WpSessionItem::flags-changed:
|
||||
|
|
@ -358,3 +443,68 @@ wp_session_item_reset (WpSessionItem * self)
|
|||
|
||||
WP_SESSION_ITEM_GET_CLASS (self)->reset (self);
|
||||
}
|
||||
|
||||
/**
|
||||
* wp_session_item_export: (virtual export)
|
||||
* @self: the session item
|
||||
* @session: the session on which to export this item
|
||||
* @callback: (scope async): a callback to call when exporting is finished
|
||||
* @callback_data: (closure): data passed to @callback
|
||||
*
|
||||
* Exports this item asynchronously on PipeWire, making it part of the
|
||||
* specified @session.
|
||||
*
|
||||
* Exporting only makes sense for endpoints (items that implement #WpSiEndpoint)
|
||||
* and endpoint links (items that implement #WpSiLink). On other items the
|
||||
* default implementation will immediately call the @callback, reporting error.
|
||||
*/
|
||||
void
|
||||
wp_session_item_export (WpSessionItem * self, WpSession * session,
|
||||
GAsyncReadyCallback callback, gpointer callback_data)
|
||||
{
|
||||
g_return_if_fail (WP_IS_SESSION_ITEM (self));
|
||||
g_return_if_fail (WP_IS_SESSION (session));
|
||||
g_return_if_fail (WP_SESSION_ITEM_GET_CLASS (self)->export);
|
||||
|
||||
WP_SESSION_ITEM_GET_CLASS (self)->export (self, session, NULL,
|
||||
callback, callback_data);
|
||||
}
|
||||
|
||||
/**
|
||||
* wp_session_item_export_finish: (virtual export_finish)
|
||||
* @self: the session item
|
||||
* @res: the async operation result
|
||||
* @error: (out) (optional): the error of the operation, if any
|
||||
*
|
||||
* Returns: %TRUE if the item is now exported, %FALSE if there was an error
|
||||
*/
|
||||
gboolean
|
||||
wp_session_item_export_finish (WpSessionItem * self, GAsyncResult * res,
|
||||
GError ** error)
|
||||
{
|
||||
g_return_val_if_fail (WP_IS_SESSION_ITEM (self), FALSE);
|
||||
g_return_val_if_fail (WP_SESSION_ITEM_GET_CLASS (self)->export_finish, FALSE);
|
||||
|
||||
return WP_SESSION_ITEM_GET_CLASS (self)->export_finish (self, res, error);
|
||||
}
|
||||
|
||||
/**
|
||||
* wp_session_item_unexport: (virtual unexport)
|
||||
* @self: the session item
|
||||
*
|
||||
* Reverses the effects of a previous call to wp_session_item_export().
|
||||
* This means that after this method is called:
|
||||
* - The item is no longer exported on PipeWire
|
||||
* - The item is no longer associated with a session
|
||||
* - If an export operation was in progress, it is cancelled.
|
||||
*
|
||||
* If the item was not exported, this method does nothing.
|
||||
*/
|
||||
void
|
||||
wp_session_item_unexport (WpSessionItem * self)
|
||||
{
|
||||
g_return_if_fail (WP_IS_SESSION_ITEM (self));
|
||||
g_return_if_fail (WP_SESSION_ITEM_GET_CLASS (self)->unexport);
|
||||
|
||||
WP_SESSION_ITEM_GET_CLASS (self)->unexport (self);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,11 +10,10 @@
|
|||
#define __WIREPLUMBER_SESSION_ITEM_H__
|
||||
|
||||
#include "transition.h"
|
||||
#include "session.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
typedef struct _WpSession WpSession;
|
||||
|
||||
/**
|
||||
* WP_TYPE_SESSION_ITEM:
|
||||
*
|
||||
|
|
@ -29,23 +28,25 @@ G_DECLARE_DERIVABLE_TYPE (WpSessionItem, wp_session_item,
|
|||
* WpSiFlags:
|
||||
* @WP_SI_FLAG_ACTIVATING: set when an activation transition is in progress
|
||||
* @WP_SI_FLAG_ACTIVE: set when an activation transition completes successfully
|
||||
* @WP_SI_FLAG_EXPORTED: set when the item has exported all necessary objects
|
||||
* to PipeWire
|
||||
* @WP_SI_FLAG_IN_ERROR: set when there was an error in the activation process;
|
||||
* to recover, the handler must call wp_session_item_reset() before anything
|
||||
* else
|
||||
* @WP_SI_FLAG_CONFIGURED: must be set by subclasses when all the required
|
||||
* (%WP_SI_CONFIG_OPTION_REQUIRED) configuration options have been set
|
||||
* @WP_SI_FLAG_EXPORTING: set when an export operation is in progress
|
||||
* @WP_SI_FLAG_EXPORTED: set when the item has exported all necessary objects
|
||||
* to PipeWire
|
||||
*/
|
||||
typedef enum {
|
||||
/* immutable flags, set internally */
|
||||
WP_SI_FLAG_ACTIVATING = (1<<0),
|
||||
WP_SI_FLAG_ACTIVE = (1<<1),
|
||||
WP_SI_FLAG_EXPORTED = (1<<2),
|
||||
WP_SI_FLAG_IN_ERROR = (1<<3),
|
||||
WP_SI_FLAG_IN_ERROR = (1<<4),
|
||||
|
||||
/* flags that can be changed by subclasses */
|
||||
WP_SI_FLAG_CONFIGURED = (1<<8),
|
||||
WP_SI_FLAG_EXPORTING = (1<<9),
|
||||
WP_SI_FLAG_EXPORTED = (1<<10),
|
||||
|
||||
/* implementation-specific flags */
|
||||
WP_SI_FLAG_CUSTOM_START = (1<<16),
|
||||
|
|
@ -70,6 +71,9 @@ typedef enum {
|
|||
* @execute_step: Implements #WpTransitionClass.execute_step() for the
|
||||
* transition of wp_session_item_activate()
|
||||
* @reset: See wp_session_item_reset()
|
||||
* @export: See wp_session_item_export()
|
||||
* @export_finish: See wp_session_item_export_finish()
|
||||
* @unexport: See wp_session_item_unexport()
|
||||
*/
|
||||
struct _WpSessionItemClass
|
||||
{
|
||||
|
|
@ -84,6 +88,13 @@ struct _WpSessionItemClass
|
|||
guint step);
|
||||
|
||||
void (*reset) (WpSessionItem * self);
|
||||
|
||||
void (*export) (WpSessionItem * self,
|
||||
WpSession * session, GCancellable * cancellable,
|
||||
GAsyncReadyCallback callback, gpointer callback_data);
|
||||
gboolean (*export_finish) (WpSessionItem * self, GAsyncResult * res,
|
||||
GError ** error);
|
||||
void (*unexport) (WpSessionItem * self);
|
||||
};
|
||||
|
||||
/* properties */
|
||||
|
|
@ -121,6 +132,19 @@ gboolean wp_session_item_activate_finish (WpSessionItem * self,
|
|||
WP_API
|
||||
void wp_session_item_reset (WpSessionItem * self);
|
||||
|
||||
/* exporting */
|
||||
|
||||
WP_API
|
||||
void wp_session_item_export (WpSessionItem * self, WpSession * session,
|
||||
GAsyncReadyCallback callback, gpointer callback_data);
|
||||
|
||||
WP_API
|
||||
gboolean wp_session_item_export_finish (WpSessionItem * self,
|
||||
GAsyncResult * res, GError ** error);
|
||||
|
||||
WP_API
|
||||
void wp_session_item_unexport (WpSessionItem * self);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -23,66 +23,33 @@ G_DEFINE_INTERFACE (WpSiEndpoint, wp_si_endpoint, WP_TYPE_SESSION_ITEM)
|
|||
static void
|
||||
wp_si_endpoint_default_init (WpSiEndpointInterface * iface)
|
||||
{
|
||||
g_signal_new ("endpoint-properties-changed", G_TYPE_FROM_INTERFACE (iface),
|
||||
G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0);
|
||||
|
||||
g_signal_new ("endpoint-streams-changed", G_TYPE_FROM_INTERFACE (iface),
|
||||
G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* wp_si_endpoint_get_name: (virtual get_name)
|
||||
* wp_si_endpoint_get_registration_info: (virtual get_registration_info)
|
||||
* @self: the session item
|
||||
*
|
||||
* Returns: (transfer none): the name of the endpoint
|
||||
* This should return information that is used for registering the endpoint,
|
||||
* as 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
|
||||
*
|
||||
* Returns: (transfer full): registration info for the endpoint
|
||||
*/
|
||||
const gchar *
|
||||
wp_si_endpoint_get_name (WpSiEndpoint * self)
|
||||
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_name, NULL);
|
||||
g_return_val_if_fail (WP_SI_ENDPOINT_GET_IFACE (self)->get_registration_info, NULL);
|
||||
|
||||
return WP_SI_ENDPOINT_GET_IFACE (self)->get_name (self);
|
||||
}
|
||||
|
||||
/**
|
||||
* wp_si_endpoint_get_media_class: (virtual get_media_class)
|
||||
* @self: the session item
|
||||
*
|
||||
* Returns: (transfer none): the media class of the endpoint
|
||||
*/
|
||||
const gchar *
|
||||
wp_si_endpoint_get_media_class (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_media_class, NULL);
|
||||
|
||||
return WP_SI_ENDPOINT_GET_IFACE (self)->get_media_class (self);
|
||||
}
|
||||
|
||||
/**
|
||||
* wp_si_endpoint_get_direction: (virtual get_direction)
|
||||
* @self: the session item
|
||||
*
|
||||
* Returns: the direction of the endpoint
|
||||
*/
|
||||
WpDirection
|
||||
wp_si_endpoint_get_direction (WpSiEndpoint * self)
|
||||
{
|
||||
g_return_val_if_fail (WP_IS_SI_ENDPOINT (self), 0);
|
||||
g_return_val_if_fail (WP_SI_ENDPOINT_GET_IFACE (self)->get_direction, 0);
|
||||
|
||||
return WP_SI_ENDPOINT_GET_IFACE (self)->get_direction (self);
|
||||
}
|
||||
|
||||
/**
|
||||
* wp_si_endpoint_get_priority: (virtual get_priority)
|
||||
* @self: the session item
|
||||
*
|
||||
* Returns: the priority of the endpoint
|
||||
*/
|
||||
guint
|
||||
wp_si_endpoint_get_priority (WpSiEndpoint * self)
|
||||
{
|
||||
g_return_val_if_fail (WP_IS_SI_ENDPOINT (self), 0);
|
||||
g_return_val_if_fail (WP_SI_ENDPOINT_GET_IFACE (self)->get_priority, 0);
|
||||
|
||||
return WP_SI_ENDPOINT_GET_IFACE (self)->get_priority (self);
|
||||
return WP_SI_ENDPOINT_GET_IFACE (self)->get_registration_info (self);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -197,21 +164,28 @@ G_DEFINE_INTERFACE (WpSiStream, wp_si_stream, WP_TYPE_SESSION_ITEM)
|
|||
static void
|
||||
wp_si_stream_default_init (WpSiStreamInterface * iface)
|
||||
{
|
||||
g_signal_new ("stream-properties-changed", G_TYPE_FROM_INTERFACE (iface),
|
||||
G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* wp_si_stream_get_name: (virtual get_name)
|
||||
* wp_si_stream_get_registration_info: (virtual get_registration_info)
|
||||
* @self: the session item
|
||||
*
|
||||
* Returns: (transfer none): the name of the stream
|
||||
* This should return information that is used for registering the stream,
|
||||
* as a GVariant tuple of type (sa{ss}) that contains, in order:
|
||||
* - s: the stream's name
|
||||
* - a{ss}: additional properties to be added to the list of global properties
|
||||
*
|
||||
* Returns: (transfer full): registration info for the stream
|
||||
*/
|
||||
const gchar *
|
||||
wp_si_stream_get_name (WpSiStream * self)
|
||||
GVariant *
|
||||
wp_si_stream_get_registration_info (WpSiStream * self)
|
||||
{
|
||||
g_return_val_if_fail (WP_IS_SI_STREAM (self), NULL);
|
||||
g_return_val_if_fail (WP_SI_STREAM_GET_IFACE (self)->get_name, NULL);
|
||||
g_return_val_if_fail (WP_SI_STREAM_GET_IFACE (self)->get_registration_info, NULL);
|
||||
|
||||
return WP_SI_STREAM_GET_IFACE (self)->get_name (self);
|
||||
return WP_SI_STREAM_GET_IFACE (self)->get_registration_info (self);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -17,6 +17,8 @@ G_BEGIN_DECLS
|
|||
|
||||
typedef struct _WpSiStream WpSiStream;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* WP_TYPE_SI_ENDPOINT:
|
||||
*
|
||||
|
|
@ -31,11 +33,7 @@ struct _WpSiEndpointInterface
|
|||
{
|
||||
GTypeInterface interface;
|
||||
|
||||
const gchar * (*get_name) (WpSiEndpoint * self);
|
||||
const gchar * (*get_media_class) (WpSiEndpoint * self);
|
||||
const gchar * (*get_role) (WpSiEndpoint * self);
|
||||
WpDirection (*get_direction) (WpSiEndpoint * self);
|
||||
guint (*get_priority) (WpSiEndpoint * self);
|
||||
GVariant * (*get_registration_info) (WpSiEndpoint * self);
|
||||
WpProperties * (*get_properties) (WpSiEndpoint * self);
|
||||
|
||||
guint (*get_n_streams) (WpSiEndpoint * self);
|
||||
|
|
@ -43,16 +41,7 @@ struct _WpSiEndpointInterface
|
|||
};
|
||||
|
||||
WP_API
|
||||
const gchar * wp_si_endpoint_get_name (WpSiEndpoint * self);
|
||||
|
||||
WP_API
|
||||
const gchar * wp_si_endpoint_get_media_class (WpSiEndpoint * self);
|
||||
|
||||
WP_API
|
||||
WpDirection wp_si_endpoint_get_direction (WpSiEndpoint * self);
|
||||
|
||||
WP_API
|
||||
guint wp_si_endpoint_get_priority (WpSiEndpoint * self);
|
||||
GVariant * wp_si_endpoint_get_registration_info (WpSiEndpoint * self);
|
||||
|
||||
WP_API
|
||||
WpProperties * wp_si_endpoint_get_properties (WpSiEndpoint * self);
|
||||
|
|
@ -102,14 +91,14 @@ struct _WpSiStreamInterface
|
|||
{
|
||||
GTypeInterface interface;
|
||||
|
||||
const gchar * (*get_name) (WpSiStream * self);
|
||||
GVariant * (*get_registration_info) (WpSiStream * self);
|
||||
WpProperties * (*get_properties) (WpSiStream * self);
|
||||
|
||||
WpSiEndpoint * (*get_parent_endpoint) (WpSiStream * self);
|
||||
};
|
||||
|
||||
WP_API
|
||||
const gchar * wp_si_stream_get_name (WpSiStream * self);
|
||||
GVariant * wp_si_stream_get_registration_info (WpSiStream * self);
|
||||
|
||||
WP_API
|
||||
WpProperties * wp_si_stream_get_properties (WpSiStream * self);
|
||||
|
|
|
|||
|
|
@ -224,16 +224,12 @@ wp_spa_props_store_from_props (WpSpaProps * self, const struct spa_pod * props,
|
|||
return count;
|
||||
}
|
||||
|
||||
// for exported update / prop_info + props
|
||||
GPtrArray *
|
||||
wp_spa_props_build_all_pods (WpSpaProps * self, struct spa_pod_builder * b)
|
||||
struct spa_pod *
|
||||
wp_spa_props_build_props (WpSpaProps * self, struct spa_pod_builder * b)
|
||||
{
|
||||
GPtrArray *res = g_ptr_array_new ();
|
||||
GList *l;
|
||||
struct spa_pod_frame f;
|
||||
struct spa_pod *pod;
|
||||
GList *l;
|
||||
|
||||
/* Props */
|
||||
spa_pod_builder_push_object (b, &f, SPA_TYPE_OBJECT_Props, SPA_PARAM_Props);
|
||||
for (l = self->entries; l != NULL; l = g_list_next (l)) {
|
||||
struct entry * e = (struct entry *) l->data;
|
||||
|
|
@ -242,10 +238,16 @@ wp_spa_props_build_all_pods (WpSpaProps * self, struct spa_pod_builder * b)
|
|||
spa_pod_builder_primitive (b, e->value);
|
||||
}
|
||||
}
|
||||
pod = spa_pod_builder_pop (b, &f);
|
||||
g_ptr_array_add (res, pod);
|
||||
return spa_pod_builder_pop (b, &f);
|
||||
}
|
||||
|
||||
GPtrArray *
|
||||
wp_spa_props_build_propinfo (WpSpaProps * self, struct spa_pod_builder * b)
|
||||
{
|
||||
GPtrArray *res = g_ptr_array_new ();
|
||||
GList *l;
|
||||
struct spa_pod *pod;
|
||||
|
||||
/* PropInfo */
|
||||
for (l = self->entries; l != NULL; l = g_list_next (l)) {
|
||||
struct entry * e = (struct entry *) l->data;
|
||||
pod = spa_pod_builder_add_object (b,
|
||||
|
|
@ -259,6 +261,20 @@ wp_spa_props_build_all_pods (WpSpaProps * self, struct spa_pod_builder * b)
|
|||
return res;
|
||||
}
|
||||
|
||||
// for exported update / prop_info + props
|
||||
GPtrArray *
|
||||
wp_spa_props_build_all_pods (WpSpaProps * self, struct spa_pod_builder * b)
|
||||
{
|
||||
GPtrArray *res;
|
||||
struct spa_pod *pod;
|
||||
|
||||
pod = wp_spa_props_build_props (self, b);
|
||||
res = wp_spa_props_build_propinfo (self, b);
|
||||
g_ptr_array_insert (res, 0, pod);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
// proxy set --> value to props object -> push
|
||||
struct spa_pod *
|
||||
wp_spa_props_build_update (WpSpaProps * self, guint32 id,
|
||||
|
|
|
|||
|
|
@ -380,39 +380,19 @@ si_adapter_multi_endpoint_init (WpSiMultiEndpointInterface * iface)
|
|||
iface->get_endpoint = si_adapter_get_endpoint;
|
||||
}
|
||||
|
||||
static const gchar *
|
||||
si_adapter_get_name (WpSiEndpoint * item)
|
||||
static GVariant *
|
||||
si_adapter_get_registration_info (WpSiEndpoint * item)
|
||||
{
|
||||
WpSiAdapter *self = WP_SI_ADAPTER (item);
|
||||
return self->name;
|
||||
}
|
||||
GVariantBuilder b;
|
||||
|
||||
static const gchar *
|
||||
si_adapter_get_media_class (WpSiEndpoint * item)
|
||||
{
|
||||
WpSiAdapter *self = WP_SI_ADAPTER (item);
|
||||
return self->media_class;
|
||||
}
|
||||
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);
|
||||
|
||||
static const gchar *
|
||||
si_adapter_get_role (WpSiEndpoint * item)
|
||||
{
|
||||
WpSiAdapter *self = WP_SI_ADAPTER (item);
|
||||
return self->role;
|
||||
}
|
||||
|
||||
static WpDirection
|
||||
si_adapter_get_direction (WpSiEndpoint * item)
|
||||
{
|
||||
WpSiAdapter *self = WP_SI_ADAPTER (item);
|
||||
return self->direction;
|
||||
}
|
||||
|
||||
static guint
|
||||
si_adapter_get_priority (WpSiEndpoint * item)
|
||||
{
|
||||
WpSiAdapter *self = WP_SI_ADAPTER (item);
|
||||
return self->priority;
|
||||
return g_variant_builder_end (&b);
|
||||
}
|
||||
|
||||
static WpProperties *
|
||||
|
|
@ -422,7 +402,10 @@ si_adapter_get_properties (WpSiEndpoint * item)
|
|||
g_autoptr (WpProperties) node_props = NULL;
|
||||
WpProperties *result;
|
||||
|
||||
result = wp_properties_new_empty ();
|
||||
result = wp_properties_new (
|
||||
PW_KEY_MEDIA_ROLE, self->role,
|
||||
"endpoint.priority", self->priority,
|
||||
NULL);
|
||||
|
||||
/* copy useful properties from the node */
|
||||
node_props = wp_proxy_get_properties (WP_PROXY (self->node));
|
||||
|
|
@ -464,20 +447,22 @@ si_adapter_get_stream (WpSiEndpoint * item, guint index)
|
|||
static void
|
||||
si_adapter_endpoint_init (WpSiEndpointInterface * iface)
|
||||
{
|
||||
iface->get_name = si_adapter_get_name;
|
||||
iface->get_media_class = si_adapter_get_media_class;
|
||||
iface->get_role = si_adapter_get_role;
|
||||
iface->get_direction = si_adapter_get_direction;
|
||||
iface->get_priority = si_adapter_get_priority;
|
||||
iface->get_registration_info = si_adapter_get_registration_info;
|
||||
iface->get_properties = si_adapter_get_properties;
|
||||
iface->get_n_streams = si_adapter_get_n_streams;
|
||||
iface->get_stream = si_adapter_get_stream;
|
||||
}
|
||||
|
||||
static const gchar *
|
||||
si_adapter_get_stream_name (WpSiStream * self)
|
||||
static GVariant *
|
||||
si_adapter_get_stream_registration_info (WpSiStream * self)
|
||||
{
|
||||
return "default";
|
||||
GVariantBuilder b;
|
||||
|
||||
g_variant_builder_init (&b, G_VARIANT_TYPE ("(sa{ss})"));
|
||||
g_variant_builder_add (&b, "s", "default");
|
||||
g_variant_builder_add (&b, "a{ss}", NULL);
|
||||
|
||||
return g_variant_builder_end (&b);
|
||||
}
|
||||
|
||||
static WpProperties *
|
||||
|
|
@ -495,7 +480,7 @@ si_adapter_get_stream_parent_endpoint (WpSiStream * self)
|
|||
static void
|
||||
si_adapter_stream_init (WpSiStreamInterface * iface)
|
||||
{
|
||||
iface->get_name = si_adapter_get_stream_name;
|
||||
iface->get_registration_info = si_adapter_get_stream_registration_info;
|
||||
iface->get_properties = si_adapter_get_stream_properties;
|
||||
iface->get_parent_endpoint = si_adapter_get_stream_parent_endpoint;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,6 +12,108 @@
|
|||
|
||||
#include "test-server.h"
|
||||
|
||||
struct _TestSiEndpoint
|
||||
{
|
||||
WpSessionItem parent;
|
||||
const gchar *name;
|
||||
const gchar *media_class;
|
||||
WpDirection direction;
|
||||
};
|
||||
|
||||
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)
|
||||
{
|
||||
return wp_properties_new ("test.property", "test-value", NULL);
|
||||
}
|
||||
|
||||
static guint
|
||||
test_si_endpoint_get_n_streams (WpSiEndpoint * item)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
static WpSiStream *
|
||||
test_si_endpoint_get_stream (WpSiEndpoint * item, guint index)
|
||||
{
|
||||
g_return_val_if_fail (index == 0, NULL);
|
||||
return WP_SI_STREAM (item);
|
||||
}
|
||||
|
||||
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;
|
||||
iface->get_n_streams = test_si_endpoint_get_n_streams;
|
||||
iface->get_stream = test_si_endpoint_get_stream;
|
||||
}
|
||||
|
||||
static GVariant *
|
||||
test_si_endpoint_get_stream_registration_info (WpSiStream * self)
|
||||
{
|
||||
GVariantBuilder b;
|
||||
|
||||
g_variant_builder_init (&b, G_VARIANT_TYPE ("(sa{ss})"));
|
||||
g_variant_builder_add (&b, "s", "default");
|
||||
g_variant_builder_add (&b, "a{ss}", NULL);
|
||||
|
||||
return g_variant_builder_end (&b);
|
||||
}
|
||||
|
||||
static WpProperties *
|
||||
test_si_endpoint_get_stream_properties (WpSiStream * self)
|
||||
{
|
||||
return wp_properties_new ("stream.property", "test-value-2", NULL);
|
||||
}
|
||||
|
||||
static WpSiEndpoint *
|
||||
test_si_endpoint_get_stream_parent_endpoint (WpSiStream * self)
|
||||
{
|
||||
return WP_SI_ENDPOINT (self);
|
||||
}
|
||||
|
||||
static void
|
||||
test_si_endpoint_stream_init (WpSiStreamInterface * iface)
|
||||
{
|
||||
iface->get_registration_info = test_si_endpoint_get_stream_registration_info;
|
||||
iface->get_properties = test_si_endpoint_get_stream_properties;
|
||||
iface->get_parent_endpoint = test_si_endpoint_get_stream_parent_endpoint;
|
||||
}
|
||||
|
||||
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)
|
||||
G_IMPLEMENT_INTERFACE (WP_TYPE_SI_STREAM, test_si_endpoint_stream_init))
|
||||
|
||||
static void
|
||||
test_si_endpoint_init (TestSiEndpoint * self)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
test_si_endpoint_class_init (TestSiEndpointClass * klass)
|
||||
{
|
||||
}
|
||||
|
||||
/*******************/
|
||||
|
||||
typedef struct {
|
||||
/* the local pipewire server */
|
||||
WpTestServer server;
|
||||
|
|
@ -29,7 +131,7 @@ typedef struct {
|
|||
WpCore *proxy_core;
|
||||
WpObjectManager *proxy_om;
|
||||
|
||||
WpImplEndpoint *impl_endpoint;
|
||||
WpProxy *impl_endpoint;
|
||||
WpProxy *proxy_endpoint;
|
||||
|
||||
gint n_events;
|
||||
|
|
@ -114,10 +216,11 @@ test_endpoint_basic_impl_object_added (WpObjectManager *om,
|
|||
{
|
||||
g_debug ("impl object added");
|
||||
|
||||
g_assert_true (WP_IS_IMPL_ENDPOINT (endpoint));
|
||||
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_IMPL_ENDPOINT (endpoint);
|
||||
fixture->impl_endpoint = WP_PROXY (endpoint);
|
||||
|
||||
if (++fixture->n_events == 3)
|
||||
g_main_loop_quit (fixture->loop);
|
||||
|
|
@ -129,7 +232,8 @@ test_endpoint_basic_impl_object_removed (WpObjectManager *om,
|
|||
{
|
||||
g_debug ("impl object removed");
|
||||
|
||||
g_assert_true (WP_IS_IMPL_ENDPOINT (endpoint));
|
||||
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;
|
||||
|
|
@ -145,6 +249,7 @@ test_endpoint_basic_proxy_object_added (WpObjectManager *om,
|
|||
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);
|
||||
|
|
@ -160,6 +265,7 @@ test_endpoint_basic_proxy_object_removed (WpObjectManager *om,
|
|||
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;
|
||||
|
|
@ -169,22 +275,49 @@ test_endpoint_basic_proxy_object_removed (WpObjectManager *om,
|
|||
}
|
||||
|
||||
static void
|
||||
test_endpoint_basic_export_done (WpProxy * endpoint, GAsyncResult * res,
|
||||
test_endpoint_basic_activate_done (WpSessionItem * item, GAsyncResult * res,
|
||||
TestEndpointFixture *fixture)
|
||||
{
|
||||
g_autoptr (GError) error = NULL;
|
||||
|
||||
g_debug ("activate done");
|
||||
|
||||
g_assert_true (wp_session_item_activate_finish (item, res, &error));
|
||||
g_assert_no_error (error);
|
||||
}
|
||||
|
||||
static void
|
||||
test_endpoint_basic_export_done (WpSessionItem * item, GAsyncResult * res,
|
||||
TestEndpointFixture *fixture)
|
||||
{
|
||||
g_autoptr (GError) error = NULL;
|
||||
|
||||
g_debug ("export done");
|
||||
|
||||
g_assert_true (wp_proxy_augment_finish (endpoint, res, &error));
|
||||
g_assert_true (wp_session_item_export_finish (item, res, &error));
|
||||
g_assert_no_error (error);
|
||||
|
||||
g_assert_true (WP_IS_IMPL_ENDPOINT (endpoint));
|
||||
|
||||
if (++fixture->n_events == 3)
|
||||
g_main_loop_quit (fixture->loop);
|
||||
}
|
||||
|
||||
static void
|
||||
test_endpoint_basic_session_bound (WpProxy * session, GAsyncResult * res,
|
||||
TestEndpointFixture *fixture)
|
||||
{
|
||||
g_autoptr (GError) error = NULL;
|
||||
|
||||
g_debug ("session export done");
|
||||
|
||||
g_assert_true (wp_proxy_augment_finish (session, res, &error));
|
||||
g_assert_no_error (error);
|
||||
|
||||
g_assert_true (WP_IS_IMPL_SESSION (session));
|
||||
|
||||
g_main_loop_quit (fixture->loop);
|
||||
}
|
||||
|
||||
#if 0
|
||||
static void
|
||||
test_endpoint_basic_control_changed (WpEndpoint * endpoint,
|
||||
guint32 control_id, TestEndpointFixture *fixture)
|
||||
|
|
@ -209,13 +342,15 @@ test_endpoint_basic_notify_properties (WpEndpoint * endpoint, GParamSpec * param
|
|||
if (++fixture->n_events == 2)
|
||||
g_main_loop_quit (fixture->loop);
|
||||
}
|
||||
#endif
|
||||
|
||||
static void
|
||||
test_endpoint_basic (TestEndpointFixture *fixture, gconstpointer data)
|
||||
{
|
||||
g_autoptr (WpImplEndpoint) endpoint = NULL;
|
||||
gfloat float_value;
|
||||
gboolean boolean_value;
|
||||
g_autoptr (TestSiEndpoint) endpoint = NULL;
|
||||
g_autoptr (WpImplSession) session = NULL;
|
||||
// gfloat float_value;
|
||||
// gboolean boolean_value;
|
||||
|
||||
/* set up the export side */
|
||||
g_signal_connect (fixture->export_om, "object-added",
|
||||
|
|
@ -223,7 +358,7 @@ test_endpoint_basic (TestEndpointFixture *fixture, gconstpointer data)
|
|||
g_signal_connect (fixture->export_om, "object-removed",
|
||||
(GCallback) test_endpoint_basic_impl_object_removed, fixture);
|
||||
wp_object_manager_add_interest (fixture->export_om,
|
||||
WP_TYPE_IMPL_ENDPOINT, NULL,
|
||||
WP_TYPE_ENDPOINT, NULL,
|
||||
WP_PROXY_FEATURES_STANDARD | WP_ENDPOINT_FEATURE_CONTROLS);
|
||||
wp_core_install_object_manager (fixture->export_core, fixture->export_om);
|
||||
|
||||
|
|
@ -241,32 +376,27 @@ test_endpoint_basic (TestEndpointFixture *fixture, gconstpointer data)
|
|||
|
||||
g_assert_true (wp_core_connect (fixture->proxy_core));
|
||||
|
||||
/* create session */
|
||||
session = wp_impl_session_new (fixture->export_core);
|
||||
wp_proxy_augment (WP_PROXY (session), WP_PROXY_FEATURE_BOUND, NULL,
|
||||
(GAsyncReadyCallback) test_endpoint_basic_session_bound, fixture);
|
||||
|
||||
/* run until session is bound */
|
||||
g_main_loop_run (fixture->loop);
|
||||
g_assert_cmpint (wp_proxy_get_features (WP_PROXY (session)), &,
|
||||
WP_PROXY_FEATURE_BOUND);
|
||||
g_assert_cmpint (wp_proxy_get_bound_id (WP_PROXY (session)), >, 0);
|
||||
|
||||
/* create endpoint */
|
||||
endpoint = wp_impl_endpoint_new (fixture->export_core);
|
||||
wp_impl_endpoint_set_property (endpoint, "test.property", "test-value");
|
||||
wp_impl_endpoint_register_control (endpoint, WP_ENDPOINT_CONTROL_VOLUME);
|
||||
wp_impl_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_proxy_get_properties (WP_PROXY (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_proxy_augment (WP_PROXY (endpoint), WP_PROXY_FEATURE_BOUND, NULL,
|
||||
endpoint = g_object_new (test_si_endpoint_get_type (), NULL);
|
||||
endpoint->name = "test-endpoint";
|
||||
endpoint->media_class = "Audio/Source";
|
||||
endpoint->direction = WP_DIRECTION_OUTPUT;
|
||||
wp_session_item_activate (WP_SESSION_ITEM (endpoint),
|
||||
(GAsyncReadyCallback) test_endpoint_basic_activate_done, fixture);
|
||||
g_assert_cmpint (wp_session_item_get_flags (WP_SESSION_ITEM (endpoint)),
|
||||
&, WP_SI_FLAG_ACTIVE);
|
||||
wp_session_item_export (WP_SESSION_ITEM (endpoint), WP_SESSION (session),
|
||||
(GAsyncReadyCallback) test_endpoint_basic_export_done, fixture);
|
||||
|
||||
/* run until objects are created and features are cached */
|
||||
|
|
@ -275,7 +405,6 @@ test_endpoint_basic (TestEndpointFixture *fixture, gconstpointer data)
|
|||
g_assert_cmpint (fixture->n_events, ==, 3);
|
||||
g_assert_nonnull (fixture->impl_endpoint);
|
||||
g_assert_nonnull (fixture->proxy_endpoint);
|
||||
g_assert_true (fixture->impl_endpoint == endpoint);
|
||||
|
||||
/* test round 1: verify the values on the proxy */
|
||||
|
||||
|
|
@ -286,14 +415,38 @@ test_endpoint_basic (TestEndpointFixture *fixture, gconstpointer data)
|
|||
WP_ENDPOINT_FEATURE_CONTROLS);
|
||||
|
||||
g_assert_cmpuint (wp_proxy_get_bound_id (fixture->proxy_endpoint), ==,
|
||||
wp_proxy_get_bound_id (WP_PROXY (endpoint)));
|
||||
wp_proxy_get_bound_id (fixture->impl_endpoint));
|
||||
|
||||
{
|
||||
g_autoptr (WpProperties) props =
|
||||
wp_proxy_get_properties (fixture->proxy_endpoint);
|
||||
|
||||
g_assert_cmpstr (wp_properties_get (props, "test.property"), ==,
|
||||
"test-value");
|
||||
}
|
||||
|
||||
{
|
||||
g_autoptr (WpProperties) props =
|
||||
wp_proxy_get_global_properties (fixture->proxy_endpoint);
|
||||
g_autofree gchar * session_id = g_strdup_printf ("%u",
|
||||
wp_proxy_get_bound_id (WP_PROXY (session)));
|
||||
|
||||
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 (wp_properties_get (props, PW_KEY_SESSION_ID), ==,
|
||||
session_id);
|
||||
}
|
||||
|
||||
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)));
|
||||
|
||||
#if 0
|
||||
g_assert_true (wp_endpoint_get_control_float (
|
||||
WP_ENDPOINT (fixture->proxy_endpoint),
|
||||
WP_ENDPOINT_CONTROL_VOLUME, &float_value));
|
||||
|
|
@ -390,6 +543,7 @@ test_endpoint_basic (TestEndpointFixture *fixture, gconstpointer data)
|
|||
g_assert_cmpstr (wp_properties_get (props, "test.property"), ==,
|
||||
"changed-value");
|
||||
}
|
||||
#endif
|
||||
|
||||
/* destroy impl endpoint */
|
||||
fixture->n_events = 0;
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue