summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGeorge Kiagiadakis <george.kiagiadakis@collabora.com>2019-05-28 15:34:45 +0300
committerGeorge Kiagiadakis <george.kiagiadakis@collabora.com>2019-05-31 13:44:41 +0300
commit43fee5b87d9198da9bdf5bfcafac7568ea915bfc (patch)
tree5d022cfc4a31c3eec8500eb6b0dca4d1471a2750
parent34c416c14055aefb6f4b288e1c6a992bb020cc7f (diff)
meta-pipewire: initial pipewire recipe
Bug-AGL: SPEC-2473 Change-Id: Ide95be79adb69437564e94071d0315ae5cfd9ae2 Signed-off-by: George Kiagiadakis <george.kiagiadakis@collabora.com>
-rw-r--r--meta-pipewire/recipes-multimedia/pipewire/0001-extensions-implement-Endpoint-ClientEndpoint-interfa.patch1562
-rw-r--r--meta-pipewire/recipes-multimedia/pipewire/0001-spa-include-install-missing-headers.patch40
-rw-r--r--meta-pipewire/recipes-multimedia/pipewire/0002-pipewire-cli-add-support-for-printing-endpoint-info-.patch148
-rw-r--r--meta-pipewire/recipes-multimedia/pipewire/pipewire.conf10
-rw-r--r--meta-pipewire/recipes-multimedia/pipewire/pipewire.inc114
-rw-r--r--meta-pipewire/recipes-multimedia/pipewire/pipewire_git.bb16
6 files changed, 1890 insertions, 0 deletions
diff --git a/meta-pipewire/recipes-multimedia/pipewire/0001-extensions-implement-Endpoint-ClientEndpoint-interfa.patch b/meta-pipewire/recipes-multimedia/pipewire/0001-extensions-implement-Endpoint-ClientEndpoint-interfa.patch
new file mode 100644
index 00000000..b78a9096
--- /dev/null
+++ b/meta-pipewire/recipes-multimedia/pipewire/0001-extensions-implement-Endpoint-ClientEndpoint-interfa.patch
@@ -0,0 +1,1562 @@
+From 3d186f7b97c508cc16955a96a69ee5d6c9db57dc Mon Sep 17 00:00:00 2001
+From: George Kiagiadakis <george.kiagiadakis@collabora.com>
+Date: Thu, 23 May 2019 18:59:05 +0300
+Subject: [PATCH 1/2] extensions: implement Endpoint & ClientEndpoint
+ interfaces
+
+The ClientEndpoint interface allows session managers to register
+endpoint objects on pipewire.
+The Endpoint interface allows other clients to interact with
+endpoints provided by the session manager.
+---
+ src/extensions/client-endpoint.h | 106 ++++
+ src/extensions/endpoint.h | 237 +++++++++
+ src/extensions/meson.build | 2 +
+ src/modules/meson.build | 12 +
+ src/modules/module-endpoint.c | 137 +++++
+ src/modules/module-endpoint/endpoint-impl.c | 428 ++++++++++++++++
+ src/modules/module-endpoint/endpoint-impl.h | 52 ++
+ src/modules/module-endpoint/protocol-native.c | 472 ++++++++++++++++++
+ src/pipewire/pipewire.c | 2 +
+ src/pipewire/type.h | 3 +-
+ 10 files changed, 1450 insertions(+), 1 deletion(-)
+ create mode 100644 src/extensions/client-endpoint.h
+ create mode 100644 src/extensions/endpoint.h
+ create mode 100644 src/modules/module-endpoint.c
+ create mode 100644 src/modules/module-endpoint/endpoint-impl.c
+ create mode 100644 src/modules/module-endpoint/endpoint-impl.h
+ create mode 100644 src/modules/module-endpoint/protocol-native.c
+
+diff --git a/src/extensions/client-endpoint.h b/src/extensions/client-endpoint.h
+new file mode 100644
+index 00000000..0389893c
+--- /dev/null
++++ b/src/extensions/client-endpoint.h
+@@ -0,0 +1,106 @@
++/* PipeWire
++ *
++ * Copyright © 2019 Collabora Ltd.
++ * @author George Kiagiadakis <george.kiagiadakis@collabora.com>
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the "Software"),
++ * to deal in the Software without restriction, including without limitation
++ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
++ * and/or sell copies of the Software, and to permit persons to whom the
++ * Software is furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the next
++ * paragraph) shall be included in all copies or substantial portions of the
++ * Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
++ * DEALINGS IN THE SOFTWARE.
++ */
++
++#ifndef PIPEWIRE_EXT_CLIENT_ENDPOINT_H
++#define PIPEWIRE_EXT_CLIENT_ENDPOINT_H
++
++#include "endpoint.h"
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++struct pw_client_endpoint_proxy;
++
++#define PW_VERSION_CLIENT_ENDPOINT 0
++#define PW_EXTENSION_MODULE_CLIENT_ENDPOINT PIPEWIRE_MODULE_PREFIX "module-endpoint"
++
++#define PW_CLIENT_ENDPOINT_PROXY_METHOD_UPDATE 0
++#define PW_CLIENT_ENDPOINT_PROXY_METHOD_NUM 1
++
++struct pw_client_endpoint_proxy_methods {
++#define PW_VERSION_CLIENT_ENDPOINT_PROXY_METHODS 0
++ uint32_t version;
++
++ /**
++ * Update endpoint info
++ */
++ int (*update) (void *object,
++#define PW_CLIENT_ENDPOINT_UPDATE_PARAMS (1 << 0)
++#define PW_CLIENT_ENDPOINT_UPDATE_PARAMS_INCREMENTAL (1 << 1)
++#define PW_CLIENT_ENDPOINT_UPDATE_INFO (1 << 2)
++ uint32_t change_mask,
++ uint32_t n_params,
++ const struct spa_pod **params,
++ const struct pw_endpoint_info *info);
++};
++
++static inline int
++pw_client_endpoint_proxy_update(struct pw_client_endpoint_proxy *p,
++ uint32_t change_mask,
++ uint32_t n_params,
++ const struct spa_pod **params,
++ struct pw_endpoint_info *info)
++{
++ return pw_proxy_do((struct pw_proxy*)p,
++ struct pw_client_endpoint_proxy_methods, update,
++ change_mask, n_params, params, info);
++}
++
++#define PW_CLIENT_ENDPOINT_PROXY_EVENT_SET_PARAM 0
++#define PW_CLIENT_ENDPOINT_PROXY_EVENT_NUM 1
++
++struct pw_client_endpoint_proxy_events {
++#define PW_VERSION_CLIENT_ENDPOINT_PROXY_EVENTS 0
++ uint32_t version;
++
++ /**
++ * Set a parameter on the endpoint
++ *
++ * \param id the parameter id to set
++ * \param flags extra parameter flags
++ * \param param the parameter to set
++ */
++ void (*set_param) (void *object, uint32_t id, uint32_t flags,
++ const struct spa_pod *param);
++};
++
++static inline void
++pw_client_endpoint_proxy_add_listener(struct pw_client_endpoint_proxy *p,
++ struct spa_hook *listener,
++ const struct pw_client_endpoint_proxy_events *events,
++ void *data)
++{
++ pw_proxy_add_proxy_listener((struct pw_proxy*)p, listener, events, data);
++}
++
++#define pw_client_endpoint_resource_set_param(r,...) \
++ pw_resource_notify(r,struct pw_client_endpoint_proxy_events,set_param,__VA_ARGS__)
++
++#ifdef __cplusplus
++} /* extern "C" */
++#endif
++
++#endif /* PIPEWIRE_EXT_CLIENT_ENDPOINT_H */
+diff --git a/src/extensions/endpoint.h b/src/extensions/endpoint.h
+new file mode 100644
+index 00000000..3b84dd49
+--- /dev/null
++++ b/src/extensions/endpoint.h
+@@ -0,0 +1,237 @@
++/* PipeWire
++ *
++ * Copyright © 2019 Collabora Ltd.
++ * @author George Kiagiadakis <george.kiagiadakis@collabora.com>
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the "Software"),
++ * to deal in the Software without restriction, including without limitation
++ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
++ * and/or sell copies of the Software, and to permit persons to whom the
++ * Software is furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the next
++ * paragraph) shall be included in all copies or substantial portions of the
++ * Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
++ * DEALINGS IN THE SOFTWARE.
++ */
++
++#ifndef PIPEWIRE_EXT_ENDPOINT_H
++#define PIPEWIRE_EXT_ENDPOINT_H
++
++#include <spa/utils/defs.h>
++#include <spa/utils/type-info.h>
++#include <pipewire/proxy.h>
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++struct pw_endpoint_proxy;
++
++#define PW_VERSION_ENDPOINT 0
++#define PW_EXTENSION_MODULE_ENDPOINT PIPEWIRE_MODULE_PREFIX "module-endpoint"
++
++/* extending enum spa_param_type */
++enum endpoint_param_type {
++ PW_ENDPOINT_PARAM_EnumControl = 0x1000,
++ PW_ENDPOINT_PARAM_Control,
++ PW_ENDPOINT_PARAM_EnumStream,
++};
++
++enum endpoint_param_object_type {
++ PW_ENDPOINT_OBJECT_ParamControl = PW_TYPE_FIRST + SPA_TYPE_OBJECT_START + 0x1001,
++ PW_ENDPOINT_OBJECT_ParamStream,
++};
++
++/** properties for PW_ENDPOINT_OBJECT_ParamControl */
++enum endpoint_param_control {
++ PW_ENDPOINT_PARAM_CONTROL_START, /**< object id, one of enum endpoint_param_type */
++ PW_ENDPOINT_PARAM_CONTROL_id, /**< control id (Int) */
++ PW_ENDPOINT_PARAM_CONTROL_stream_id, /**< stream id (Int) */
++ PW_ENDPOINT_PARAM_CONTROL_name, /**< control name (String) */
++ PW_ENDPOINT_PARAM_CONTROL_type, /**< control type (Range) */
++ PW_ENDPOINT_PARAM_CONTROL_value, /**< control value */
++};
++
++/** properties for PW_ENDPOINT_OBJECT_ParamStream */
++enum endpoint_param_stream {
++ PW_ENDPOINT_PARAM_STREAM_START, /**< object id, one of enum endpoint_param_type */
++ PW_ENDPOINT_PARAM_STREAM_id, /**< stream id (Int) */
++ PW_ENDPOINT_PARAM_STREAM_name, /**< stream name (String) */
++};
++
++static const struct spa_type_info endpoint_param_type_info[] = {
++ { PW_ENDPOINT_PARAM_EnumControl, SPA_TYPE_Int, SPA_TYPE_INFO_PARAM_ID_BASE "EnumControl", NULL },
++ { PW_ENDPOINT_PARAM_Control, SPA_TYPE_Int, SPA_TYPE_INFO_PARAM_ID_BASE "Control", NULL },
++ { PW_ENDPOINT_PARAM_EnumStream, SPA_TYPE_Int, SPA_TYPE_INFO_PARAM_ID_BASE "EnumStream", NULL },
++ { 0, 0, NULL, NULL },
++};
++
++#define PW_ENDPOINT_TYPE_INFO_ParamControl SPA_TYPE_INFO_PARAM_BASE "ParamControl"
++#define PW_ENDPOINT_TYPE_INFO_PARAM_CONTROL_BASE PW_ENDPOINT_TYPE_INFO_ParamControl ":"
++
++static const struct spa_type_info endpoint_param_control_info[] = {
++ { PW_ENDPOINT_PARAM_CONTROL_START, SPA_TYPE_Id, PW_ENDPOINT_TYPE_INFO_PARAM_CONTROL_BASE, spa_type_param, },
++ { PW_ENDPOINT_PARAM_CONTROL_id, SPA_TYPE_Int, PW_ENDPOINT_TYPE_INFO_PARAM_CONTROL_BASE "id", NULL },
++ { PW_ENDPOINT_PARAM_CONTROL_stream_id, SPA_TYPE_Int, PW_ENDPOINT_TYPE_INFO_PARAM_CONTROL_BASE "streamId", NULL },
++ { PW_ENDPOINT_PARAM_CONTROL_name, SPA_TYPE_String, PW_ENDPOINT_TYPE_INFO_PARAM_CONTROL_BASE "name", NULL },
++ { PW_ENDPOINT_PARAM_CONTROL_type, SPA_TYPE_Pod, PW_ENDPOINT_TYPE_INFO_PARAM_CONTROL_BASE "type", NULL },
++ { PW_ENDPOINT_PARAM_CONTROL_value, SPA_TYPE_Struct, PW_ENDPOINT_TYPE_INFO_PARAM_CONTROL_BASE "labels", NULL },
++ { 0, 0, NULL, NULL },
++};
++
++#define PW_ENDPOINT_TYPE_INFO_ParamStream SPA_TYPE_INFO_PARAM_BASE "ParamStream"
++#define PW_ENDPOINT_TYPE_INFO_PARAM_STREAM_BASE PW_ENDPOINT_TYPE_INFO_ParamStream ":"
++
++static const struct spa_type_info endpoint_param_stream_info[] = {
++ { PW_ENDPOINT_PARAM_STREAM_START, SPA_TYPE_Id, PW_ENDPOINT_TYPE_INFO_PARAM_STREAM_BASE, spa_type_param, },
++ { PW_ENDPOINT_PARAM_STREAM_id, SPA_TYPE_Int, PW_ENDPOINT_TYPE_INFO_PARAM_STREAM_BASE "id", NULL },
++ { PW_ENDPOINT_PARAM_STREAM_name, SPA_TYPE_String, PW_ENDPOINT_TYPE_INFO_PARAM_STREAM_BASE "name", NULL },
++ { 0, 0, NULL, NULL },
++};
++
++static const struct spa_type_info endpoint_param_object_type_info[] = {
++ { PW_ENDPOINT_OBJECT_ParamControl, SPA_TYPE_Object, SPA_TYPE_INFO_OBJECT_BASE "ParamControl", endpoint_param_control_info, },
++ { PW_ENDPOINT_OBJECT_ParamStream, SPA_TYPE_Object, SPA_TYPE_INFO_OBJECT_BASE "ParamStream", endpoint_param_stream_info },
++ { 0, 0, NULL, NULL },
++};
++
++struct pw_endpoint_info {
++ uint32_t id; /**< id of the global */
++#define PW_ENDPOINT_CHANGE_MASK_PARAMS (1 << 0)
++#define PW_ENDPOINT_CHANGE_MASK_PROPS (1 << 1)
++ uint32_t change_mask; /**< bitfield of changed fields since last call */
++ uint32_t n_params; /**< number of items in \a params */
++ struct spa_param_info *params; /**< parameters */
++ struct spa_dict *props; /**< extra properties */
++};
++
++#define PW_ENDPOINT_PROXY_METHOD_SUBSCRIBE_PARAMS 0
++#define PW_ENDPOINT_PROXY_METHOD_ENUM_PARAMS 1
++#define PW_ENDPOINT_PROXY_METHOD_SET_PARAM 2
++#define PW_ENDPOINT_PROXY_METHOD_NUM 3
++
++struct pw_endpoint_proxy_methods {
++#define PW_VERSION_ENDPOINT_PROXY_METHODS 0
++ uint32_t version;
++
++ /**
++ * Subscribe to parameter changes
++ *
++ * Automatically emit param events for the given ids when
++ * they are changed.
++ *
++ * \param ids an array of param ids
++ * \param n_ids the number of ids in \a ids
++ */
++ int (*subscribe_params) (void *object, uint32_t *ids, uint32_t n_ids);
++
++ /**
++ * Enumerate endpoint parameters
++ *
++ * Start enumeration of endpoint parameters. For each param, a
++ * param event will be emited.
++ *
++ * \param seq a sequence number to place in the reply
++ * \param id the parameter id to enum or SPA_ID_INVALID for all
++ * \param start the start index or 0 for the first param
++ * \param num the maximum number of params to retrieve
++ * \param filter a param filter or NULL
++ */
++ int (*enum_params) (void *object, int seq,
++ uint32_t id, uint32_t start, uint32_t num,
++ const struct spa_pod *filter);
++
++ /**
++ * Set a parameter on the endpoint
++ *
++ * \param id the parameter id to set
++ * \param flags extra parameter flags
++ * \param param the parameter to set
++ */
++ int (*set_param) (void *object, uint32_t id, uint32_t flags,
++ const struct spa_pod *param);
++};
++
++static inline int
++pw_endpoint_proxy_subscribe_params(struct pw_endpoint_proxy *p, uint32_t *ids, uint32_t n_ids)
++{
++ return pw_proxy_do((struct pw_proxy*)p, struct pw_endpoint_proxy_methods,
++ subscribe_params, ids, n_ids);
++}
++
++static inline int
++pw_endpoint_proxy_enum_params(struct pw_endpoint_proxy *p, int seq,
++ uint32_t id, uint32_t start, uint32_t num,
++ const struct spa_pod *filter)
++{
++ return pw_proxy_do((struct pw_proxy*)p, struct pw_endpoint_proxy_methods,
++ enum_params, seq, id, start, num, filter);
++}
++
++static inline int
++pw_endpoint_proxy_set_param(struct pw_endpoint_proxy *p, uint32_t id,
++ uint32_t flags, const struct spa_pod *param)
++{
++ return pw_proxy_do((struct pw_proxy*)p, struct pw_endpoint_proxy_methods,
++ set_param, id, flags, param);
++}
++
++#define PW_ENDPOINT_PROXY_EVENT_INFO 0
++#define PW_ENDPOINT_PROXY_EVENT_PARAM 1
++#define PW_ENDPOINT_PROXY_EVENT_NUM 2
++
++struct pw_endpoint_proxy_events {
++#define PW_VERSION_ENDPOINT_PROXY_EVENTS 0
++ uint32_t version;
++
++ /**
++ * Notify endpoint info
++ *
++ * \param info info about the endpoint
++ */
++ void (*info) (void *object, const struct pw_endpoint_info * info);
++
++ /**
++ * Notify an endpoint param
++ *
++ * Event emited as a result of the enum_params method.
++ *
++ * \param seq the sequence number of the request
++ * \param id the param id
++ * \param index the param index
++ * \param next the param index of the next param
++ * \param param the parameter
++ */
++ void (*param) (void *object, int seq, uint32_t id,
++ uint32_t index, uint32_t next,
++ const struct spa_pod *param);
++};
++
++static inline void
++pw_endpoint_proxy_add_listener(struct pw_endpoint_proxy *p,
++ struct spa_hook *listener,
++ const struct pw_endpoint_proxy_events *events,
++ void *data)
++{
++ pw_proxy_add_proxy_listener((struct pw_proxy*)p, listener, events, data);
++}
++
++#define pw_endpoint_resource_info(r,...) \
++ pw_resource_notify(r,struct pw_endpoint_proxy_events,info,__VA_ARGS__)
++#define pw_endpoint_resource_param(r,...) \
++ pw_resource_notify(r,struct pw_endpoint_proxy_events,param,__VA_ARGS__)
++
++#ifdef __cplusplus
++} /* extern "C" */
++#endif
++
++#endif /* PIPEWIRE_EXT_ENDPOINT_H */
+diff --git a/src/extensions/meson.build b/src/extensions/meson.build
+index a7f5d3cb..9f690caf 100644
+--- a/src/extensions/meson.build
++++ b/src/extensions/meson.build
+@@ -1,5 +1,7 @@
+ pipewire_ext_headers = [
++ 'client-endpoint.h',
+ 'client-node.h',
++ 'endpoint.h',
+ 'protocol-native.h',
+ ]
+
+diff --git a/src/modules/meson.build b/src/modules/meson.build
+index 98bc3864..572f1b6b 100644
+--- a/src/modules/meson.build
++++ b/src/modules/meson.build
+@@ -37,6 +37,18 @@ pipewire_module_client_node = shared_library('pipewire-module-client-node',
+ dependencies : [mathlib, dl_lib, pipewire_dep],
+ )
+
++pipewire_module_endpoint = shared_library('pipewire-module-endpoint',
++ [ 'module-endpoint.c',
++ 'module-endpoint/endpoint-impl.c',
++ 'module-endpoint/protocol-native.c',
++ ],
++ c_args : pipewire_module_c_args,
++ include_directories : [configinc, spa_inc],
++ install : true,
++ install_dir : modules_install_dir,
++ dependencies : [mathlib, dl_lib, pipewire_dep],
++)
++
+ pipewire_module_link_factory = shared_library('pipewire-module-link-factory',
+ [ 'module-link-factory.c' ],
+ c_args : pipewire_module_c_args,
+diff --git a/src/modules/module-endpoint.c b/src/modules/module-endpoint.c
+new file mode 100644
+index 00000000..d830de1b
+--- /dev/null
++++ b/src/modules/module-endpoint.c
+@@ -0,0 +1,137 @@
++/* PipeWire
++ *
++ * Copyright © 2019 Collabora Ltd.
++ * @author George Kiagiadakis <george.kiagiadakis@collabora.com>
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the "Software"),
++ * to deal in the Software without restriction, including without limitation
++ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
++ * and/or sell copies of the Software, and to permit persons to whom the
++ * Software is furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the next
++ * paragraph) shall be included in all copies or substantial portions of the
++ * Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
++ * DEALINGS IN THE SOFTWARE.
++ */
++
++#include "config.h"
++
++#include "module-endpoint/endpoint-impl.h"
++
++struct pw_protocol *pw_protocol_native_ext_endpoint_init(struct pw_core *core);
++
++static const struct spa_dict_item module_props[] = {
++ { PW_MODULE_PROP_AUTHOR, "George Kiagiadakis <george.kiagiadakis@collabora.com>" },
++ { PW_MODULE_PROP_DESCRIPTION, "Allows clients to interract with session manager endpoints" },
++ { PW_MODULE_PROP_VERSION, PACKAGE_VERSION },
++};
++
++struct factory_data {
++ struct pw_factory *this;
++ struct pw_properties *properties;
++
++ struct pw_module *module;
++ struct spa_hook module_listener;
++};
++
++static void *create_object(void *_data,
++ struct pw_resource *resource,
++ uint32_t type,
++ uint32_t version,
++ struct pw_properties *properties,
++ uint32_t new_id)
++{
++ void *result;
++ struct pw_resource *endpoint_resource;
++ struct pw_global *parent;
++ struct pw_client *client = pw_resource_get_client(resource);
++
++ endpoint_resource = pw_resource_new(client, new_id, PW_PERM_RWX, type, version, 0);
++ if (endpoint_resource == NULL)
++ goto no_mem;
++
++ parent = pw_client_get_global(client);
++
++ result = pw_client_endpoint_new(endpoint_resource, parent, properties);
++ if (result == NULL)
++ goto no_mem;
++
++ return result;
++
++ no_mem:
++ pw_log_error("can't create endpoint");
++ pw_resource_error(resource, -ENOMEM, "can't create endpoint: no memory");
++ if (properties)
++ pw_properties_free(properties);
++ return NULL;
++}
++
++static const struct pw_factory_implementation impl_factory = {
++ PW_VERSION_FACTORY_IMPLEMENTATION,
++ .create_object = create_object,
++};
++
++static void module_destroy(void *data)
++{
++ struct factory_data *d = data;
++
++ spa_hook_remove(&d->module_listener);
++
++ if (d->properties)
++ pw_properties_free(d->properties);
++
++ pw_factory_destroy(d->this);
++}
++
++static const struct pw_module_events module_events = {
++ PW_VERSION_MODULE_EVENTS,
++ .destroy = module_destroy,
++};
++
++static int module_init(struct pw_module *module, struct pw_properties *properties)
++{
++ struct pw_core *core = pw_module_get_core(module);
++ struct pw_factory *factory;
++ struct factory_data *data;
++
++ factory = pw_factory_new(core,
++ "client-endpoint",
++ PW_TYPE_INTERFACE_ClientEndpoint,
++ PW_VERSION_CLIENT_ENDPOINT,
++ NULL,
++ sizeof(*data));
++ if (factory == NULL)
++ return -ENOMEM;
++
++ data = pw_factory_get_user_data(factory);
++ data->this = factory;
++ data->module = module;
++ data->properties = properties;
++
++ pw_log_debug("module-endpoint %p: new", module);
++
++ pw_factory_set_implementation(factory, &impl_factory, data);
++ pw_factory_register(factory, NULL, pw_module_get_global(module), NULL);
++
++ pw_protocol_native_ext_endpoint_init(core);
++
++ pw_module_add_listener(module, &data->module_listener, &module_events, data);
++ pw_module_update_properties(module, &SPA_DICT_INIT_ARRAY(module_props));
++
++ return 0;
++}
++
++SPA_EXPORT
++int pipewire__module_init(struct pw_module *module, const char *args)
++{
++ return module_init(module, NULL);
++}
+diff --git a/src/modules/module-endpoint/endpoint-impl.c b/src/modules/module-endpoint/endpoint-impl.c
+new file mode 100644
+index 00000000..252eeca1
+--- /dev/null
++++ b/src/modules/module-endpoint/endpoint-impl.c
+@@ -0,0 +1,428 @@
++/* PipeWire
++ *
++ * Copyright © 2019 Collabora Ltd.
++ * @author George Kiagiadakis <george.kiagiadakis@collabora.com>
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the "Software"),
++ * to deal in the Software without restriction, including without limitation
++ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
++ * and/or sell copies of the Software, and to permit persons to whom the
++ * Software is furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the next
++ * paragraph) shall be included in all copies or substantial portions of the
++ * Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
++ * DEALINGS IN THE SOFTWARE.
++ */
++
++#include "endpoint-impl.h"
++#include <pipewire/private.h>
++#include <spa/pod/filter.h>
++#include <spa/pod/compare.h>
++
++struct pw_endpoint {
++ struct pw_core *core;
++ struct pw_global *global;
++ struct pw_global *parent;
++
++ struct pw_client_endpoint *client_ep;
++
++ uint32_t n_params;
++ struct spa_pod **params;
++
++ struct pw_endpoint_info info;
++ struct pw_properties *props;
++};
++
++struct pw_client_endpoint {
++ struct pw_resource *owner_resource;
++ struct spa_hook owner_resource_listener;
++
++ struct pw_endpoint endpoint;
++};
++
++struct resource_data {
++ struct pw_endpoint *endpoint;
++ struct pw_client_endpoint *client_ep;
++
++ struct spa_hook resource_listener;
++
++ uint32_t n_subscribe_ids;
++ uint32_t subscribe_ids[32];
++};
++
++static int
++endpoint_enum_params (void *object, int seq,
++ uint32_t id, uint32_t start, uint32_t num,
++ const struct spa_pod *filter)
++{
++ struct pw_resource *resource = object;
++ struct resource_data *data = pw_resource_get_user_data(resource);
++ struct pw_endpoint *this = data->endpoint;
++ struct spa_pod *result;
++ struct spa_pod *param;
++ uint8_t buffer[1024];
++ struct spa_pod_builder b = { 0 };
++ uint32_t index;
++ uint32_t next = start;
++ uint32_t count = 0;
++
++ while (true) {
++ index = next++;
++ if (index >= this->n_params)
++ break;
++
++ param = this->params[index];
++
++ if (param == NULL || !spa_pod_is_object_id(param, id))
++ continue;
++
++ spa_pod_builder_init(&b, buffer, sizeof(buffer));
++ if (spa_pod_filter(&b, &result, param, filter) != 0)
++ continue;
++
++ pw_log_debug("endpoint %p: %d param %u", this, seq, index);
++
++ pw_endpoint_resource_param(resource, seq, id, index, next, result);
++
++ if (++count == num)
++ break;
++ }
++ return 0;
++}
++
++static int
++endpoint_subscribe_params (void *object, uint32_t *ids, uint32_t n_ids)
++{
++ struct pw_resource *resource = object;
++ struct resource_data *data = pw_resource_get_user_data(resource);
++ uint32_t i;
++
++ n_ids = SPA_MIN(n_ids, SPA_N_ELEMENTS(data->subscribe_ids));
++ data->n_subscribe_ids = n_ids;
++
++ for (i = 0; i < n_ids; i++) {
++ data->subscribe_ids[i] = ids[i];
++ pw_log_debug("endpoint %p: resource %d subscribe param %u",
++ data->endpoint, resource->id, ids[i]);
++ endpoint_enum_params(resource, 1, ids[i], 0, UINT32_MAX, NULL);
++ }
++ return 0;
++}
++
++static int
++endpoint_set_param (void *object, uint32_t id, uint32_t flags,
++ const struct spa_pod *param)
++{
++ struct pw_resource *resource = object;
++ struct resource_data *data = pw_resource_get_user_data(resource);
++ struct pw_client_endpoint *client_ep = data->client_ep;
++
++ pw_client_endpoint_resource_set_param(client_ep->owner_resource,
++ id, flags, param);
++
++ return 0;
++}
++
++static const struct pw_endpoint_proxy_methods endpoint_methods = {
++ PW_VERSION_ENDPOINT_PROXY_METHODS,
++ .subscribe_params = endpoint_subscribe_params,
++ .enum_params = endpoint_enum_params,
++ .set_param = endpoint_set_param,
++};
++
++static void
++endpoint_unbind(void *data)
++{
++ struct pw_resource *resource = data;
++ spa_list_remove(&resource->link);
++}
++
++static const struct pw_resource_events resource_events = {
++ PW_VERSION_RESOURCE_EVENTS,
++ .destroy = endpoint_unbind,
++};
++
++static int
++endpoint_bind(void *_data, struct pw_client *client, uint32_t permissions,
++ uint32_t version, uint32_t id)
++{
++ struct pw_endpoint *this = _data;
++ struct pw_global *global = this->global;
++ struct pw_resource *resource;
++ struct resource_data *data;
++
++ resource = pw_resource_new(client, id, permissions, global->type, version, sizeof(*data));
++ if (resource == NULL)
++ goto no_mem;
++
++ data = pw_resource_get_user_data(resource);
++ data->endpoint = this;
++ data->client_ep = this->client_ep;
++ pw_resource_add_listener(resource, &data->resource_listener, &resource_events, resource);
++
++ pw_resource_set_implementation(resource, &endpoint_methods, resource);
++
++ pw_log_debug("endpoint %p: bound to %d", this, resource->id);
++
++ spa_list_append(&global->resource_list, &resource->link);
++
++ this->info.change_mask = PW_ENDPOINT_CHANGE_MASK_PARAMS |
++ PW_ENDPOINT_CHANGE_MASK_PROPS;
++ pw_endpoint_resource_info(resource, &this->info);
++ this->info.change_mask = 0;
++
++ return 0;
++
++ no_mem:
++ pw_log_error("can't create node resource");
++ return -ENOMEM;
++}
++
++static int
++pw_endpoint_init(struct pw_endpoint *this,
++ struct pw_core *core,
++ struct pw_client *owner,
++ struct pw_global *parent,
++ struct pw_properties *properties)
++{
++ struct pw_properties *props = NULL;
++
++ pw_log_debug("endpoint %p: new", this);
++
++ this->core = core;
++ this->parent = parent;
++
++ props = properties ? properties : pw_properties_new(NULL, NULL);
++ if (!props)
++ goto no_mem;
++
++ this->props = pw_properties_copy (props);
++ if (!this->props)
++ goto no_mem;
++
++ this->global = pw_global_new (core,
++ PW_TYPE_INTERFACE_Endpoint,
++ PW_VERSION_ENDPOINT,
++ props, endpoint_bind, this);
++ if (!this->global)
++ goto no_mem;
++
++ this->info.id = this->global->id;
++ this->info.props = &this->props->dict;
++
++ return pw_global_register(this->global, owner, parent);
++
++ no_mem:
++ pw_log_error("can't create endpoint - out of memory");
++ if (props && !properties)
++ pw_properties_free(props);
++ if (this->props)
++ pw_properties_free(this->props);
++ return -ENOMEM;
++}
++
++static void
++pw_endpoint_clear(struct pw_endpoint *this)
++{
++ uint32_t i;
++
++ pw_log_debug("endpoint %p: destroy", this);
++
++ pw_global_destroy(this->global);
++
++ for (i = 0; i < this->n_params; i++)
++ free(this->params[i]);
++ free(this->params);
++
++ free(this->info.params);
++
++ if (this->props)
++ pw_properties_free(this->props);
++}
++
++static void
++endpoint_notify_subscribed(struct pw_endpoint *this,
++ uint32_t index, uint32_t next)
++{
++ struct pw_global *global = this->global;
++ struct pw_resource *resource;
++ struct resource_data *data;
++ struct spa_pod *param = this->params[index];
++ uint32_t id;
++ uint32_t i;
++
++ if (!param || !spa_pod_is_object (param))
++ return;
++
++ id = SPA_POD_OBJECT_ID (param);
++
++ spa_list_for_each(resource, &global->resource_list, link) {
++ data = pw_resource_get_user_data(resource);
++ for (i = 0; i < data->n_subscribe_ids; i++) {
++ if (data->subscribe_ids[i] == id) {
++ pw_endpoint_resource_param(resource, 1, id,
++ index, next, param);
++ }
++ }
++ }
++}
++
++static int
++client_endpoint_update(void *object,
++ uint32_t change_mask,
++ uint32_t n_params,
++ const struct spa_pod **params,
++ const struct pw_endpoint_info *info)
++{
++ struct pw_client_endpoint *cliep = object;
++ struct pw_endpoint *this = &cliep->endpoint;
++
++ if (change_mask & PW_CLIENT_ENDPOINT_UPDATE_PARAMS) {
++ uint32_t i;
++
++ pw_log_debug("endpoint %p: update %d params", this, n_params);
++
++ for (i = 0; i < this->n_params; i++)
++ free(this->params[i]);
++ this->n_params = n_params;
++ this->params = realloc(this->params, this->n_params * sizeof(struct spa_pod *));
++
++ for (i = 0; i < this->n_params; i++) {
++ this->params[i] = params[i] ? spa_pod_copy(params[i]) : NULL;
++ endpoint_notify_subscribed(this, i, i+1);
++ }
++ }
++ else if (change_mask & PW_CLIENT_ENDPOINT_UPDATE_PARAMS_INCREMENTAL) {
++ uint32_t i, j;
++ const struct spa_pod_prop *pold, *pnew;
++
++ pw_log_debug("endpoint %p: update %d params incremental", this, n_params);
++
++ for (i = 0; i < this->n_params; i++) {
++ /* we only support incremental updates for controls */
++ if (!spa_pod_is_object_id (this->params[i], PW_ENDPOINT_PARAM_Control))
++ continue;
++
++ for (j = 0; j < n_params; j++) {
++ if (!spa_pod_is_object_id (params[j], PW_ENDPOINT_PARAM_Control)) {
++ pw_log_warn ("endpoint %p: ignoring incremental update "
++ "on non-control param", this);
++ continue;
++ }
++
++ pold = spa_pod_object_find_prop (
++ (const struct spa_pod_object *) this->params[i],
++ NULL, PW_ENDPOINT_PARAM_CONTROL_id);
++ pnew = spa_pod_object_find_prop (
++ (const struct spa_pod_object *) params[j],
++ NULL, PW_ENDPOINT_PARAM_CONTROL_id);
++
++ if (pold && pnew && spa_pod_compare (&pold->value, &pnew->value) == 0) {
++ free (this->params[i]);
++ this->params[i] = spa_pod_copy (params[j]);
++ endpoint_notify_subscribed(this, i, UINT32_MAX);
++ }
++ }
++ }
++ }
++
++ if (change_mask & PW_CLIENT_ENDPOINT_UPDATE_INFO) {
++ struct pw_global *global = this->global;
++ struct pw_resource *resource;
++
++ if (info->change_mask & PW_ENDPOINT_CHANGE_MASK_PARAMS) {
++ size_t size = info->n_params * sizeof(struct spa_param_info);
++ free(this->info.params);
++ this->info.params = malloc(size);
++ this->info.n_params = info->n_params;
++ memcpy(this->info.params, info->params, size);
++ }
++
++ if (info->change_mask & PW_ENDPOINT_CHANGE_MASK_PROPS) {
++ pw_properties_update(this->props, info->props);
++ }
++
++ this->info.change_mask = info->change_mask;
++ spa_list_for_each(resource, &global->resource_list, link) {
++ pw_endpoint_resource_info(resource, &this->info);
++ }
++ this->info.change_mask = 0;
++ }
++
++ return 0;
++}
++
++static struct pw_client_endpoint_proxy_methods client_endpoint_methods = {
++ PW_VERSION_CLIENT_ENDPOINT_PROXY_METHODS,
++ .update = client_endpoint_update,
++};
++
++static void
++client_endpoint_resource_destroy(void *data)
++{
++ struct pw_client_endpoint *this = data;
++
++ pw_log_debug("client-endpoint %p: destroy", this);
++
++ pw_endpoint_clear(&this->endpoint);
++
++ this->owner_resource = NULL;
++ spa_hook_remove(&this->owner_resource_listener);
++ free(this);
++}
++
++static const struct pw_resource_events owner_resource_events = {
++ PW_VERSION_RESOURCE_EVENTS,
++ .destroy = client_endpoint_resource_destroy,
++};
++
++struct pw_client_endpoint *
++pw_client_endpoint_new(struct pw_resource *owner_resource,
++ struct pw_global *parent,
++ struct pw_properties *properties)
++{
++ struct pw_client_endpoint *this;
++ struct pw_client *owner = pw_resource_get_client(owner_resource);
++ struct pw_core *core = pw_client_get_core(owner);
++
++ this = calloc(1, sizeof(struct pw_client_endpoint));
++ if (this == NULL)
++ return NULL;
++
++ pw_log_debug("client-endpoint %p: new", this);
++
++ if (pw_endpoint_init(&this->endpoint, core, owner, parent, properties) < 0)
++ goto error_no_endpoint;
++ this->endpoint.client_ep = this;
++
++ this->owner_resource = owner_resource;
++ pw_resource_add_listener(this->owner_resource,
++ &this->owner_resource_listener,
++ &owner_resource_events,
++ this);
++ pw_resource_set_implementation(this->owner_resource,
++ &client_endpoint_methods,
++ this);
++
++ return this;
++
++ error_no_endpoint:
++ pw_resource_destroy(owner_resource);
++ free(this);
++ return NULL;
++}
++
++void
++pw_client_endpoint_destroy(struct pw_client_endpoint *this)
++{
++ pw_resource_destroy(this->owner_resource);
++}
+diff --git a/src/modules/module-endpoint/endpoint-impl.h b/src/modules/module-endpoint/endpoint-impl.h
+new file mode 100644
+index 00000000..059aa904
+--- /dev/null
++++ b/src/modules/module-endpoint/endpoint-impl.h
+@@ -0,0 +1,52 @@
++/* PipeWire
++ *
++ * Copyright © 2019 Collabora Ltd.
++ * @author George Kiagiadakis <george.kiagiadakis@collabora.com>
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the "Software"),
++ * to deal in the Software without restriction, including without limitation
++ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
++ * and/or sell copies of the Software, and to permit persons to whom the
++ * Software is furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the next
++ * paragraph) shall be included in all copies or substantial portions of the
++ * Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
++ * DEALINGS IN THE SOFTWARE.
++ */
++
++#ifndef PIPEWIRE_ENDPOINT_IMPL_H
++#define PIPEWIRE_ENDPOINT_IMPL_H
++
++#include <pipewire/pipewire.h>
++#include <extensions/endpoint.h>
++#include <extensions/client-endpoint.h>
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++struct pw_endpoint;
++struct pw_client_endpoint;
++
++struct pw_client_endpoint *
++pw_client_endpoint_new(struct pw_resource *resource,
++ struct pw_global *parent,
++ struct pw_properties *properties);
++
++void
++pw_client_endpoint_destroy(struct pw_client_endpoint *endpoint);
++
++#ifdef __cplusplus
++}
++#endif
++
++#endif /* PIPEWIRE_ENDPOINT_IMPL_H */
+diff --git a/src/modules/module-endpoint/protocol-native.c b/src/modules/module-endpoint/protocol-native.c
+new file mode 100644
+index 00000000..a41d3119
+--- /dev/null
++++ b/src/modules/module-endpoint/protocol-native.c
+@@ -0,0 +1,472 @@
++/* PipeWire
++ *
++ * Copyright © 2019 Collabora Ltd.
++ * @author George Kiagiadakis <george.kiagiadakis@collabora.com>
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the "Software"),
++ * to deal in the Software without restriction, including without limitation
++ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
++ * and/or sell copies of the Software, and to permit persons to whom the
++ * Software is furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the next
++ * paragraph) shall be included in all copies or substantial portions of the
++ * Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
++ * DEALINGS IN THE SOFTWARE.
++ */
++
++#include <pipewire/pipewire.h>
++#include <spa/pod/parser.h>
++
++#include <extensions/client-endpoint.h>
++#include <extensions/endpoint.h>
++#include <extensions/protocol-native.h>
++
++static void
++serialize_pw_endpoint_info(struct spa_pod_builder *b,
++ const struct pw_endpoint_info *info)
++{
++ struct spa_pod_frame f;
++ uint32_t i, n_props;
++
++ n_props = info->props ? info->props->n_items : 0;
++
++ spa_pod_builder_push_struct(b, &f);
++ spa_pod_builder_add(b,
++ SPA_POD_Id(info->id),
++ SPA_POD_Int(info->change_mask),
++ SPA_POD_Int(info->n_params),
++ SPA_POD_Int(n_props),
++ NULL);
++
++ for (i = 0; i < info->n_params; i++) {
++ spa_pod_builder_add(b,
++ SPA_POD_Id(info->params[i].id),
++ SPA_POD_Int(info->params[i].flags), NULL);
++ }
++
++ for (i = 0; i < n_props; i++) {
++ spa_pod_builder_add(b,
++ SPA_POD_String(info->props->items[i].key),
++ SPA_POD_String(info->props->items[i].value),
++ NULL);
++ }
++
++ spa_pod_builder_pop(b, &f);
++}
++
++/* macro because of alloca() */
++#define deserialize_pw_endpoint_info(p, f, info) \
++do { \
++ if (spa_pod_parser_push_struct(p, f) < 0 || \
++ spa_pod_parser_get(p, \
++ SPA_POD_Id(&(info)->id), \
++ SPA_POD_Int(&(info)->change_mask), \
++ SPA_POD_Int(&(info)->n_params), \
++ SPA_POD_Int(&(info)->props->n_items), \
++ NULL) < 0) \
++ return -EINVAL; \
++ \
++ if ((info)->n_params > 0) \
++ (info)->params = alloca((info)->n_params * sizeof(struct spa_param_info)); \
++ if ((info)->props->n_items > 0) \
++ (info)->props->items = alloca((info)->props->n_items * sizeof(struct spa_dict_item)); \
++ \
++ for (i = 0; i < (info)->n_params; i++) { \
++ if (spa_pod_parser_get(p, \
++ SPA_POD_Id(&(info)->params[i].id), \
++ SPA_POD_Int(&(info)->params[i].flags), \
++ NULL) < 0) \
++ return -EINVAL; \
++ } \
++ \
++ for (i = 0; i < (info)->props->n_items; i++) { \
++ if (spa_pod_parser_get(p, \
++ SPA_POD_String(&(info)->props->items[i].key), \
++ SPA_POD_String(&(info)->props->items[i].value), \
++ NULL) < 0) \
++ return -EINVAL; \
++ } \
++ \
++ spa_pod_parser_pop(p, f); \
++} while(0)
++
++static int
++endpoint_marshal_subscribe_params(void *object, uint32_t *ids, uint32_t n_ids)
++{
++ struct pw_proxy *proxy = object;
++ struct spa_pod_builder *b;
++
++ b = pw_protocol_native_begin_proxy(proxy,
++ PW_ENDPOINT_PROXY_METHOD_SUBSCRIBE_PARAMS, NULL);
++
++ spa_pod_builder_add_struct(b,
++ SPA_POD_Array(sizeof(uint32_t), SPA_TYPE_Id, n_ids, ids));
++
++ return pw_protocol_native_end_proxy(proxy, b);
++}
++
++static int
++endpoint_demarshal_subscribe_params(void *object, const struct pw_protocol_native_message *msg)
++{
++ struct pw_resource *resource = object;
++ struct spa_pod_parser prs;
++ uint32_t csize, ctype, n_ids;
++ uint32_t *ids;
++
++ spa_pod_parser_init(&prs, msg->data, msg->size);
++ if (spa_pod_parser_get_struct(&prs,
++ SPA_POD_Array(&csize, &ctype, &n_ids, &ids)) < 0)
++ return -EINVAL;
++
++ if (ctype != SPA_TYPE_Id)
++ return -EINVAL;
++
++ return pw_resource_do(resource, struct pw_endpoint_proxy_methods,
++ subscribe_params, 0, ids, n_ids);
++}
++
++static int
++endpoint_marshal_enum_params(void *object, int seq, uint32_t id,
++ uint32_t index, uint32_t num, const struct spa_pod *filter)
++{
++ struct pw_proxy *proxy = object;
++ struct spa_pod_builder *b;
++
++ b = pw_protocol_native_begin_proxy(proxy,
++ PW_ENDPOINT_PROXY_METHOD_ENUM_PARAMS, NULL);
++
++ spa_pod_builder_add_struct(b,
++ SPA_POD_Int(seq),
++ SPA_POD_Id(id),
++ SPA_POD_Int(index),
++ SPA_POD_Int(num),
++ SPA_POD_Pod(filter));
++
++ return pw_protocol_native_end_proxy(proxy, b);
++}
++
++static int
++endpoint_demarshal_enum_params(void *object, const struct pw_protocol_native_message *msg)
++{
++ struct pw_resource *resource = object;
++ struct spa_pod_parser prs;
++ uint32_t id, index, num;
++ int seq;
++ struct spa_pod *filter;
++
++ spa_pod_parser_init(&prs, msg->data, msg->size);
++ if (spa_pod_parser_get_struct(&prs,
++ SPA_POD_Int(&seq),
++ SPA_POD_Id(&id),
++ SPA_POD_Int(&index),
++ SPA_POD_Int(&num),
++ SPA_POD_Pod(&filter)) < 0)
++ return -EINVAL;
++
++ return pw_resource_do(resource, struct pw_endpoint_proxy_methods,
++ enum_params, 0, seq, id, index, num, filter);
++}
++
++static int
++endpoint_marshal_set_param(void *object, uint32_t id, uint32_t flags,
++ const struct spa_pod *param)
++{
++ struct pw_proxy *proxy = object;
++ struct spa_pod_builder *b;
++
++ b = pw_protocol_native_begin_proxy(proxy,
++ PW_ENDPOINT_PROXY_METHOD_SET_PARAM, NULL);
++
++ spa_pod_builder_add_struct(b,
++ SPA_POD_Id(id),
++ SPA_POD_Int(flags),
++ SPA_POD_Pod(param));
++ return pw_protocol_native_end_proxy(proxy, b);
++}
++
++static int
++endpoint_demarshal_set_param(void *object, const struct pw_protocol_native_message *msg)
++{
++ struct pw_resource *resource = object;
++ struct spa_pod_parser prs;
++ uint32_t id, flags;
++ struct spa_pod *param;
++
++ spa_pod_parser_init(&prs, msg->data, msg->size);
++ if (spa_pod_parser_get_struct(&prs,
++ SPA_POD_Id(&id),
++ SPA_POD_Int(&flags),
++ SPA_POD_Pod(&param)) < 0)
++ return -EINVAL;
++
++ return pw_resource_do(resource, struct pw_endpoint_proxy_methods,
++ set_param, 0, id, flags, param);
++}
++
++static void
++endpoint_marshal_info(void *object, const struct pw_endpoint_info *info)
++{
++ struct pw_resource *resource = object;
++ struct spa_pod_builder *b;
++
++ b = pw_protocol_native_begin_resource(resource,
++ PW_ENDPOINT_PROXY_EVENT_INFO, NULL);
++ serialize_pw_endpoint_info (b, info);
++ pw_protocol_native_end_resource(resource, b);
++}
++
++static int
++endpoint_demarshal_info(void *object, const struct pw_protocol_native_message *msg)
++{
++ struct pw_proxy *proxy = object;
++ struct spa_pod_parser prs;
++ struct spa_pod_frame f;
++ struct spa_dict props = SPA_DICT_INIT(NULL, 0);
++ struct pw_endpoint_info info = { .props = &props };
++ uint32_t i;
++
++ spa_pod_parser_init(&prs, msg->data, msg->size);
++
++ deserialize_pw_endpoint_info(&prs, &f, &info);
++
++ return pw_proxy_notify(proxy, struct pw_endpoint_proxy_events,
++ info, 0, &info);
++}
++
++static void
++endpoint_marshal_param(void *object, int seq, uint32_t id,
++ uint32_t index, uint32_t next, const struct spa_pod *param)
++{
++ struct pw_resource *resource = object;
++ struct spa_pod_builder *b;
++
++ b = pw_protocol_native_begin_resource(resource,
++ PW_ENDPOINT_PROXY_EVENT_PARAM, NULL);
++
++ spa_pod_builder_add_struct(b,
++ SPA_POD_Int(seq),
++ SPA_POD_Id(id),
++ SPA_POD_Int(index),
++ SPA_POD_Int(next),
++ SPA_POD_Pod(param));
++
++ pw_protocol_native_end_resource(resource, b);
++}
++
++static int
++endpoint_demarshal_param(void *object, const struct pw_protocol_native_message *msg)
++{
++ struct pw_proxy *proxy = object;
++ struct spa_pod_parser prs;
++ uint32_t id, index, next;
++ int seq;
++ struct spa_pod *param;
++
++ spa_pod_parser_init(&prs, msg->data, msg->size);
++ if (spa_pod_parser_get_struct(&prs,
++ SPA_POD_Int(&seq),
++ SPA_POD_Id(&id),
++ SPA_POD_Int(&index),
++ SPA_POD_Int(&next),
++ SPA_POD_Pod(&param)) < 0)
++ return -EINVAL;
++
++ return pw_proxy_notify(proxy, struct pw_endpoint_proxy_events, param, 0,
++ seq, id, index, next, param);
++}
++
++static const struct pw_endpoint_proxy_methods pw_protocol_native_endpoint_method_marshal = {
++ PW_VERSION_ENDPOINT_PROXY_METHODS,
++ &endpoint_marshal_subscribe_params,
++ &endpoint_marshal_enum_params,
++ &endpoint_marshal_set_param,
++};
++
++static const struct pw_protocol_native_demarshal pw_protocol_native_endpoint_method_demarshal[] = {
++ { &endpoint_demarshal_subscribe_params, 0 },
++ { &endpoint_demarshal_enum_params, 0 },
++ { &endpoint_demarshal_set_param, 0 }
++};
++
++static const struct pw_endpoint_proxy_events pw_protocol_native_endpoint_event_marshal = {
++ PW_VERSION_ENDPOINT_PROXY_EVENTS,
++ &endpoint_marshal_info,
++ &endpoint_marshal_param,
++};
++
++static const struct pw_protocol_native_demarshal pw_protocol_native_endpoint_event_demarshal[] = {
++ { &endpoint_demarshal_info, 0 },
++ { &endpoint_demarshal_param, 0 }
++};
++
++static const struct pw_protocol_marshal pw_protocol_native_endpoint_marshal = {
++ PW_TYPE_INTERFACE_Endpoint,
++ PW_VERSION_ENDPOINT,
++ PW_ENDPOINT_PROXY_METHOD_NUM,
++ PW_ENDPOINT_PROXY_EVENT_NUM,
++ &pw_protocol_native_endpoint_method_marshal,
++ &pw_protocol_native_endpoint_method_demarshal,
++ &pw_protocol_native_endpoint_event_marshal,
++ &pw_protocol_native_endpoint_event_demarshal,
++};
++
++
++static int
++client_endpoint_marshal_update(
++ void *object,
++ uint32_t change_mask,
++ uint32_t n_params,
++ const struct spa_pod **params,
++ const struct pw_endpoint_info *info)
++{
++ struct pw_proxy *proxy = object;
++ struct spa_pod_builder *b;
++ struct spa_pod_frame f;
++ uint32_t i;
++
++ b = pw_protocol_native_begin_proxy(proxy,
++ PW_CLIENT_ENDPOINT_PROXY_METHOD_UPDATE, NULL);
++
++ spa_pod_builder_push_struct(b, &f);
++ spa_pod_builder_add(b,
++ SPA_POD_Int(change_mask),
++ SPA_POD_Int(n_params), NULL);
++
++ for (i = 0; i < n_params; i++)
++ spa_pod_builder_add(b, SPA_POD_Pod(params[i]), NULL);
++
++ if (change_mask & PW_CLIENT_ENDPOINT_UPDATE_INFO)
++ serialize_pw_endpoint_info(b, info);
++
++ spa_pod_builder_pop(b, &f);
++
++ return pw_protocol_native_end_proxy(proxy, b);
++}
++
++static int
++client_endpoint_demarshal_update(void *object,
++ const struct pw_protocol_native_message *msg)
++{
++ struct pw_resource *resource = object;
++ struct spa_pod_parser prs;
++ struct spa_pod_frame f[2];
++ uint32_t change_mask, n_params;
++ const struct spa_pod **params = NULL;
++ struct spa_dict props = SPA_DICT_INIT(NULL, 0);
++ struct pw_endpoint_info info = { .props = &props };
++ uint32_t i;
++
++ spa_pod_parser_init(&prs, msg->data, msg->size);
++ if (spa_pod_parser_push_struct(&prs, &f[0]) < 0 ||
++ spa_pod_parser_get(&prs,
++ SPA_POD_Int(&change_mask),
++ SPA_POD_Int(&n_params), NULL) < 0)
++ return -EINVAL;
++
++ if (n_params > 0)
++ params = alloca(n_params * sizeof(struct spa_pod *));
++ for (i = 0; i < n_params; i++)
++ if (spa_pod_parser_get(&prs,
++ SPA_POD_PodObject(&params[i]), NULL) < 0)
++ return -EINVAL;
++
++ if (change_mask & PW_CLIENT_ENDPOINT_UPDATE_INFO)
++ deserialize_pw_endpoint_info(&prs, &f[1], &info);
++
++ pw_resource_do(resource, struct pw_client_endpoint_proxy_methods,
++ update, 0, change_mask, n_params, params, &info);
++ return 0;
++}
++
++static void
++client_endpoint_marshal_set_param (void *object,
++ uint32_t id, uint32_t flags,
++ const struct spa_pod *param)
++{
++ struct pw_resource *resource = object;
++ struct spa_pod_builder *b;
++
++ b = pw_protocol_native_begin_resource(resource,
++ PW_CLIENT_ENDPOINT_PROXY_EVENT_SET_PARAM, NULL);
++
++ spa_pod_builder_add_struct(b,
++ SPA_POD_Id(id),
++ SPA_POD_Int(flags),
++ SPA_POD_Pod(param));
++
++ pw_protocol_native_end_resource(resource, b);
++}
++
++static int
++client_endpoint_demarshal_set_param(void *object,
++ const struct pw_protocol_native_message *msg)
++{
++ struct pw_proxy *proxy = object;
++ struct spa_pod_parser prs;
++ uint32_t id, flags;
++ const struct spa_pod *param = NULL;
++
++ spa_pod_parser_init(&prs, msg->data, msg->size);
++ if (spa_pod_parser_get_struct(&prs,
++ SPA_POD_Id(&id),
++ SPA_POD_Int(&flags),
++ SPA_POD_PodObject(&param)) < 0)
++ return -EINVAL;
++
++ pw_proxy_notify(proxy, struct pw_client_endpoint_proxy_events,
++ set_param, 0, id, flags, param);
++ return 0;
++}
++
++static const struct pw_client_endpoint_proxy_methods pw_protocol_native_client_endpoint_method_marshal = {
++ PW_VERSION_CLIENT_ENDPOINT_PROXY_METHODS,
++ &client_endpoint_marshal_update,
++};
++
++static const struct pw_protocol_native_demarshal pw_protocol_native_client_endpoint_method_demarshal[] = {
++ { &client_endpoint_demarshal_update, 0 }
++};
++
++static const struct pw_client_endpoint_proxy_events pw_protocol_native_client_endpoint_event_marshal = {
++ PW_VERSION_CLIENT_ENDPOINT_PROXY_EVENTS,
++ &client_endpoint_marshal_set_param,
++};
++
++static const struct pw_protocol_native_demarshal pw_protocol_native_client_endpoint_event_demarshal[] = {
++ { &client_endpoint_demarshal_set_param, 0 }
++};
++
++static const struct pw_protocol_marshal pw_protocol_native_client_endpoint_marshal = {
++ PW_TYPE_INTERFACE_ClientEndpoint,
++ PW_VERSION_CLIENT_ENDPOINT,
++ PW_CLIENT_ENDPOINT_PROXY_METHOD_NUM,
++ PW_CLIENT_ENDPOINT_PROXY_EVENT_NUM,
++ &pw_protocol_native_client_endpoint_method_marshal,
++ &pw_protocol_native_client_endpoint_method_demarshal,
++ &pw_protocol_native_client_endpoint_event_marshal,
++ &pw_protocol_native_client_endpoint_event_demarshal,
++};
++
++struct pw_protocol *pw_protocol_native_ext_endpoint_init(struct pw_core *core)
++{
++ struct pw_protocol *protocol;
++
++ protocol = pw_core_find_protocol(core, PW_TYPE_INFO_PROTOCOL_Native);
++
++ if (protocol == NULL)
++ return NULL;
++
++ pw_protocol_add_marshal(protocol, &pw_protocol_native_client_endpoint_marshal);
++ pw_protocol_add_marshal(protocol, &pw_protocol_native_endpoint_marshal);
++
++ return protocol;
++}
+diff --git a/src/pipewire/pipewire.c b/src/pipewire/pipewire.c
+index a8752438..bbbf9420 100644
+--- a/src/pipewire/pipewire.c
++++ b/src/pipewire/pipewire.c
+@@ -647,6 +647,8 @@ static const struct spa_type_info type_info[] = {
+ { PW_TYPE_INTERFACE_Module, SPA_TYPE_Pointer, PW_TYPE_INFO_INTERFACE_BASE "Module", NULL },
+ { PW_TYPE_INTERFACE_ClientNode, SPA_TYPE_Pointer, PW_TYPE_INFO_INTERFACE_BASE "ClientNode", NULL },
+ { PW_TYPE_INTERFACE_Device, SPA_TYPE_Pointer, PW_TYPE_INFO_INTERFACE_BASE "Device", NULL },
++ { PW_TYPE_INTERFACE_Endpoint, SPA_TYPE_Pointer, PW_TYPE_INFO_INTERFACE_BASE "Endpoint", NULL},
++ { PW_TYPE_INTERFACE_ClientEndpoint, SPA_TYPE_Pointer, PW_TYPE_INFO_INTERFACE_BASE "ClientEndpoint", NULL},
+ { SPA_ID_INVALID, SPA_ID_INVALID, "spa_types", spa_types },
+ { 0, 0, NULL, NULL },
+ };
+diff --git a/src/pipewire/type.h b/src/pipewire/type.h
+index a1b205f7..39544913 100644
+--- a/src/pipewire/type.h
++++ b/src/pipewire/type.h
+@@ -48,7 +48,8 @@ enum {
+ /* extensions */
+ PW_TYPE_INTERFACE_EXTENSIONS = PW_TYPE_INTERFACE_START + 0x1000,
+ PW_TYPE_INTERFACE_ClientNode,
+-
++ PW_TYPE_INTERFACE_Endpoint,
++ PW_TYPE_INTERFACE_ClientEndpoint,
+ };
+
+ #define PW_TYPE_INFO_BASE "PipeWire:"
+--
+2.20.1
+
diff --git a/meta-pipewire/recipes-multimedia/pipewire/0001-spa-include-install-missing-headers.patch b/meta-pipewire/recipes-multimedia/pipewire/0001-spa-include-install-missing-headers.patch
new file mode 100644
index 00000000..01efe773
--- /dev/null
+++ b/meta-pipewire/recipes-multimedia/pipewire/0001-spa-include-install-missing-headers.patch
@@ -0,0 +1,40 @@
+From 14893ae87ab0b15b7e438779433c4973c797c5f5 Mon Sep 17 00:00:00 2001
+From: George Kiagiadakis <george.kiagiadakis@collabora.com>
+Date: Wed, 29 May 2019 12:09:13 +0300
+Subject: [PATCH] spa/include: install missing headers
+
+---
+ spa/include/spa/meson.build | 3 +++
+ 1 file changed, 3 insertions(+)
+
+diff --git a/spa/include/spa/meson.build b/spa/include/spa/meson.build
+index c9d07659..c079a1a2 100644
+--- a/spa/include/spa/meson.build
++++ b/spa/include/spa/meson.build
+@@ -39,6 +39,7 @@ spa_monitor_headers = [
+ 'monitor/device.h',
+ 'monitor/monitor.h',
+ 'monitor/type-info.h',
++ 'monitor/utils.h',
+ ]
+
+ install_headers(spa_monitor_headers,
+@@ -50,6 +51,7 @@ spa_node_headers = [
+ 'node/io.h',
+ 'node/node.h',
+ 'node/type-info.h',
++ 'node/utils.h',
+ ]
+
+ install_headers(spa_node_headers,
+@@ -97,6 +99,7 @@ spa_utils_headers = [
+ 'utils/dict.h',
+ 'utils/hook.h',
+ 'utils/list.h',
++ 'utils/result.h',
+ 'utils/ringbuffer.h',
+ 'utils/type.h',
+ 'utils/type-info.h',
+--
+2.20.1
+
diff --git a/meta-pipewire/recipes-multimedia/pipewire/0002-pipewire-cli-add-support-for-printing-endpoint-info-.patch b/meta-pipewire/recipes-multimedia/pipewire/0002-pipewire-cli-add-support-for-printing-endpoint-info-.patch
new file mode 100644
index 00000000..008c15f9
--- /dev/null
+++ b/meta-pipewire/recipes-multimedia/pipewire/0002-pipewire-cli-add-support-for-printing-endpoint-info-.patch
@@ -0,0 +1,148 @@
+From e76140c534dde71424eb9abd1dde69cf14152da5 Mon Sep 17 00:00:00 2001
+From: George Kiagiadakis <george.kiagiadakis@collabora.com>
+Date: Tue, 28 May 2019 11:46:36 +0300
+Subject: [PATCH 2/2] pipewire-cli: add support for printing endpoint info &
+ params
+
+Note that you have to run:
+ load-module libpipewire-module-endpoint
+before trying to query any endpoints
+---
+ src/tools/pipewire-cli.c | 78 +++++++++++++++++++++++++++++++++++++++-
+ 1 file changed, 77 insertions(+), 1 deletion(-)
+
+diff --git a/src/tools/pipewire-cli.c b/src/tools/pipewire-cli.c
+index 521739f6..9511db82 100644
+--- a/src/tools/pipewire-cli.c
++++ b/src/tools/pipewire-cli.c
+@@ -38,6 +38,8 @@
+ #include <pipewire/type.h>
+ #include <pipewire/permission.h>
+
++#include <extensions/endpoint.h>
++
+ static const char WHITESPACE[] = " \t";
+
+ struct remote_data;
+@@ -176,8 +178,12 @@ static void print_params(struct spa_param_info *params, uint32_t n_params, char
+ return;
+ }
+ for (i = 0; i < n_params; i++) {
++ const struct spa_type_info *type_info = spa_type_param;
++ if (params[i].id >= PW_ENDPOINT_PARAM_EnumControl)
++ type_info = endpoint_param_type_info;
++
+ fprintf(stdout, "%c\t %d (%s) %c%c\n", mark, params[i].id,
+- spa_debug_type_find_name(spa_type_param, params[i].id),
++ spa_debug_type_find_name(type_info, params[i].id),
+ params[i].flags & SPA_PARAM_INFO_READ ? 'r' : '-',
+ params[i].flags & SPA_PARAM_INFO_WRITE ? 'w' : '-');
+ }
+@@ -641,6 +647,16 @@ static void info_device(struct proxy_data *pd)
+ info->change_mask = 0;
+ }
+
++static void info_endpoint(struct proxy_data *pd)
++{
++ struct pw_endpoint_info *info = pd->info;
++
++ info_global(pd);
++ print_properties(info->props, MARK_CHANGE(1), true);
++ print_params(info->params, info->n_params, MARK_CHANGE(0), true);
++ info->change_mask = 0;
++}
++
+ static void core_event_info(void *object, const struct pw_core_info *info)
+ {
+ struct proxy_data *pd = object;
+@@ -708,6 +724,9 @@ static void event_param(void *object, int seq, uint32_t id,
+
+ if (spa_pod_is_object_type(param, SPA_TYPE_OBJECT_Format))
+ spa_debug_format(2, NULL, param);
++ else if (spa_pod_is_object_type(param, PW_ENDPOINT_OBJECT_ParamControl) ||
++ spa_pod_is_object_type(param, PW_ENDPOINT_OBJECT_ParamStream))
++ spa_debug_pod(2, endpoint_param_object_type_info, param);
+ else
+ spa_debug_pod(2, NULL, param);
+ }
+@@ -842,6 +861,53 @@ static const struct pw_device_proxy_events device_events = {
+ .param = event_param
+ };
+
++static void endpoint_info_free(struct pw_endpoint_info *info)
++{
++ free(info->params);
++ if (info->props)
++ pw_properties_free ((struct pw_properties *)info->props);
++ free(info);
++}
++
++static void endpoint_event_info(void *object,
++ const struct pw_endpoint_info *update)
++{
++ struct proxy_data *pd = object;
++ struct remote_data *rd = pd->rd;
++ struct pw_endpoint_info *info = pd->info;
++
++ if (!info) {
++ info = pd->info = calloc(1, sizeof(*info));
++ info->id = update->id;
++ }
++ if (update->change_mask & PW_ENDPOINT_CHANGE_MASK_PARAMS) {
++ info->n_params = update->n_params;
++ free(info->params);
++ info->params = malloc(info->n_params * sizeof(struct spa_param_info));
++ memcpy(info->params, update->params,
++ info->n_params * sizeof(struct spa_param_info));
++ }
++ if (update->change_mask & PW_ENDPOINT_CHANGE_MASK_PROPS) {
++ if (info->props)
++ pw_properties_free ((struct pw_properties *)info->props);
++ info->props =
++ (struct spa_dict *) pw_properties_new_dict (update->props);
++ }
++
++ if (pd->global == NULL)
++ pd->global = pw_map_lookup(&rd->globals, info->id);
++ if (pd->global && pd->global->info_pending) {
++ info_endpoint(pd);
++ pd->global->info_pending = false;
++ }
++}
++
++static const struct pw_endpoint_proxy_events endpoint_events = {
++ PW_VERSION_ENDPOINT_PROXY_EVENTS,
++ .info = endpoint_event_info,
++ .param = event_param
++};
++
+ static void
+ destroy_proxy (void *data)
+ {
+@@ -928,6 +994,12 @@ static bool bind_global(struct remote_data *rd, struct global *global, char **er
+ destroy = (pw_destroy_t) pw_link_info_free;
+ info_func = info_link;
+ break;
++ case PW_TYPE_INTERFACE_Endpoint:
++ events = &endpoint_events;
++ client_version = PW_VERSION_ENDPOINT;
++ destroy = (pw_destroy_t) endpoint_info_free;
++ info_func = info_endpoint;
++ break;
+ default:
+ asprintf(error, "unsupported type %s", spa_debug_type_find_name(pw_type_info(), global->type));
+ return false;
+@@ -1201,6 +1273,10 @@ static bool do_enum_params(struct data *data, const char *cmd, char *args, char
+ pw_device_proxy_enum_params((struct pw_device_proxy*)global->proxy, 0,
+ param_id, 0, 0, NULL);
+ break;
++ case PW_TYPE_INTERFACE_Endpoint:
++ pw_endpoint_proxy_enum_params((struct pw_endpoint_proxy*)global->proxy, 0,
++ param_id, 0, 0, NULL);
++ break;
+ default:
+ asprintf(error, "enum-params not implemented on object %d", atoi(a[0]));
+ return false;
+--
+2.20.1
+
diff --git a/meta-pipewire/recipes-multimedia/pipewire/pipewire.conf b/meta-pipewire/recipes-multimedia/pipewire/pipewire.conf
new file mode 100644
index 00000000..d09ee8ed
--- /dev/null
+++ b/meta-pipewire/recipes-multimedia/pipewire/pipewire.conf
@@ -0,0 +1,10 @@
+# daemon config file for PipeWire version "0.2.9"
+# distributed by Automotive Grade Linux
+load-module libpipewire-module-protocol-native
+load-module libpipewire-module-spa-monitor alsa/libspa-alsa alsa-monitor alsa
+load-module libpipewire-module-client-node
+load-module libpipewire-module-access
+load-module libpipewire-module-audio-dsp
+load-module libpipewire-module-link-factory
+load-module libpipewire-module-endpoint
+exec /usr/bin/wireplumber
diff --git a/meta-pipewire/recipes-multimedia/pipewire/pipewire.inc b/meta-pipewire/recipes-multimedia/pipewire/pipewire.inc
new file mode 100644
index 00000000..c1916c10
--- /dev/null
+++ b/meta-pipewire/recipes-multimedia/pipewire/pipewire.inc
@@ -0,0 +1,114 @@
+SUMMARY = "Multimedia processing server for Linux"
+HOMEPAGE = "https://pipewire.org"
+BUGTRACKER = "https://github.com/PipeWire/pipewire/issues"
+AUTHOR = "Wim Taymans <wtaymans@redhat.com>"
+SECTION = "multimedia"
+
+LICENSE = "MIT & LGPL-2.1"
+LIC_FILES_CHKSUM = "\
+ file://COPYING;beginline=3;md5=b3adc775ca6ee80056383a5ae814cc75 \
+ file://pipewire-alsa/LICENSE;md5=fc178bcd425090939a8b634d1d6a9594 \
+ file://pipewire-jack/LICENSE;md5=fc178bcd425090939a8b634d1d6a9594 \
+ file://pipewire-pulseaudio/LICENSE;md5=fc178bcd425090939a8b634d1d6a9594 \
+"
+
+inherit meson pkgconfig systemd manpages
+
+DEPENDS = "dbus"
+
+PACKAGECONFIG ??= "\
+ ${@bb.utils.filter('DISTRO_FEATURES', 'systemd', d)} \
+ alsa audioconvert \
+ pipewire-alsa \
+"
+
+GST_VER = "1.0"
+
+# systemd integration
+PACKAGECONFIG[systemd] = "-Dsystemd=true,-Dsystemd=false,systemd"
+
+# SPA plugins
+PACKAGECONFIG[alsa] = "-Dalsa=true,-Dalsa=false,udev alsa-lib"
+PACKAGECONFIG[audioconvert] = "-Daudioconvert=true,-Daudioconvert=false,speexdsp"
+PACKAGECONFIG[audiotestsrc] = "-Daudiotestsrc=true,-Daudiotestsrc=false, "
+PACKAGECONFIG[bluez5] = "-Dbluez5=true,-Dbluez5=false,bluez5 sbc"
+PACKAGECONFIG[v4l2] = "-Dv4l2=true,-Dv4l2=false,udev v4l-utils"
+PACKAGECONFIG[videotestsrc] = "-Dvideotestsrc=true,-Dvideotestsrc=false, "
+
+# alsa plugin to redirect audio to pipewire
+PACKAGECONFIG[pipewire-alsa] = "-Dpipewire-alsa=true,-Dpipewire-alsa=false,alsa-lib"
+# pulseaudio drop-in replacement library
+PACKAGECONFIG[pipewire-pulseaudio] = "-Dpipewire-pulseaudio=true,-Dpipewire-pulseaudio=false,pulseaudio glib-2.0"
+# jack drop-in replacement library
+PACKAGECONFIG[pipewire-jack] = "-Dpipewire-jack=true,-Dpipewire-jack=false,jack"
+
+# GStreamer plugins
+PACKAGECONFIG[gstreamer] = "-Dgstreamer=true,-Dgstreamer=false,glib-2.0 gstreamer${GST_VER} gstreamer${GST_VER}-plugins-base"
+
+# man pages
+PACKAGECONFIG[manpages] = "-Dman=true,-Dman=false,libxml-parser-perl-native"
+
+do_install_append() {
+ # if we are distributing our own configuration file,
+ # replace the one installed by pipewire
+ if [ -f ${WORKDIR}/pipewire.conf ]
+ then
+ install -m 0644 ${WORKDIR}/pipewire.conf ${D}${sysconfdir}/pipewire/pipewire.conf
+ fi
+
+ # only install the alsa config file if the alsa-lib plugin has been built
+ # this avoids creating the pipewire-alsa package when the pipewire-alsa
+ # feature is not enabled
+ if [ -d ${D}${libdir}/alsa-lib ]
+ then
+ mkdir -p ${D}${datadir}/alsa/alsa.conf.d
+ install -m 0644 ${S}/pipewire-alsa/conf/50-pipewire.conf ${D}${datadir}/alsa/alsa.conf.d/50-pipewire.conf
+ fi
+}
+
+PACKAGES += "\
+ lib${PN} \
+ lib${PN}-modules \
+ ${PN}-spa-plugins \
+ ${PN}-alsa \
+ ${PN}-pulseaudio \
+ ${PN}-jack \
+ gstreamer${GST_VER}-${PN} \
+"
+
+FILES_${PN} = "\
+ ${bindir}/pipewire* \
+ ${sysconfdir}/pipewire/pipewire.conf \
+ ${systemd_user_unitdir}/* \
+"
+CONFFILES_${PN} += "\
+ ${sysconfdir}/pipewire/pipewire.conf \
+"
+
+FILES_lib${PN} = "\
+ ${libdir}/libpipewire-*.so.* \
+"
+
+FILES_lib${PN}-modules = "\
+ ${libdir}/pipewire-*/* \
+"
+
+FILES_${PN}-spa-plugins = "\
+ ${bindir}/spa-* \
+ ${libdir}/spa/* \
+"
+
+FILES_${PN}-alsa = "\
+ ${libdir}/alsa-lib/* \
+ ${datadir}/alsa/alsa.conf.d/50-pipewire.conf \
+"
+
+FILES_${PN}-pulseaudio = "\
+ ${libdir}/libpulse*.so.* \
+"
+
+FILES_gstreamer${GST_VER}-${PN} = "\
+ ${libdir}/gstreamer-${GST_VER}/* \
+"
+
+RDEPENDS_lib${PN} += "lib${PN}-modules ${PN}-spa-plugins"
diff --git a/meta-pipewire/recipes-multimedia/pipewire/pipewire_git.bb b/meta-pipewire/recipes-multimedia/pipewire/pipewire_git.bb
new file mode 100644
index 00000000..65efe214
--- /dev/null
+++ b/meta-pipewire/recipes-multimedia/pipewire/pipewire_git.bb
@@ -0,0 +1,16 @@
+require pipewire.inc
+
+FILESEXTRAPATHS_prepend := "${THISDIR}:"
+
+SRC_URI = "gitsm://github.com/PipeWire/pipewire;protocol=https;branch=work \
+ file://0001-spa-include-install-missing-headers.patch \
+ file://0001-extensions-implement-Endpoint-ClientEndpoint-interfa.patch \
+ file://0002-pipewire-cli-add-support-for-printing-endpoint-info-.patch \
+ file://pipewire.conf \
+ "
+SRCREV = "4be788962e60891237f1f018627bf709ae3981e6"
+
+PV = "0.2.90+git${SRCPV}-1"
+S = "${WORKDIR}/git"
+
+RDEPENDS_${PN} += "wireplumber"