meta-pipewire: initial pipewire recipe 53/21453/4
authorGeorge Kiagiadakis <george.kiagiadakis@collabora.com>
Tue, 28 May 2019 12:34:45 +0000 (15:34 +0300)
committerGeorge Kiagiadakis <george.kiagiadakis@collabora.com>
Fri, 31 May 2019 10:44:41 +0000 (13:44 +0300)
Bug-AGL: SPEC-2473

Change-Id: Ide95be79adb69437564e94071d0315ae5cfd9ae2
Signed-off-by: George Kiagiadakis <george.kiagiadakis@collabora.com>
meta-pipewire/recipes-multimedia/pipewire/0001-extensions-implement-Endpoint-ClientEndpoint-interfa.patch [new file with mode: 0644]
meta-pipewire/recipes-multimedia/pipewire/0001-spa-include-install-missing-headers.patch [new file with mode: 0644]
meta-pipewire/recipes-multimedia/pipewire/0002-pipewire-cli-add-support-for-printing-endpoint-info-.patch [new file with mode: 0644]
meta-pipewire/recipes-multimedia/pipewire/pipewire.conf [new file with mode: 0644]
meta-pipewire/recipes-multimedia/pipewire/pipewire.inc [new file with mode: 0644]
meta-pipewire/recipes-multimedia/pipewire/pipewire_git.bb [new file with mode: 0644]

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 (file)
index 0000000..b78a909
--- /dev/null
@@ -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 (file)
index 0000000..01efe77
--- /dev/null
@@ -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 (file)
index 0000000..008c15f
--- /dev/null
@@ -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 (file)
index 0000000..d09ee8e
--- /dev/null
@@ -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 (file)
index 0000000..c1916c1
--- /dev/null
@@ -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 (file)
index 0000000..65efe21
--- /dev/null
@@ -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"