diff options
Diffstat (limited to 'meta-pipewire/recipes-multimedia')
31 files changed, 5990 insertions, 4863 deletions
diff --git a/meta-pipewire/recipes-multimedia/pipewire/pipewire-conf-agl/pipewire.conf.in b/meta-pipewire/recipes-multimedia/pipewire/pipewire-conf-agl/pipewire.conf.in index ad9ab7e6..24646593 100644 --- a/meta-pipewire/recipes-multimedia/pipewire/pipewire-conf-agl/pipewire.conf.in +++ b/meta-pipewire/recipes-multimedia/pipewire/pipewire-conf-agl/pipewire.conf.in @@ -1,11 +1,17 @@ # daemon config file for PipeWire version "0.2.9" # distributed by Automotive Grade Linux + +add-spa-lib audio.convert* audioconvert/libspa-audioconvert +add-spa-lib api.alsa.* alsa/libspa-alsa +add-spa-lib api.v4l2.* v4l2/libspa-v4l2 +add-spa-lib api.bluez5.* bluez5/libspa-bluez5 + load-module libpipewire-module-protocol-native -load-module libpipewire-module-spa-monitor alsa/libspa-alsa alsa-monitor alsa -#IF_BLUEZ5 load-module libpipewire-module-spa-monitor bluez5/libspa-bluez5 bluez5-monitor bluez5 +load-module libpipewire-module-spa-node-factory load-module libpipewire-module-client-node +load-module libpipewire-module-client-device load-module libpipewire-module-access -load-module libpipewire-module-audio-dsp +load-module libpipewire-module-adapter load-module libpipewire-module-link-factory -load-module libpipewire-module-endpoint +load-module libpipewire-module-session-manager exec /usr/bin/wireplumber diff --git a/meta-pipewire/recipes-multimedia/pipewire/pipewire.inc b/meta-pipewire/recipes-multimedia/pipewire/pipewire.inc index e9046e8e..de6d9b86 100644 --- a/meta-pipewire/recipes-multimedia/pipewire/pipewire.inc +++ b/meta-pipewire/recipes-multimedia/pipewire/pipewire.inc @@ -31,11 +31,13 @@ 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[audioconvert] = "-Daudioconvert=true,-Daudioconvert=false," PACKAGECONFIG[audiotestsrc] = "-Daudiotestsrc=true,-Daudiotestsrc=false, " PACKAGECONFIG[bluez5] = "-Dbluez5=true,-Dbluez5=false,bluez5 sbc" +PACKAGECONFIG[jack] = "-Djack=true,-Djack=false,jack" PACKAGECONFIG[v4l2] = "-Dv4l2=true,-Dv4l2=false,udev v4l-utils" PACKAGECONFIG[videotestsrc] = "-Dvideotestsrc=true,-Dvideotestsrc=false, " +PACKAGECONFIG[vulkan] = "-Dvulkan=true,-Dvulkan=false,vulkan" # alsa plugin to redirect audio to pipewire PACKAGECONFIG[pipewire-alsa] = "-Dpipewire-alsa=true,-Dpipewire-alsa=false,alsa-lib" diff --git a/meta-pipewire/recipes-multimedia/pipewire/pipewire/0005-arm-build-with-mno-unaligned-access.patch b/meta-pipewire/recipes-multimedia/pipewire/pipewire/0001-arm-build-with-mno-unaligned-access.patch index a670e7ff..3309836a 100644 --- a/meta-pipewire/recipes-multimedia/pipewire/pipewire/0005-arm-build-with-mno-unaligned-access.patch +++ b/meta-pipewire/recipes-multimedia/pipewire/pipewire/0001-arm-build-with-mno-unaligned-access.patch @@ -1,4 +1,4 @@ -From 2016605938f02835c75928648e99b25f7248aa5b Mon Sep 17 00:00:00 2001 +From e1d8927b7963a9bf2c09e50cd95943c3139313cf Mon Sep 17 00:00:00 2001 From: George Kiagiadakis <george.kiagiadakis@collabora.com> Date: Mon, 24 Jun 2019 12:19:20 +0300 Subject: [PATCH] arm: build with -mno-unaligned-access @@ -10,7 +10,7 @@ See also https://github.com/PipeWire/pipewire/issues/161 1 file changed, 5 insertions(+) diff --git a/meson.build b/meson.build -index 81303d27..f3cc6030 100644 +index aa60db35..5cd61fde 100644 --- a/meson.build +++ b/meson.build @@ -50,6 +50,11 @@ if cc.get_id() == 'gcc' @@ -26,5 +26,5 @@ index 81303d27..f3cc6030 100644 sse2_args = '-msse2' ssse3_args = '-mssse3' -- -2.20.1 +2.23.0 diff --git a/meta-pipewire/recipes-multimedia/pipewire/pipewire/0001-spa-include-install-missing-headers.patch b/meta-pipewire/recipes-multimedia/pipewire/pipewire/0001-spa-include-install-missing-headers.patch deleted file mode 100644 index 5b928117..00000000 --- a/meta-pipewire/recipes-multimedia/pipewire/pipewire/0001-spa-include-install-missing-headers.patch +++ /dev/null @@ -1,41 +0,0 @@ -From dbb6e10df8c2ba9b874eb9350d4cb93c62dba5a9 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 - -Upstream-Status: Accepted ---- - 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/pipewire/0002-extensions-implement-Endpoint-ClientEndpoint-interfa.patch b/meta-pipewire/recipes-multimedia/pipewire/pipewire/0002-extensions-implement-Endpoint-ClientEndpoint-interfa.patch deleted file mode 100644 index e49edf49..00000000 --- a/meta-pipewire/recipes-multimedia/pipewire/pipewire/0002-extensions-implement-Endpoint-ClientEndpoint-interfa.patch +++ /dev/null @@ -1,1563 +0,0 @@ -From 5afe82a430642c2f7e116941709a624b8fd73011 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] 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. - -Upstream-Status: Pending ---- - 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..211c0895 ---- /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 "value", 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(¶m)) < 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(¶m)) < 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(¶ms[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(¶m)) < 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/pipewire/0006-logger-print-timestamps-on-logged-messages.patch b/meta-pipewire/recipes-multimedia/pipewire/pipewire/0002-logger-print-timestamps-on-logged-messages.patch index 4d9117f0..6012aa56 100644 --- a/meta-pipewire/recipes-multimedia/pipewire/pipewire/0006-logger-print-timestamps-on-logged-messages.patch +++ b/meta-pipewire/recipes-multimedia/pipewire/pipewire/0002-logger-print-timestamps-on-logged-messages.patch @@ -1,4 +1,4 @@ -From 352c58357e5922b21d664c1f5a0b89a74f864f41 Mon Sep 17 00:00:00 2001 +From 289f58b815badd54a32f2409bae7abd7e5474327 Mon Sep 17 00:00:00 2001 From: George Kiagiadakis <george.kiagiadakis@collabora.com> Date: Wed, 3 Jul 2019 17:47:46 +0300 Subject: [PATCH] logger: print timestamps on logged messages @@ -14,7 +14,7 @@ Upstream-Status: Submitted [https://github.com/PipeWire/pipewire/pull/164] 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/spa/plugins/support/logger.c b/spa/plugins/support/logger.c -index 87ba3c21..2976601c 100644 +index 9ed2896b..4100102c 100644 --- a/spa/plugins/support/logger.c +++ b/spa/plugins/support/logger.c @@ -27,6 +27,7 @@ @@ -22,10 +22,10 @@ index 87ba3c21..2976601c 100644 #include <errno.h> #include <stdio.h> +#include <time.h> - #include <sys/eventfd.h> #include <spa/support/log.h> -@@ -70,6 +71,9 @@ impl_log_logv(struct spa_log *log, + #include <spa/support/loop.h> +@@ -72,6 +73,9 @@ impl_log_logv(void *object, const char *prefix = "", *suffix = ""; int size; bool do_trace; @@ -35,7 +35,7 @@ index 87ba3c21..2976601c 100644 if ((do_trace = (level == SPA_LOG_LEVEL_TRACE && impl->have_source))) level++; -@@ -86,8 +90,9 @@ impl_log_logv(struct spa_log *log, +@@ -88,8 +92,9 @@ impl_log_logv(void *object, } vsnprintf(text, sizeof(text), fmt, args); @@ -48,5 +48,5 @@ index 87ba3c21..2976601c 100644 if (SPA_UNLIKELY(do_trace)) { uint32_t index; -- -2.20.1 +2.23.0 diff --git a/meta-pipewire/recipes-multimedia/pipewire/pipewire/0007-alsa-make-corrections-on-the-timeout-based-on-how-fa.patch b/meta-pipewire/recipes-multimedia/pipewire/pipewire/0003-alsa-make-corrections-on-the-timeout-based-on-how-fa.patch index 86495014..17280da0 100644 --- a/meta-pipewire/recipes-multimedia/pipewire/pipewire/0007-alsa-make-corrections-on-the-timeout-based-on-how-fa.patch +++ b/meta-pipewire/recipes-multimedia/pipewire/pipewire/0003-alsa-make-corrections-on-the-timeout-based-on-how-fa.patch @@ -1,4 +1,4 @@ -From 05a3a926df4906cdf60f7978843c637bbf37714a Mon Sep 17 00:00:00 2001 +From 0c232229f3dc6b0cdf02ef44ae212b256c1828a3 Mon Sep 17 00:00:00 2001 From: George Kiagiadakis <george.kiagiadakis@collabora.com> Date: Tue, 9 Jul 2019 18:06:18 +0300 Subject: [PATCH] alsa: make corrections on the timeout based on how fast ALSA @@ -28,17 +28,17 @@ Fixes #163 Upstream-Status: Submitted [https://github.com/PipeWire/pipewire/pull/166] --- - spa/plugins/alsa/alsa-utils.c | 31 ++++++++++++++++++++++++++----- + spa/plugins/alsa/alsa-utils.c | 29 +++++++++++++++++++++++++---- spa/plugins/alsa/alsa-utils.h | 2 ++ - 2 files changed, 28 insertions(+), 5 deletions(-) + 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/spa/plugins/alsa/alsa-utils.c b/spa/plugins/alsa/alsa-utils.c -index 87582c1c..872969bf 100644 +index 7cf1d0d2..e8548345 100644 --- a/spa/plugins/alsa/alsa-utils.c +++ b/spa/plugins/alsa/alsa-utils.c -@@ -593,7 +593,21 @@ static int get_status(struct state *state, snd_pcm_sframes_t *delay) - - static int update_time(struct state *state, uint64_t nsec, snd_pcm_sframes_t delay, bool slave) +@@ -624,7 +624,21 @@ static int get_status(struct state *state, snd_pcm_uframes_t *delay, snd_pcm_ufr + static int update_time(struct state *state, uint64_t nsec, snd_pcm_sframes_t delay, + snd_pcm_sframes_t target, bool slave) { - double err, corr; + double err, corr, drift; @@ -58,9 +58,9 @@ index 87582c1c..872969bf 100644 + } if (state->stream == SND_PCM_STREAM_PLAYBACK) - err = delay - state->last_threshold; -@@ -650,11 +664,11 @@ static int update_time(struct state *state, uint64_t nsec, snd_pcm_sframes_t del - state->clock->rate_diff = corr; + err = delay - target; +@@ -677,11 +691,11 @@ static int update_time(struct state *state, uint64_t nsec, snd_pcm_sframes_t del + state->clock->next_nsec = state->next_time; } - spa_log_trace_fp(state->log, "slave:%d %"PRIu64" %f %ld %f %f %d", slave, nsec, @@ -74,7 +74,7 @@ index 87582c1c..872969bf 100644 state->last_threshold = state->threshold; return 0; -@@ -783,6 +797,10 @@ again: +@@ -812,6 +826,10 @@ again: goto again; state->sample_count += total_written; @@ -85,17 +85,8 @@ index 87582c1c..872969bf 100644 if (!state->alsa_started && total_written > 0) { spa_log_trace(state->log, "snd_pcm_start %lu", written); -@@ -935,7 +953,7 @@ static int handle_play(struct state *state, uint64_t nsec, snd_pcm_sframes_t del - { - int res; - -- if (delay >= state->last_threshold * 2) { -+ if (delay > state->last_threshold * 2) { - spa_log_trace(state->log, "early wakeup %ld %d", delay, state->threshold); - state->next_time = nsec + (delay - state->last_threshold) * SPA_NSEC_PER_SEC / state->rate; - return -EAGAIN; -@@ -944,6 +962,8 @@ static int handle_play(struct state *state, uint64_t nsec, snd_pcm_sframes_t del - if ((res = update_time(state, nsec, delay, false)) < 0) +@@ -981,6 +999,8 @@ static int handle_play(struct state *state, uint64_t nsec, + if ((res = update_time(state, nsec, delay, target, false)) < 0) return res; + state->fill_level = delay; @@ -103,19 +94,19 @@ index 87582c1c..872969bf 100644 if (spa_list_is_empty(&state->ready)) { struct spa_io_buffers *io = state->io; -@@ -1072,6 +1092,7 @@ int spa_alsa_start(struct state *state) +@@ -1120,6 +1140,7 @@ int spa_alsa_start(struct state *state) - state->slaved = is_slaved(state); + state->threshold = (state->duration * state->rate + state->rate_denom-1) / state->rate_denom; state->last_threshold = state->threshold; + state->fill_level = 0; init_loop(state); state->safety = 0.0; diff --git a/spa/plugins/alsa/alsa-utils.h b/spa/plugins/alsa/alsa-utils.h -index a862873f..b53890b5 100644 +index 110d4204..bab0f67b 100644 --- a/spa/plugins/alsa/alsa-utils.h +++ b/spa/plugins/alsa/alsa-utils.h -@@ -134,7 +134,9 @@ struct state { +@@ -141,7 +141,9 @@ struct state { int64_t sample_time; uint64_t next_time; uint64_t base_time; @@ -126,5 +117,5 @@ index a862873f..b53890b5 100644 double safety; -- -2.20.1 +2.23.0 diff --git a/meta-pipewire/recipes-multimedia/pipewire/pipewire/0010-gst-Implement-new-pwaudio-src-sink-elements-based-on.patch b/meta-pipewire/recipes-multimedia/pipewire/pipewire/0004-gst-Implement-new-pwaudio-src-sink-elements-based-on.patch index 6b1a6441..84b35e64 100644 --- a/meta-pipewire/recipes-multimedia/pipewire/pipewire/0010-gst-Implement-new-pwaudio-src-sink-elements-based-on.patch +++ b/meta-pipewire/recipes-multimedia/pipewire/pipewire/0004-gst-Implement-new-pwaudio-src-sink-elements-based-on.patch @@ -1,4 +1,4 @@ -From bbc875ec4268a88bf2465244e089b119011e7479 Mon Sep 17 00:00:00 2001 +From 2570b2f404ce094098e2244833ab7dddf62d02a0 Mon Sep 17 00:00:00 2001 From: George Kiagiadakis <george.kiagiadakis@collabora.com> Date: Tue, 19 Feb 2019 18:23:19 +0200 Subject: [PATCH] gst: Implement new pwaudio{src,sink} elements, based on @@ -1245,5 +1245,5 @@ index ad0e0801..0e922347 100644 pipewire_gst_c_args = [ -- -2.20.1 +2.23.0 diff --git a/meta-pipewire/recipes-multimedia/pipewire/pipewire/0004-pipewire-cli-add-command-to-modify-endpoint-control-.patch b/meta-pipewire/recipes-multimedia/pipewire/pipewire/0004-pipewire-cli-add-command-to-modify-endpoint-control-.patch deleted file mode 100644 index 4394d60d..00000000 --- a/meta-pipewire/recipes-multimedia/pipewire/pipewire/0004-pipewire-cli-add-command-to-modify-endpoint-control-.patch +++ /dev/null @@ -1,124 +0,0 @@ -From 824c8abf88e9ee82567c177145798b619298ab91 Mon Sep 17 00:00:00 2001 -From: George Kiagiadakis <george.kiagiadakis@collabora.com> -Date: Wed, 5 Jun 2019 14:57:37 +0300 -Subject: [PATCH] pipewire-cli: add command to modify endpoint control values - -Upstream-Status: Pending ---- - src/tools/pipewire-cli.c | 86 ++++++++++++++++++++++++++++++++++++++++ - 1 file changed, 86 insertions(+) - -diff --git a/src/tools/pipewire-cli.c b/src/tools/pipewire-cli.c -index 9511db82..b52ab100 100644 ---- a/src/tools/pipewire-cli.c -+++ b/src/tools/pipewire-cli.c -@@ -210,6 +210,7 @@ static bool do_export_node(struct data *data, const char *cmd, char *args, char - static bool do_enum_params(struct data *data, const char *cmd, char *args, char **error); - static bool do_permissions(struct data *data, const char *cmd, char *args, char **error); - static bool do_get_permissions(struct data *data, const char *cmd, char *args, char **error); -+static bool do_endpoint_control(struct data *data, const char *cmd, char *args, char **error); - - static struct command command_list[] = { - { "help", "Show this help", do_help }, -@@ -228,6 +229,7 @@ static struct command command_list[] = { - { "enum-params", "Enumerate params of an object <object-id> [<param-id-name>]", do_enum_params }, - { "permissions", "Set permissions for a client <client-id> <permissions>", do_permissions }, - { "get-permissions", "Get permissions of a client <client-id>", do_get_permissions }, -+ { "endpoint-control", "Set control value on an endpoint <object-id> <control-id> <type: b|i|l|d> <value>", do_endpoint_control }, - }; - - static bool do_help(struct data *data, const char *cmd, char *args, char **error) -@@ -1357,6 +1359,90 @@ static bool do_get_permissions(struct data *data, const char *cmd, char *args, c - return true; - } - -+static bool do_endpoint_control(struct data *data, const char *cmd, char *args, char **error) -+{ -+ struct remote_data *rd = data->current; -+ int n; -+ char *a[4]; -+ uint32_t id, control_id; -+ struct global *global; -+ char buffer[1024]; -+ struct spa_pod_builder b; -+ struct spa_pod_frame f; -+ struct spa_pod *param; -+ -+ n = pw_split_ip(args, WHITESPACE, 4, a); -+ if (n < 4) { -+ asprintf(error, "%s <object-id> <control-id> <type: b|i|l|d> <value>", cmd); -+ return false; -+ } -+ -+ id = atoi(a[0]); -+ global = pw_map_lookup(&rd->globals, id); -+ if (global == NULL) { -+ asprintf(error, "%s: unknown global %d", cmd, id); -+ return false; -+ } -+ if (global->type != PW_TYPE_INTERFACE_Endpoint) { -+ asprintf(error, "object %d is not an endpoint", atoi(a[0])); -+ return false; -+ } -+ if (global->proxy == NULL) { -+ if (!bind_global(rd, global, error)) -+ return false; -+ } -+ -+ control_id = atoi(a[1]); -+ -+ spa_pod_builder_init(&b, buffer, 1024); -+ spa_pod_builder_push_object (&b, &f, -+ PW_ENDPOINT_OBJECT_ParamControl, PW_ENDPOINT_PARAM_Control); -+ spa_pod_builder_add(&b, -+ PW_ENDPOINT_PARAM_CONTROL_id, SPA_POD_Int(control_id), -+ NULL); -+ -+ switch (*a[2]) { -+ case 'b': { -+ bool val = atoi(a[3]); -+ spa_pod_builder_add(&b, -+ PW_ENDPOINT_PARAM_CONTROL_value, SPA_POD_Bool(val), -+ NULL); -+ break; -+ } -+ case 'i': { -+ int val = atoi(a[3]); -+ spa_pod_builder_add(&b, -+ PW_ENDPOINT_PARAM_CONTROL_value, SPA_POD_Int(val), -+ NULL); -+ break; -+ } -+ case 'l': { -+ int64_t val = strtoll(a[3], NULL, 10); -+ spa_pod_builder_add(&b, -+ PW_ENDPOINT_PARAM_CONTROL_value, SPA_POD_Long(val), -+ NULL); -+ break; -+ } -+ case 'd': { -+ double val = strtod(a[3], NULL); -+ spa_pod_builder_add(&b, -+ PW_ENDPOINT_PARAM_CONTROL_value, SPA_POD_Double(val), -+ NULL); -+ break; -+ } -+ default: -+ asprintf(error, "%s: unknown value type %s", cmd, a[2]); -+ return false; -+ } -+ -+ param = spa_pod_builder_pop(&b, &f); -+ -+ pw_endpoint_proxy_set_param((struct pw_endpoint_proxy *) global->proxy, -+ PW_ENDPOINT_PARAM_Control, 0, param); -+ -+ return true; -+} -+ - static bool parse(struct data *data, char *buf, size_t size, char **error) - { - char *a[2]; --- -2.20.1 - diff --git a/meta-pipewire/recipes-multimedia/pipewire/pipewire/0012-gst-pwaudioringbuffer-request-pause-play-on-the-appr.patch b/meta-pipewire/recipes-multimedia/pipewire/pipewire/0005-gst-pwaudioringbuffer-request-pause-play-on-the-appr.patch index 3680cc35..7d0bc5a2 100644 --- a/meta-pipewire/recipes-multimedia/pipewire/pipewire/0012-gst-pwaudioringbuffer-request-pause-play-on-the-appr.patch +++ b/meta-pipewire/recipes-multimedia/pipewire/pipewire/0005-gst-pwaudioringbuffer-request-pause-play-on-the-appr.patch @@ -1,4 +1,4 @@ -From 1eb1e3a839f97ad4aa43c289f702c587a068a333 Mon Sep 17 00:00:00 2001 +From 9a01115acc2316908afaed004e2f042125b7f5e3 Mon Sep 17 00:00:00 2001 From: George Kiagiadakis <george.kiagiadakis@collabora.com> Date: Thu, 11 Jul 2019 16:21:17 +0300 Subject: [PATCH] gst/pwaudioringbuffer: request pause/play on the appropriate @@ -20,7 +20,7 @@ Upstream-Status: Submitted [https://github.com/PipeWire/pipewire/pull/140] 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/src/gst/gstpwaudioringbuffer.c b/src/gst/gstpwaudioringbuffer.c -index 181304e8..04259927 100644 +index 989b2cd7..97350f38 100644 --- a/src/gst/gstpwaudioringbuffer.c +++ b/src/gst/gstpwaudioringbuffer.c @@ -202,11 +202,16 @@ on_stream_state_changed (void *data, enum pw_stream_state old, @@ -72,5 +72,5 @@ index 181304e8..04259927 100644 } pw_thread_loop_signal (self->main_loop, FALSE); -- -2.20.1 +2.23.0 diff --git a/meta-pipewire/recipes-multimedia/pipewire/pipewire/0013-gst-pwaudioringbuffer-wait-only-for-STREAM_STATE_CON.patch b/meta-pipewire/recipes-multimedia/pipewire/pipewire/0006-gst-pwaudioringbuffer-wait-only-for-STREAM_STATE_CON.patch index 539e3a5e..124a3804 100644 --- a/meta-pipewire/recipes-multimedia/pipewire/pipewire/0013-gst-pwaudioringbuffer-wait-only-for-STREAM_STATE_CON.patch +++ b/meta-pipewire/recipes-multimedia/pipewire/pipewire/0006-gst-pwaudioringbuffer-wait-only-for-STREAM_STATE_CON.patch @@ -1,4 +1,4 @@ -From 1b2bf0f435f2912c32fbd7a6118ed9bfb41f031c Mon Sep 17 00:00:00 2001 +From f4903fe9c356b58737eb33fcfa389e006d18f801 Mon Sep 17 00:00:00 2001 From: George Kiagiadakis <george.kiagiadakis@collabora.com> Date: Thu, 11 Jul 2019 16:34:35 +0300 Subject: [PATCH] gst/pwaudioringbuffer: wait only for STREAM_STATE_CONFIGURE @@ -15,10 +15,10 @@ Upstream-Status: Submitted [https://github.com/PipeWire/pipewire/pull/140] 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/gst/gstpwaudioringbuffer.c b/src/gst/gstpwaudioringbuffer.c -index 04259927..b92b5feb 100644 +index 97350f38..3efec6ec 100644 --- a/src/gst/gstpwaudioringbuffer.c +++ b/src/gst/gstpwaudioringbuffer.c -@@ -444,9 +444,9 @@ gst_pw_audio_ring_buffer_acquire (GstAudioRingBuffer *buf, +@@ -442,9 +442,9 @@ gst_pw_audio_ring_buffer_acquire (GstAudioRingBuffer *buf, params, 1) < 0) goto start_error; @@ -31,5 +31,5 @@ index 04259927..b92b5feb 100644 pw_thread_loop_unlock (self->main_loop); -- -2.20.1 +2.23.0 diff --git a/meta-pipewire/recipes-multimedia/pipewire/pipewire/0014-gst-pwaudiosink-set-the-default-latency-time-buffer-.patch b/meta-pipewire/recipes-multimedia/pipewire/pipewire/0007-gst-pwaudiosink-set-the-default-latency-time-buffer-.patch index 6f15b7f7..9ae8eab3 100644 --- a/meta-pipewire/recipes-multimedia/pipewire/pipewire/0014-gst-pwaudiosink-set-the-default-latency-time-buffer-.patch +++ b/meta-pipewire/recipes-multimedia/pipewire/pipewire/0007-gst-pwaudiosink-set-the-default-latency-time-buffer-.patch @@ -1,4 +1,4 @@ -From 460ce06c9cc6fd7b0106e0ce8a265bbeff4ae406 Mon Sep 17 00:00:00 2001 +From 4d8e0de16717f250d22b24f335df8f27c67f2c52 Mon Sep 17 00:00:00 2001 From: George Kiagiadakis <george.kiagiadakis@collabora.com> Date: Thu, 11 Jul 2019 17:07:15 +0300 Subject: [PATCH] gst/pwaudiosink: set the default latency time (buffer size) @@ -33,5 +33,5 @@ index 6cb71385..069996c3 100644 static void -- -2.20.1 +2.23.0 diff --git a/meta-pipewire/recipes-multimedia/pipewire/pipewire/0008-audio-dsp-allow-mode-to-be-set-with-a-property.patch b/meta-pipewire/recipes-multimedia/pipewire/pipewire/0008-audio-dsp-allow-mode-to-be-set-with-a-property.patch deleted file mode 100644 index 681bae69..00000000 --- a/meta-pipewire/recipes-multimedia/pipewire/pipewire/0008-audio-dsp-allow-mode-to-be-set-with-a-property.patch +++ /dev/null @@ -1,45 +0,0 @@ -From 0c85e1110e32720785f861c7a85cf0636394eee4 Mon Sep 17 00:00:00 2001 -From: Julian Bouzas <julian.bouzas@collabora.com> -Date: Thu, 27 Jun 2019 12:44:39 -0400 -Subject: [PATCH] audio-dsp: allow mode to be set with a property - -Upstream-Status: Submitted [https://github.com/PipeWire/pipewire/pull/167] ---- - src/modules/module-audio-dsp/audio-dsp.c | 7 +++++-- - 1 file changed, 5 insertions(+), 2 deletions(-) - -diff --git a/src/modules/module-audio-dsp/audio-dsp.c b/src/modules/module-audio-dsp/audio-dsp.c -index be8a7592..68249307 100644 ---- a/src/modules/module-audio-dsp/audio-dsp.c -+++ b/src/modules/module-audio-dsp/audio-dsp.c -@@ -264,7 +264,7 @@ struct pw_node *pw_audio_dsp_new(struct pw_core *core, - { - struct pw_node *node; - struct node *n; -- const char *api, *alias, *str, *factory; -+ const char *api, *alias, *str, *factory, *mode; - char node_name[128]; - struct pw_properties *pr; - int i; -@@ -279,6 +279,7 @@ struct pw_node *pw_audio_dsp_new(struct pw_core *core, - pw_log_error("missing audio-dsp.name property"); - goto error; - } -+ mode = pw_properties_get(pr, "audio-dsp.mode"); - - snprintf(node_name, sizeof(node_name), "system_%s", alias); - for (i = 0; node_name[i]; i++) { -@@ -296,7 +297,9 @@ struct pw_node *pw_audio_dsp_new(struct pw_core *core, - if ((str = pw_properties_get(pr, "node.id")) != NULL) - pw_properties_set(pr, "node.session", str); - -- if (direction == PW_DIRECTION_OUTPUT) { -+ if (mode) { -+ factory = mode; -+ } else if (direction == PW_DIRECTION_OUTPUT) { - pw_properties_set(pr, "merger.monitor", "1"); - factory = "merge"; - } else { --- -2.20.1 - diff --git a/meta-pipewire/recipes-multimedia/pipewire/pipewire/0021-gst-pwaudioringbuffer-set-node.latency-to-get-schedu.patch b/meta-pipewire/recipes-multimedia/pipewire/pipewire/0008-gst-pwaudioringbuffer-set-node.latency-to-get-schedu.patch index 9b51a297..a6d7724f 100644 --- a/meta-pipewire/recipes-multimedia/pipewire/pipewire/0021-gst-pwaudioringbuffer-set-node.latency-to-get-schedu.patch +++ b/meta-pipewire/recipes-multimedia/pipewire/pipewire/0008-gst-pwaudioringbuffer-set-node.latency-to-get-schedu.patch @@ -1,4 +1,4 @@ -From 851738e3c5970a699d2313dec1cfeedb9d051d83 Mon Sep 17 00:00:00 2001 +From a9fb1fa9ce662ee3f06afda5fd9eb2182520ea4d Mon Sep 17 00:00:00 2001 From: George Kiagiadakis <george.kiagiadakis@collabora.com> Date: Tue, 20 Aug 2019 18:33:35 +0300 Subject: [PATCH] gst: pwaudioringbuffer: set node.latency to get scheduled @@ -10,10 +10,10 @@ Upstream-Status: Pending 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/gst/gstpwaudioringbuffer.c b/src/gst/gstpwaudioringbuffer.c -index b92b5feb..2314dd77 100644 +index 3efec6ec..8136b815 100644 --- a/src/gst/gstpwaudioringbuffer.c +++ b/src/gst/gstpwaudioringbuffer.c -@@ -403,11 +403,9 @@ gst_pw_audio_ring_buffer_acquire (GstAudioRingBuffer *buf, +@@ -402,11 +402,9 @@ gst_pw_audio_ring_buffer_acquire (GstAudioRingBuffer *buf, /* construct param & props objects */ @@ -26,16 +26,16 @@ index b92b5feb..2314dd77 100644 } spa_pod_builder_init (&b, buffer, sizeof (buffer)); -@@ -425,6 +423,9 @@ gst_pw_audio_ring_buffer_acquire (GstAudioRingBuffer *buf, - self->channels = GST_AUDIO_INFO_CHANNELS (&spec->info); +@@ -423,6 +421,9 @@ gst_pw_audio_ring_buffer_acquire (GstAudioRingBuffer *buf, + self->rate = GST_AUDIO_INFO_RATE (&spec->info); self->segoffset = 0; -+ pw_properties_setf(props, "node.latency", "%u/%u", ++ pw_properties_setf(props, PW_KEY_NODE_LATENCY, "%u/%u", + self->segsize / self->bpf, self->rate); + /* connect stream */ pw_thread_loop_lock (self->main_loop); -- -2.23.0.rc1 +2.23.0 diff --git a/meta-pipewire/recipes-multimedia/pipewire/pipewire/0009-alsa-do-not-expose-non-interleaved-formats-since-the.patch b/meta-pipewire/recipes-multimedia/pipewire/pipewire/0009-alsa-do-not-expose-non-interleaved-formats-since-the.patch new file mode 100644 index 00000000..8d8d4d11 --- /dev/null +++ b/meta-pipewire/recipes-multimedia/pipewire/pipewire/0009-alsa-do-not-expose-non-interleaved-formats-since-the.patch @@ -0,0 +1,37 @@ +From b80645ad1b348a99b2cbdc170e122dc06d367ea9 Mon Sep 17 00:00:00 2001 +From: George Kiagiadakis <george.kiagiadakis@collabora.com> +Date: Wed, 18 Sep 2019 12:31:36 +0300 +Subject: [PATCH] alsa: do not expose non-interleaved formats, since they won't + work + +This bug appeared when testing bluez-alsa nodes with pipewire. +bluez-alsa exposes also non-interleaved formats and if such a format +is picked, then nothing works because the converters are not working + +Upstream-Status: Inappropriate [workaround] +--- + spa/plugins/alsa/alsa-utils.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/spa/plugins/alsa/alsa-utils.c b/spa/plugins/alsa/alsa-utils.c +index e8548345..76c81c7c 100644 +--- a/spa/plugins/alsa/alsa-utils.c ++++ b/spa/plugins/alsa/alsa-utils.c +@@ -289,12 +289,14 @@ spa_alsa_enum_format(struct state *state, int seq, uint32_t start, uint32_t num, + spa_pod_builder_id(&b, fi->spa_format); + spa_pod_builder_id(&b, fi->spa_format); + } ++ /* + if (snd_pcm_access_mask_test(amask, SND_PCM_ACCESS_MMAP_NONINTERLEAVED) && + fi->spa_pformat != SPA_AUDIO_FORMAT_UNKNOWN) { + if (j++ == 0) + spa_pod_builder_id(&b, fi->spa_pformat); + spa_pod_builder_id(&b, fi->spa_pformat); + } ++ */ + } + } + if (j > 1) +-- +2.23.0 + diff --git a/meta-pipewire/recipes-multimedia/pipewire/pipewire/0009-audioconvert-do-setup-internal-links-and-buffers-als.patch b/meta-pipewire/recipes-multimedia/pipewire/pipewire/0009-audioconvert-do-setup-internal-links-and-buffers-als.patch deleted file mode 100644 index 87141e91..00000000 --- a/meta-pipewire/recipes-multimedia/pipewire/pipewire/0009-audioconvert-do-setup-internal-links-and-buffers-als.patch +++ /dev/null @@ -1,55 +0,0 @@ -From ddcda9fa6ec49168c5ddd9fbeda748c5fad18fce Mon Sep 17 00:00:00 2001 -From: George Kiagiadakis <george.kiagiadakis@collabora.com> -Date: Wed, 10 Jul 2019 14:53:15 +0300 -Subject: [PATCH] audioconvert: do setup internal links and buffers also in - convert mode - -Upstream-Status: Submitted [https://github.com/PipeWire/pipewire/pull/167] ---- - spa/plugins/audioconvert/audioconvert.c | 10 ++++++---- - 1 file changed, 6 insertions(+), 4 deletions(-) - -diff --git a/spa/plugins/audioconvert/audioconvert.c b/spa/plugins/audioconvert/audioconvert.c -index fa8dec97..0af0732d 100644 ---- a/spa/plugins/audioconvert/audioconvert.c -+++ b/spa/plugins/audioconvert/audioconvert.c -@@ -79,6 +79,8 @@ struct impl { - #define MODE_MERGE 1 - #define MODE_CONVERT 2 - int mode; -+ bool fmt_is_set[2]; -+ bool buffers_set[2]; - bool started; - - struct spa_handle *hnd_fmt[2]; -@@ -791,11 +793,11 @@ impl_node_port_set_param(struct spa_node *node, - if (id == SPA_PARAM_Format) { - if (param == NULL) - clean_convert(this); -- else if ((direction == SPA_DIRECTION_OUTPUT && this->mode == MODE_MERGE) || -- (direction == SPA_DIRECTION_INPUT && this->mode == MODE_SPLIT)) { -+ else if (this->fmt_is_set[SPA_DIRECTION_REVERSE(direction)]) { - if ((res = setup_convert(this)) < 0) - return res; - } -+ this->fmt_is_set[direction] = (param != NULL); - } - return res; - } -@@ -824,11 +826,11 @@ impl_node_port_use_buffers(struct spa_node *node, - direction, port_id, buffers, n_buffers)) < 0) - return res; - -- if ((direction == SPA_DIRECTION_OUTPUT && this->mode == MODE_MERGE) || -- (direction == SPA_DIRECTION_INPUT && this->mode == MODE_SPLIT)) { -+ if (buffers && this->buffers_set[SPA_DIRECTION_REVERSE(direction)]) { - if ((res = setup_buffers(this, SPA_DIRECTION_INPUT)) < 0) - return res; - } -+ this->buffers_set[direction] = (buffers != NULL); - return res; - } - --- -2.20.1 - diff --git a/meta-pipewire/recipes-multimedia/pipewire/pipewire/0010-bluez-monitor-fix-usage-of-pw_properties_setf-withou.patch b/meta-pipewire/recipes-multimedia/pipewire/pipewire/0010-bluez-monitor-fix-usage-of-pw_properties_setf-withou.patch new file mode 100644 index 00000000..284d01d0 --- /dev/null +++ b/meta-pipewire/recipes-multimedia/pipewire/pipewire/0010-bluez-monitor-fix-usage-of-pw_properties_setf-withou.patch @@ -0,0 +1,27 @@ +From 6f3335cfc06053e3ea5598058e0a2581e7ffa4af Mon Sep 17 00:00:00 2001 +From: George Kiagiadakis <george.kiagiadakis@collabora.com> +Date: Sun, 22 Sep 2019 17:37:49 +0300 +Subject: [PATCH] bluez-monitor: fix usage of pw_properties_setf without a + format string + +Upstream-Status: Submitted [https://github.com/PipeWire/pipewire/pull/189] +--- + src/examples/bluez-monitor.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/examples/bluez-monitor.c b/src/examples/bluez-monitor.c +index 1ae5ffe0..4bdfeb0d 100644 +--- a/src/examples/bluez-monitor.c ++++ b/src/examples/bluez-monitor.c +@@ -123,7 +123,7 @@ static struct bluez5_node *bluez5_create_node(struct bluez5_object *obj, uint32_ + + pw_properties_setf(node->props, PW_KEY_NODE_NAME, "%s.%s", info->factory_name, str); + pw_properties_set(node->props, PW_KEY_NODE_DESCRIPTION, str); +- pw_properties_setf(node->props, "factory.name", info->factory_name); ++ pw_properties_set(node->props, "factory.name", info->factory_name); + + node->monitor = monitor; + node->object = obj; +-- +2.23.0 + diff --git a/meta-pipewire/recipes-multimedia/pipewire/pipewire/0011-gst-pwaudioringbuffer-make-the-buffer-size-sensitive.patch b/meta-pipewire/recipes-multimedia/pipewire/pipewire/0011-gst-pwaudioringbuffer-make-the-buffer-size-sensitive.patch deleted file mode 100644 index 5ffabb6d..00000000 --- a/meta-pipewire/recipes-multimedia/pipewire/pipewire/0011-gst-pwaudioringbuffer-make-the-buffer-size-sensitive.patch +++ /dev/null @@ -1,60 +0,0 @@ -From 6e289d0058d71bc433d1918a8bbf3305f3e4f517 Mon Sep 17 00:00:00 2001 -From: Julian Bouzas <julian.bouzas@collabora.com> -Date: Tue, 7 May 2019 10:36:35 -0400 -Subject: [PATCH] gst/pwaudioringbuffer: make the buffer size sensitive to the - number of channels - -Upstream-Status: Submitted [https://github.com/PipeWire/pipewire/pull/140] ---- - src/gst/gstpwaudioringbuffer.c | 6 ++++-- - src/gst/gstpwaudioringbuffer.h | 1 + - 2 files changed, 5 insertions(+), 2 deletions(-) - -diff --git a/src/gst/gstpwaudioringbuffer.c b/src/gst/gstpwaudioringbuffer.c -index 989b2cd7..181304e8 100644 ---- a/src/gst/gstpwaudioringbuffer.c -+++ b/src/gst/gstpwaudioringbuffer.c -@@ -246,17 +246,18 @@ on_stream_format_changed (void *data, const struct spa_pod *format) - const struct spa_pod *params[1]; - struct spa_pod_builder b = { NULL }; - uint8_t buffer[512]; -+ const gint b_size = self->segsize * self->channels; - - spa_pod_builder_init (&b, buffer, sizeof (buffer)); - params[0] = spa_pod_builder_add_object (&b, - SPA_TYPE_OBJECT_ParamBuffers, SPA_PARAM_Buffers, - SPA_PARAM_BUFFERS_buffers, SPA_POD_CHOICE_RANGE_Int(16, 1, INT32_MAX), - SPA_PARAM_BUFFERS_blocks, SPA_POD_Int(1), -- SPA_PARAM_BUFFERS_size, SPA_POD_Int(self->segsize), -+ SPA_PARAM_BUFFERS_size, SPA_POD_Int(b_size), - SPA_PARAM_BUFFERS_stride, SPA_POD_Int(self->bpf), - SPA_PARAM_BUFFERS_align, SPA_POD_Int(16)); - -- GST_DEBUG_OBJECT (self->elem, "doing finish format, buffer size:%d", self->segsize); -+ GST_DEBUG_OBJECT (self->elem, "doing finish format, buffer size:%d", b_size); - pw_stream_finish_format (self->stream, 0, params, 1); - } - -@@ -402,6 +403,7 @@ gst_pw_audio_ring_buffer_acquire (GstAudioRingBuffer *buf, - self->segsize = spec->segsize; - self->bpf = GST_AUDIO_INFO_BPF (&spec->info); - self->rate = GST_AUDIO_INFO_RATE (&spec->info); -+ self->channels = GST_AUDIO_INFO_CHANNELS (&spec->info); - self->segoffset = 0; - - /* connect stream */ -diff --git a/src/gst/gstpwaudioringbuffer.h b/src/gst/gstpwaudioringbuffer.h -index f47f668a..f600f012 100644 ---- a/src/gst/gstpwaudioringbuffer.h -+++ b/src/gst/gstpwaudioringbuffer.h -@@ -64,6 +64,7 @@ struct _GstPwAudioRingBuffer - gint segsize; - gint bpf; - gint rate; -+ gint channels; - - /* on_stream_process() state */ - gint segoffset; --- -2.20.1 - diff --git a/meta-pipewire/recipes-multimedia/pipewire/pipewire/0011-meson-revert-version-check-to-require-meson-0.47-not.patch b/meta-pipewire/recipes-multimedia/pipewire/pipewire/0011-meson-revert-version-check-to-require-meson-0.47-not.patch new file mode 100644 index 00000000..8471276b --- /dev/null +++ b/meta-pipewire/recipes-multimedia/pipewire/pipewire/0011-meson-revert-version-check-to-require-meson-0.47-not.patch @@ -0,0 +1,30 @@ +From 544adb27b33843c0bb970ddb07485f57c2dad7a9 Mon Sep 17 00:00:00 2001 +From: George Kiagiadakis <george.kiagiadakis@collabora.com> +Date: Sun, 22 Sep 2019 17:59:19 +0300 +Subject: [PATCH] meson: revert version check to require meson 0.47, not 0.50 + +meson 0.50 is not really needed, but there are some strange warnings +if you require an older version; in any case, AGL does not have 0.50 +yet, so let's not fail compilation because of that... + +Upstream-Status: Inappropriate [workaround] +--- + meson.build | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/meson.build b/meson.build +index 5cd61fde..51a839fa 100644 +--- a/meson.build ++++ b/meson.build +@@ -1,7 +1,7 @@ + project('pipewire', ['c' ], + version : '0.2.9', + license : 'MIT', +- meson_version : '>= 0.50.0', ++ meson_version : '>= 0.47.0', + default_options : [ 'warning_level=1', + 'c_std=gnu99', + 'buildtype=debugoptimized' ]) +-- +2.23.0 + diff --git a/meta-pipewire/recipes-multimedia/pipewire/pipewire/0012-extensions-implement-new-session-manager-extension.patch b/meta-pipewire/recipes-multimedia/pipewire/pipewire/0012-extensions-implement-new-session-manager-extension.patch new file mode 100644 index 00000000..cdb030e1 --- /dev/null +++ b/meta-pipewire/recipes-multimedia/pipewire/pipewire/0012-extensions-implement-new-session-manager-extension.patch @@ -0,0 +1,5715 @@ +From acbce75de9587917cfa659ebc0e3404b6b1d4c29 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] extensions: implement new session manager extension + +This extension, implemented in module-session-manager, implements +a set of objects that are useful for session managers. + +Upstream-Status: Pending +--- + src/extensions/meson.build | 9 + + src/extensions/session-manager.h | 34 + + .../session-manager/impl-interfaces.h | 329 +++ + src/extensions/session-manager/interfaces.h | 465 ++++ + src/extensions/session-manager/introspect.h | 131 + + src/extensions/session-manager/keys.h | 40 + + src/modules/meson.build | 17 + + src/modules/module-session-manager.c | 56 + + .../module-session-manager/client-endpoint.c | 270 +++ + .../module-session-manager/client-endpoint.h | 60 + + .../module-session-manager/client-session.c | 270 +++ + .../module-session-manager/client-session.h | 62 + + .../module-session-manager/endpoint-link.c | 359 +++ + .../module-session-manager/endpoint-link.h | 64 + + .../module-session-manager/endpoint-stream.c | 329 +++ + .../module-session-manager/endpoint-stream.h | 64 + + src/modules/module-session-manager/endpoint.c | 343 +++ + src/modules/module-session-manager/endpoint.h | 61 + + .../module-session-manager/protocol-native.c | 2125 +++++++++++++++++ + src/modules/module-session-manager/session.c | 341 +++ + src/modules/module-session-manager/session.h | 61 + + src/pipewire/pipewire.c | 6 + + src/pipewire/type.h | 7 +- + 23 files changed, 5502 insertions(+), 1 deletion(-) + create mode 100644 src/extensions/session-manager.h + create mode 100644 src/extensions/session-manager/impl-interfaces.h + create mode 100644 src/extensions/session-manager/interfaces.h + create mode 100644 src/extensions/session-manager/introspect.h + create mode 100644 src/extensions/session-manager/keys.h + create mode 100644 src/modules/module-session-manager.c + create mode 100644 src/modules/module-session-manager/client-endpoint.c + create mode 100644 src/modules/module-session-manager/client-endpoint.h + create mode 100644 src/modules/module-session-manager/client-session.c + create mode 100644 src/modules/module-session-manager/client-session.h + create mode 100644 src/modules/module-session-manager/endpoint-link.c + create mode 100644 src/modules/module-session-manager/endpoint-link.h + create mode 100644 src/modules/module-session-manager/endpoint-stream.c + create mode 100644 src/modules/module-session-manager/endpoint-stream.h + create mode 100644 src/modules/module-session-manager/endpoint.c + create mode 100644 src/modules/module-session-manager/endpoint.h + create mode 100644 src/modules/module-session-manager/protocol-native.c + create mode 100644 src/modules/module-session-manager/session.c + create mode 100644 src/modules/module-session-manager/session.h + +diff --git a/src/extensions/meson.build b/src/extensions/meson.build +index a7f5d3cb..95377faa 100644 +--- a/src/extensions/meson.build ++++ b/src/extensions/meson.build +@@ -1,6 +1,15 @@ ++pipewire_ext_sm_headers = [ ++ 'session-manager/impl-interfaces.h', ++ 'session-manager/interfaces.h', ++ 'session-manager/introspect.h', ++ 'session-manager/keys.h', ++] ++ + pipewire_ext_headers = [ + 'client-node.h', + 'protocol-native.h', ++ 'session-manager.h', + ] + ++install_headers(pipewire_ext_sm_headers, subdir : 'pipewire/extensions/session-manager') + install_headers(pipewire_ext_headers, subdir : 'pipewire/extensions') +diff --git a/src/extensions/session-manager.h b/src/extensions/session-manager.h +new file mode 100644 +index 00000000..95e759b0 +--- /dev/null ++++ b/src/extensions/session-manager.h +@@ -0,0 +1,34 @@ ++/* 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_SESSION_MANAGER_H ++#define PIPEWIRE_EXT_SESSION_MANAGER_H ++ ++#include "session-manager/introspect.h" ++#include "session-manager/interfaces.h" ++#include "session-manager/impl-interfaces.h" ++#include "session-manager/keys.h" ++ ++#endif /* PIPEWIRE_EXT_SESSION_MANAGER_H */ +diff --git a/src/extensions/session-manager/impl-interfaces.h b/src/extensions/session-manager/impl-interfaces.h +new file mode 100644 +index 00000000..66daa0b9 +--- /dev/null ++++ b/src/extensions/session-manager/impl-interfaces.h +@@ -0,0 +1,329 @@ ++/* 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_SESSION_MANAGER_IMPL_INTERFACES_H ++#define PIPEWIRE_EXT_SESSION_MANAGER_IMPL_INTERFACES_H ++ ++#include <spa/utils/defs.h> ++#include <spa/utils/hook.h> ++#include <errno.h> ++ ++#include "introspect.h" ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++#define PW_VERSION_CLIENT_ENDPOINT_PROXY 0 ++struct pw_client_endpoint_proxy { struct spa_interface iface; }; ++ ++#define PW_CLIENT_ENDPOINT_PROXY_EVENT_SET_ID 0 ++#define PW_CLIENT_ENDPOINT_PROXY_EVENT_SET_SESSION_ID 1 ++#define PW_CLIENT_ENDPOINT_PROXY_EVENT_SET_PARAM 2 ++#define PW_CLIENT_ENDPOINT_PROXY_EVENT_STREAM_SET_PARAM 3 ++#define PW_CLIENT_ENDPOINT_PROXY_EVENT_NUM 4 ++ ++struct pw_client_endpoint_proxy_events { ++#define PW_VERSION_CLIENT_ENDPOINT_PROXY_EVENTS 0 ++ uint32_t version; /**< version of this structure */ ++ ++ /** ++ * Sets the id of the \a endpoint. ++ * ++ * On endpoint implementations, this is called by the server to notify ++ * the implementation of the assigned global id of the endpoint. The ++ * implementation is obliged to set this id in the ++ * #struct pw_endpoint_info \a id field. The implementation should also ++ * not emit the info() event before this method is called. ++ * ++ * \param endpoint a #pw_endpoint ++ * \param id the global id assigned to this endpoint ++ * ++ * \return 0 on success ++ * -EINVAL when the id has already been set ++ * -ENOTSUP on the server-side endpoint implementation ++ */ ++ int (*set_id) (void *endpoint, uint32_t id); ++ ++ /** ++ * Sets the session id of the \a endpoint. ++ * ++ * On endpoints that are not session masters, this method notifies ++ * the implementation that it has been associated with a session. ++ * The implementation is obliged to set this id in the ++ * #struct pw_endpoint_info \a session_id field. ++ * ++ * \param endpoint a #pw_endpoint ++ * \param id the session id associated with this endpoint ++ * ++ * \return 0 on success ++ * -EINVAL when the session id has already been set ++ * -ENOTSUP when the endpoint is a session master ++ */ ++ int (*set_session_id) (void *endpoint, uint32_t session_id); ++ ++ /** ++ * Set the configurable parameter in \a endpoint. ++ * ++ * Usually, \a param will be obtained from enum_params and then ++ * modified but it is also possible to set another spa_pod ++ * as long as its keys and types match a supported object. ++ * ++ * Objects with property keys that are not known are ignored. ++ * ++ * This function must be called from the main thread. ++ * ++ * \param endpoint a #struct pw_endpoint ++ * \param id the parameter id to configure ++ * \param flags additional flags ++ * \param param the parameter to configure ++ * ++ * \return 0 on success ++ * -EINVAL when \a endpoint is NULL ++ * -ENOTSUP when there are no parameters implemented on \a endpoint ++ * -ENOENT the parameter is unknown ++ */ ++ int (*set_param) (void *endpoint, ++ uint32_t id, uint32_t flags, ++ const struct spa_pod *param); ++ ++ /** ++ * Set a parameter on \a stream_id of \a endpoint. ++ * ++ * When \a param is NULL, the parameter will be unset. ++ * ++ * This function must be called from the main thread. ++ * ++ * \param endpoint a #struct pw_endpoint ++ * \param stream_id the stream to configure ++ * \param id the parameter id to set ++ * \param flags optional flags ++ * \param param a #struct spa_pod with the parameter to set ++ * \return 0 on success ++ * 1 on success, the value of \a param might have been ++ * changed depending on \a flags and the final value can ++ * be found by doing stream_enum_params. ++ * -EINVAL when \a endpoint is NULL or invalid arguments are given ++ * -ESRCH when the type or size of a property is not correct. ++ * -ENOENT when the param id is not found ++ */ ++ int (*stream_set_param) (void *endpoint, uint32_t stream_id, ++ uint32_t id, uint32_t flags, ++ const struct spa_pod *param); ++}; ++ ++#define PW_CLIENT_ENDPOINT_PROXY_METHOD_ADD_LISTENER 0 ++#define PW_CLIENT_ENDPOINT_PROXY_METHOD_UPDATE 1 ++#define PW_CLIENT_ENDPOINT_PROXY_METHOD_STREAM_UPDATE 2 ++#define PW_CLIENT_ENDPOINT_PROXY_METHOD_NUM 3 ++ ++struct pw_client_endpoint_proxy_methods { ++#define PW_VERSION_CLIENT_ENDPOINT_PROXY_METHODS 0 ++ uint32_t version; /**< version of this structure */ ++ ++ int (*add_listener) (void *object, ++ struct spa_hook *listener, ++ const struct pw_client_endpoint_proxy_events *events, ++ void *data); ++ ++ /** Update endpoint information */ ++ int (*update) (void *object, ++#define PW_CLIENT_ENDPOINT_UPDATE_PARAMS (1 << 0) ++#define PW_CLIENT_ENDPOINT_UPDATE_INFO (1 << 1) ++ uint32_t change_mask, ++ uint32_t n_params, ++ const struct spa_pod **params, ++ const struct pw_endpoint_info *info); ++ ++ /** Update stream information */ ++ int (*stream_update) (void *object, ++ uint32_t stream_id, ++#define PW_CLIENT_ENDPOINT_STREAM_UPDATE_PARAMS (1 << 0) ++#define PW_CLIENT_ENDPOINT_STREAM_UPDATE_INFO (1 << 1) ++#define PW_CLIENT_ENDPOINT_STREAM_UPDATE_DESTROYED (1 << 2) ++ uint32_t change_mask, ++ uint32_t n_params, ++ const struct spa_pod **params, ++ const struct pw_endpoint_stream_info *info); ++}; ++ ++#define pw_client_endpoint_proxy_method(o,method,version,...) \ ++({ \ ++ int _res = -ENOTSUP; \ ++ struct pw_client_endpoint_proxy *_p = o; \ ++ spa_interface_call_res(&_p->iface, \ ++ struct pw_client_endpoint_proxy_methods, _res, \ ++ method, version, ##__VA_ARGS__); \ ++ _res; \ ++}) ++ ++#define pw_client_endpoint_proxy_add_listener(o,...) pw_client_endpoint_proxy_method(o,add_listener,0,__VA_ARGS__) ++#define pw_client_endpoint_proxy_update(o,...) pw_client_endpoint_proxy_method(o,update,0,__VA_ARGS__) ++#define pw_client_endpoint_proxy_stream_update(o,...) pw_client_endpoint_proxy_method(o,stream_update,0,__VA_ARGS__) ++ ++ ++#define PW_VERSION_CLIENT_SESSION_PROXY 0 ++struct pw_client_session_proxy { struct spa_interface iface; }; ++ ++#define PW_CLIENT_SESSION_PROXY_EVENT_SET_ID 0 ++#define PW_CLIENT_SESSION_PROXY_EVENT_SET_PARAM 1 ++#define PW_CLIENT_SESSION_PROXY_EVENT_LINK_SET_PARAM 2 ++#define PW_CLIENT_SESSION_PROXY_EVENT_CREATE_LINK 3 ++#define PW_CLIENT_SESSION_PROXY_EVENT_DESTROY_LINK 4 ++#define PW_CLIENT_SESSION_PROXY_EVENT_LINK_REQUEST_STATE 5 ++#define PW_CLIENT_SESSION_PROXY_EVENT_NUM 6 ++ ++struct pw_client_session_proxy_events { ++#define PW_VERSION_CLIENT_SESSION_PROXY_EVENTS 0 ++ uint32_t version; /**< version of this structure */ ++ ++ /** ++ * Sets the id of the \a session. ++ * ++ * On session implementations, this is called by the server to notify ++ * the implementation of the assigned global id of the session. The ++ * implementation is obliged to set this id in the ++ * #struct pw_session_info \a id field. The implementation should also ++ * not emit the info() event before this method is called. ++ * ++ * \param session a #pw_session ++ * \param id the global id assigned to this session ++ * ++ * \return 0 on success ++ * -EINVAL when the id has already been set ++ * -ENOTSUP on the server-side session implementation ++ */ ++ int (*set_id) (void *session, uint32_t id); ++ ++ /** ++ * Set the configurable parameter in \a session. ++ * ++ * Usually, \a param will be obtained from enum_params and then ++ * modified but it is also possible to set another spa_pod ++ * as long as its keys and types match a supported object. ++ * ++ * Objects with property keys that are not known are ignored. ++ * ++ * This function must be called from the main thread. ++ * ++ * \param session a #struct pw_session ++ * \param id the parameter id to configure ++ * \param flags additional flags ++ * \param param the parameter to configure ++ * ++ * \return 0 on success ++ * -EINVAL when \a session is NULL ++ * -ENOTSUP when there are no parameters implemented on \a session ++ * -ENOENT the parameter is unknown ++ */ ++ int (*set_param) (void *session, ++ uint32_t id, uint32_t flags, ++ const struct spa_pod *param); ++ ++ /** ++ * Set a parameter on \a link_id of \a session. ++ * ++ * When \a param is NULL, the parameter will be unset. ++ * ++ * This function must be called from the main thread. ++ * ++ * \param session a #struct pw_session ++ * \param link_id the link to configure ++ * \param id the parameter id to set ++ * \param flags optional flags ++ * \param param a #struct spa_pod with the parameter to set ++ * \return 0 on success ++ * 1 on success, the value of \a param might have been ++ * changed depending on \a flags and the final value can ++ * be found by doing link_enum_params. ++ * -EINVAL when \a session is NULL or invalid arguments are given ++ * -ESRCH when the type or size of a property is not correct. ++ * -ENOENT when the param id is not found ++ */ ++ int (*link_set_param) (void *session, uint32_t link_id, ++ uint32_t id, uint32_t flags, ++ const struct spa_pod *param); ++ ++ int (*create_link) (void *session, const struct spa_dict *props); ++ ++ int (*destroy_link) (void *session, uint32_t link_id); ++ ++ int (*link_request_state) (void *session, uint32_t link_id, uint32_t state); ++}; ++ ++#define PW_CLIENT_SESSION_PROXY_METHOD_ADD_LISTENER 0 ++#define PW_CLIENT_SESSION_PROXY_METHOD_UPDATE 1 ++#define PW_CLIENT_SESSION_PROXY_METHOD_LINK_UPDATE 2 ++#define PW_CLIENT_SESSION_PROXY_METHOD_NUM 3 ++ ++struct pw_client_session_proxy_methods { ++#define PW_VERSION_CLIENT_SESSION_PROXY_METHODS 0 ++ uint32_t version; /**< version of this structure */ ++ ++ int (*add_listener) (void *object, ++ struct spa_hook *listener, ++ const struct pw_client_session_proxy_events *events, ++ void *data); ++ ++ /** Update session information */ ++ int (*update) (void *object, ++#define PW_CLIENT_SESSION_UPDATE_PARAMS (1 << 0) ++#define PW_CLIENT_SESSION_UPDATE_INFO (1 << 1) ++ uint32_t change_mask, ++ uint32_t n_params, ++ const struct spa_pod **params, ++ const struct pw_session_info *info); ++ ++ /** Update link information */ ++ int (*link_update) (void *object, ++ uint32_t link_id, ++#define PW_CLIENT_SESSION_LINK_UPDATE_PARAMS (1 << 0) ++#define PW_CLIENT_SESSION_LINK_UPDATE_INFO (1 << 1) ++#define PW_CLIENT_SESSION_LINK_UPDATE_DESTROYED (1 << 2) ++ uint32_t change_mask, ++ uint32_t n_params, ++ const struct spa_pod **params, ++ const struct pw_endpoint_link_info *info); ++}; ++ ++#define pw_client_session_proxy_method(o,method,version,...) \ ++({ \ ++ int _res = -ENOTSUP; \ ++ struct pw_client_session_proxy *_p = o; \ ++ spa_interface_call_res(&_p->iface, \ ++ struct pw_client_session_proxy_methods, _res, \ ++ method, version, ##__VA_ARGS__); \ ++ _res; \ ++}) ++ ++#define pw_client_session_proxy_add_listener(o,...) pw_client_session_proxy_method(o,add_listener,0,__VA_ARGS__) ++#define pw_client_session_proxy_update(o,...) pw_client_session_proxy_method(o,update,0,__VA_ARGS__) ++#define pw_client_session_proxy_link_update(o,...) pw_client_session_proxy_method(o,link_update,0,__VA_ARGS__) ++ ++#ifdef __cplusplus ++} /* extern "C" */ ++#endif ++ ++#endif /* PIPEWIRE_EXT_SESSION_MANAGER_IMPL_INTERFACES_H */ +diff --git a/src/extensions/session-manager/interfaces.h b/src/extensions/session-manager/interfaces.h +new file mode 100644 +index 00000000..0651e8bf +--- /dev/null ++++ b/src/extensions/session-manager/interfaces.h +@@ -0,0 +1,465 @@ ++/* 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_SESSION_MANAGER_INTERFACES_H ++#define PIPEWIRE_EXT_SESSION_MANAGER_INTERFACES_H ++ ++#include <spa/utils/defs.h> ++#include <spa/utils/hook.h> ++ ++#include "introspect.h" ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++#define PW_VERSION_SESSION_PROXY 0 ++struct pw_session_proxy { struct spa_interface iface; }; ++#define PW_VERSION_ENDPOINT_PROXY 0 ++struct pw_endpoint_proxy { struct spa_interface iface; }; ++#define PW_VERSION_ENDPOINT_STREAM_PROXY 0 ++struct pw_endpoint_stream_proxy { struct spa_interface iface; }; ++#define PW_VERSION_ENDPOINT_LINK_PROXY 0 ++struct pw_endpoint_link_proxy { struct spa_interface iface; }; ++ ++/* Session */ ++ ++#define PW_SESSION_PROXY_EVENT_INFO 0 ++#define PW_SESSION_PROXY_EVENT_PARAM 1 ++#define PW_SESSION_PROXY_EVENT_NUM 2 ++ ++struct pw_session_proxy_events { ++#define PW_VERSION_SESSION_PROXY_EVENTS 0 ++ uint32_t version; /**< version of this structure */ ++ ++ /** ++ * Notify session info ++ * ++ * \param info info about the session ++ */ ++ void (*info) (void *object, const struct pw_session_info *info); ++ ++ /** ++ * Notify a session 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); ++}; ++ ++#define PW_SESSION_PROXY_METHOD_ADD_LISTENER 0 ++#define PW_SESSION_PROXY_METHOD_SUBSCRIBE_PARAMS 1 ++#define PW_SESSION_PROXY_METHOD_ENUM_PARAMS 2 ++#define PW_SESSION_PROXY_METHOD_SET_PARAM 3 ++#define PW_SESSION_PROXY_METHOD_CREATE_LINK 4 ++#define PW_SESSION_PROXY_METHOD_NUM 5 ++ ++struct pw_session_proxy_methods { ++#define PW_VERSION_SESSION_PROXY_METHODS 0 ++ uint32_t version; /**< version of this structure */ ++ ++ int (*add_listener) (void *object, ++ struct spa_hook *listener, ++ const struct pw_session_proxy_events *events, ++ void *data); ++ ++ /** ++ * 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 session parameters ++ * ++ * Start enumeration of session parameters. For each param, a ++ * param event will be emited. ++ * ++ * \param seq a sequence number returned in the reply ++ * \param id the parameter id to enumerate ++ * \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 session ++ * ++ * \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); ++ ++ int (*create_link) (void *object, const struct spa_dict *props); ++}; ++ ++#define pw_session_proxy_method(o,method,version,...) \ ++({ \ ++ int _res = -ENOTSUP; \ ++ struct pw_session_proxy *_p = o; \ ++ spa_interface_call_res(&_p->iface, \ ++ struct pw_session_proxy_methods, _res, \ ++ method, version, ##__VA_ARGS__); \ ++ _res; \ ++}) ++ ++#define pw_session_proxy_add_listener(c,...) pw_session_proxy_method(c,add_listener,0,__VA_ARGS__) ++#define pw_session_proxy_subscribe_params(c,...) pw_session_proxy_method(c,subscribe_params,0,__VA_ARGS__) ++#define pw_session_proxy_enum_params(c,...) pw_session_proxy_method(c,enum_params,0,__VA_ARGS__) ++#define pw_session_proxy_create_link(c,...) pw_session_proxy_method(c,create_link,0,__VA_ARGS__) ++ ++/* Endpoint */ ++ ++#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; /**< version of this structure */ ++ ++ /** ++ * Notify endpoint info ++ * ++ * \param info info about the endpoint ++ */ ++ void (*info) (void *object, const struct pw_endpoint_info *info); ++ ++ /** ++ * Notify a 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); ++}; ++ ++#define PW_ENDPOINT_PROXY_METHOD_ADD_LISTENER 0 ++#define PW_ENDPOINT_PROXY_METHOD_SUBSCRIBE_PARAMS 1 ++#define PW_ENDPOINT_PROXY_METHOD_ENUM_PARAMS 2 ++#define PW_ENDPOINT_PROXY_METHOD_SET_PARAM 3 ++#define PW_ENDPOINT_PROXY_METHOD_NUM 4 ++ ++struct pw_endpoint_proxy_methods { ++#define PW_VERSION_ENDPOINT_PROXY_METHODS 0 ++ uint32_t version; /**< version of this structure */ ++ ++ int (*add_listener) (void *object, ++ struct spa_hook *listener, ++ const struct pw_endpoint_proxy_events *events, ++ void *data); ++ ++ /** ++ * 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 returned in the reply ++ * \param id the parameter id to enumerate ++ * \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); ++}; ++ ++#define pw_endpoint_proxy_method(o,method,version,...) \ ++({ \ ++ int _res = -ENOTSUP; \ ++ struct pw_endpoint_proxy *_p = o; \ ++ spa_interface_call_res(&_p->iface, \ ++ struct pw_endpoint_proxy_methods, _res, \ ++ method, version, ##__VA_ARGS__); \ ++ _res; \ ++}) ++ ++#define pw_endpoint_proxy_add_listener(c,...) pw_endpoint_proxy_method(c,add_listener,0,__VA_ARGS__) ++#define pw_endpoint_proxy_subscribe_params(c,...) pw_endpoint_proxy_method(c,subscribe_params,0,__VA_ARGS__) ++#define pw_endpoint_proxy_enum_params(c,...) pw_endpoint_proxy_method(c,enum_params,0,__VA_ARGS__) ++ ++/* Endpoint Stream */ ++ ++#define PW_ENDPOINT_STREAM_PROXY_EVENT_INFO 0 ++#define PW_ENDPOINT_STREAM_PROXY_EVENT_PARAM 1 ++#define PW_ENDPOINT_STREAM_PROXY_EVENT_NUM 2 ++ ++struct pw_endpoint_stream_proxy_events { ++#define PW_VERSION_ENDPOINT_STREAM_PROXY_EVENTS 0 ++ uint32_t version; /**< version of this structure */ ++ ++ /** ++ * Notify endpoint stream info ++ * ++ * \param info info about the endpoint stream ++ */ ++ void (*info) (void *object, const struct pw_endpoint_stream_info *info); ++ ++ /** ++ * Notify a endpoint stream 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); ++}; ++ ++#define PW_ENDPOINT_STREAM_PROXY_METHOD_ADD_LISTENER 0 ++#define PW_ENDPOINT_STREAM_PROXY_METHOD_SUBSCRIBE_PARAMS 1 ++#define PW_ENDPOINT_STREAM_PROXY_METHOD_ENUM_PARAMS 2 ++#define PW_ENDPOINT_STREAM_PROXY_METHOD_SET_PARAM 3 ++#define PW_ENDPOINT_STREAM_PROXY_METHOD_NUM 4 ++ ++struct pw_endpoint_stream_proxy_methods { ++#define PW_VERSION_ENDPOINT_STREAM_PROXY_METHODS 0 ++ uint32_t version; /**< version of this structure */ ++ ++ int (*add_listener) (void *object, ++ struct spa_hook *listener, ++ const struct pw_endpoint_stream_proxy_events *events, ++ void *data); ++ ++ /** ++ * 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 stream parameters ++ * ++ * Start enumeration of stream parameters. For each param, a ++ * param event will be emited. ++ * ++ * \param seq a sequence number returned in the reply ++ * \param id the parameter id to enumerate ++ * \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 stream ++ * ++ * \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); ++}; ++ ++#define pw_endpoint_stream_proxy_method(o,method,version,...) \ ++({ \ ++ int _res = -ENOTSUP; \ ++ struct pw_endpoint_stream_proxy *_p = o; \ ++ spa_interface_call_res(&_p->iface, \ ++ struct pw_endpoint_stream_proxy_methods, _res, \ ++ method, version, ##__VA_ARGS__); \ ++ _res; \ ++}) ++ ++#define pw_endpoint_stream_proxy_add_listener(c,...) pw_endpoint_stream_proxy_method(c,add_listener,0,__VA_ARGS__) ++#define pw_endpoint_stream_proxy_subscribe_params(c,...) pw_endpoint_stream_proxy_method(c,subscribe_params,0,__VA_ARGS__) ++#define pw_endpoint_stream_proxy_enum_params(c,...) pw_endpoint_stream_proxy_method(c,enum_params,0,__VA_ARGS__) ++ ++/* Endpoint Link */ ++ ++#define PW_ENDPOINT_LINK_PROXY_EVENT_INFO 0 ++#define PW_ENDPOINT_LINK_PROXY_EVENT_PARAM 1 ++#define PW_ENDPOINT_LINK_PROXY_EVENT_NUM 2 ++ ++struct pw_endpoint_link_proxy_events { ++#define PW_VERSION_ENDPOINT_LINK_PROXY_EVENTS 0 ++ uint32_t version; /**< version of this structure */ ++ ++ /** ++ * Notify endpoint link info ++ * ++ * \param info info about the endpoint link ++ */ ++ void (*info) (void *object, const struct pw_endpoint_link_info *info); ++ ++ /** ++ * Notify a endpoint link 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); ++}; ++ ++#define PW_ENDPOINT_LINK_PROXY_METHOD_ADD_LISTENER 0 ++#define PW_ENDPOINT_LINK_PROXY_METHOD_SUBSCRIBE_PARAMS 1 ++#define PW_ENDPOINT_LINK_PROXY_METHOD_ENUM_PARAMS 2 ++#define PW_ENDPOINT_LINK_PROXY_METHOD_SET_PARAM 3 ++#define PW_ENDPOINT_LINK_PROXY_METHOD_REQUEST_STATE 4 ++#define PW_ENDPOINT_LINK_PROXY_METHOD_DESTROY 5 ++#define PW_ENDPOINT_LINK_PROXY_METHOD_NUM 6 ++ ++struct pw_endpoint_link_proxy_methods { ++#define PW_VERSION_ENDPOINT_LINK_PROXY_METHODS 0 ++ uint32_t version; /**< version of this structure */ ++ ++ int (*add_listener) (void *object, ++ struct spa_hook *listener, ++ const struct pw_endpoint_link_proxy_events *events, ++ void *data); ++ ++ /** ++ * 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 link parameters ++ * ++ * Start enumeration of link parameters. For each param, a ++ * param event will be emited. ++ * ++ * \param seq a sequence number returned in the reply ++ * \param id the parameter id to enumerate ++ * \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 link ++ * ++ * \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); ++ ++ int (*request_state) (void *object, enum pw_endpoint_link_state state); ++ ++ int (*destroy) (void *object); ++ ++}; ++ ++#define pw_endpoint_link_proxy_method(o,method,version,...) \ ++({ \ ++ int _res = -ENOTSUP; \ ++ struct pw_endpoint_link_proxy *_p = o; \ ++ spa_interface_call_res(&_p->iface, \ ++ struct pw_endpoint_link_proxy_methods, _res, \ ++ method, version, ##__VA_ARGS__); \ ++ _res; \ ++}) ++ ++#define pw_endpoint_link_proxy_add_listener(c,...) pw_endpoint_link_proxy_method(c,add_listener,0,__VA_ARGS__) ++#define pw_endpoint_link_proxy_subscribe_params(c,...) pw_endpoint_link_proxy_method(c,subscribe_params,0,__VA_ARGS__) ++#define pw_endpoint_link_proxy_enum_params(c,...) pw_endpoint_link_proxy_method(c,enum_params,0,__VA_ARGS__) ++#define pw_endpoint_link_proxy_request_state(c,...) pw_endpoint_link_proxy_method(c,request_state,0,__VA_ARGS__) ++#define pw_endpoint_link_proxy_destroy(c,...) pw_endpoint_link_proxy_method(c,destroy,0,__VA_ARGS__) ++ ++ ++#ifdef __cplusplus ++} /* extern "C" */ ++#endif ++ ++#endif /* PIPEWIRE_EXT_SESSION_MANAGER_INTERFACES_H */ +diff --git a/src/extensions/session-manager/introspect.h b/src/extensions/session-manager/introspect.h +new file mode 100644 +index 00000000..3b0e4113 +--- /dev/null ++++ b/src/extensions/session-manager/introspect.h +@@ -0,0 +1,131 @@ ++/* 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_SESSION_MANAGER_INTROSPECT_H ++#define PIPEWIRE_EXT_SESSION_MANAGER_INTROSPECT_H ++ ++#include <spa/utils/defs.h> ++#include <spa/utils/dict.h> ++#include <spa/param/param.h> ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++#define PW_KEY_ENDPOINT_ID "endpoint.id" ++#define PW_KEY_SESSION_ID "session.id" ++ ++enum pw_endpoint_direction { ++ PW_ENDPOINT_DIRECTION_SINK_INPUT = SPA_DIRECTION_INPUT, ++ PW_ENDPOINT_DIRECTION_SOURCE_OUTPUT = SPA_DIRECTION_OUTPUT, ++ PW_ENDPOINT_DIRECTION_SOURCE, ++ PW_ENDPOINT_DIRECTION_SINK, ++}; ++ ++enum pw_endpoint_link_state { ++ PW_ENDPOINT_LINK_STATE_ERROR = -1, ++ PW_ENDPOINT_LINK_STATE_PREPARING, ++ PW_ENDPOINT_LINK_STATE_INACTIVE, ++ PW_ENDPOINT_LINK_STATE_ACTIVE, ++}; ++ ++struct pw_session_info { ++#define PW_VERSION_SESSION_INFO 0 ++ uint32_t version; /**< version of this structure */ ++ uint32_t id; /**< the session id (global) */ ++#define PW_SESSION_CHANGE_MASK_PROPS (1 << 0) ++#define PW_SESSION_CHANGE_MASK_PARAMS (1 << 1) ++#define PW_SESSION_CHANGE_MASK_ALL ((1 << 2)-1) ++ uint32_t change_mask; /**< bitfield of changed fields since last call */ ++ struct spa_dict *props; /**< extra properties */ ++ struct spa_param_info *params; /**< parameters */ ++ uint32_t n_params; /**< number of items in \a params */ ++}; ++ ++struct pw_endpoint_info { ++#define PW_VERSION_ENDPOINT_INFO 0 ++ uint32_t version; /**< version of this structure */ ++ uint32_t id; /**< the endpoint id (global) */ ++ char *name; /**< name of the endpoint */ ++ char *media_class; /**< media class of the endpoint */ ++ enum pw_endpoint_direction direction; /**< direction of the endpoint */ ++#define PW_ENDPOINT_FLAG_PROVIDES_SESSION (1 << 0) ++ uint32_t flags; /**< additional flags */ ++#define PW_ENDPOINT_CHANGE_MASK_STREAMS (1 << 0) ++#define PW_ENDPOINT_CHANGE_MASK_SESSION (1 << 1) ++#define PW_ENDPOINT_CHANGE_MASK_PROPS (1 << 2) ++#define PW_ENDPOINT_CHANGE_MASK_PARAMS (1 << 3) ++#define PW_ENDPOINT_CHANGE_MASK_ALL ((1 << 4)-1) ++ uint32_t change_mask; /**< bitfield of changed fields since last call */ ++ uint32_t n_streams; /**< number of streams available */ ++ uint32_t session_id; /**< the id of the controlling session */ ++ struct spa_dict *props; /**< extra properties */ ++ struct spa_param_info *params; /**< parameters */ ++ uint32_t n_params; /**< number of items in \a params */ ++}; ++ ++struct pw_endpoint_stream_info { ++#define PW_VERSION_ENDPOINT_STREAM_INFO 0 ++ uint32_t version; /**< version of this structure */ ++ uint32_t id; /**< the stream id (local or global) */ ++ uint32_t endpoint_id; /**< the endpoint id (global) */ ++ char *name; /**< name of the stream */ ++#define PW_ENDPOINT_STREAM_CHANGE_MASK_LINK_PARAMS (1 << 0) ++#define PW_ENDPOINT_STREAM_CHANGE_MASK_PROPS (1 << 1) ++#define PW_ENDPOINT_STREAM_CHANGE_MASK_PARAMS (1 << 2) ++#define PW_ENDPOINT_STREAM_CHANGE_MASK_ALL ((1 << 3)-1) ++ uint32_t change_mask; /**< bitfield of changed fields since last call */ ++ struct spa_pod *link_params; /**< information for linking this stream */ ++ struct spa_dict *props; /**< extra properties */ ++ struct spa_param_info *params; /**< parameters */ ++ uint32_t n_params; /**< number of items in \a params */ ++}; ++ ++struct pw_endpoint_link_info { ++#define PW_VERSION_ENDPOINT_LINK_INFO 0 ++ uint32_t version; /**< version of this structure */ ++ uint32_t id; /**< the link id (global) */ ++ uint32_t session_id; /**< the session id (global) */ ++ uint32_t output_endpoint_id; /**< the output endpoint id (global) */ ++ uint32_t output_stream_id; /**< the output stream id (local or global) */ ++ uint32_t input_endpoint_id; /**< the input endpoint id (global) */ ++ uint32_t input_stream_id; /**< the input stream id (local or global) */ ++#define PW_ENDPOINT_LINK_CHANGE_MASK_STATE (1 << 0) ++#define PW_ENDPOINT_LINK_CHANGE_MASK_PROPS (1 << 1) ++#define PW_ENDPOINT_LINK_CHANGE_MASK_PARAMS (1 << 2) ++#define PW_ENDPOINT_LINK_CHANGE_MASK_ALL ((1 << 3)-1) ++ uint32_t change_mask; /**< bitfield of changed fields since last call */ ++ enum pw_endpoint_link_state state; /**< the state of the link */ ++ char *error; /**< error string if state == ERROR */ ++ struct spa_dict *props; /**< extra properties */ ++ struct spa_param_info *params; /**< parameters */ ++ uint32_t n_params; /**< number of items in \a params */ ++}; ++ ++#ifdef __cplusplus ++} /* extern "C" */ ++#endif ++ ++#endif /* PIPEWIRE_EXT_SESSION_MANAGER_INTROSPECT_H */ +diff --git a/src/extensions/session-manager/keys.h b/src/extensions/session-manager/keys.h +new file mode 100644 +index 00000000..a7167510 +--- /dev/null ++++ b/src/extensions/session-manager/keys.h +@@ -0,0 +1,40 @@ ++/* 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_SESSION_MANAGER_KEYS_H ++#define PIPEWIRE_EXT_SESSION_MANAGER_KEYS_H ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++#define PW_KEY_ENDPOINT_ID "endpoint.id" ++#define PW_KEY_SESSION_ID "session.id" ++ ++#ifdef __cplusplus ++} ++#endif ++ ++#endif /* PIPEWIRE_EXT_SESSION_MANAGER_KEYS_H */ +diff --git a/src/modules/meson.build b/src/modules/meson.build +index 9e1e94bd..da2684b6 100644 +--- a/src/modules/meson.build ++++ b/src/modules/meson.build +@@ -100,3 +100,20 @@ pipewire_module_adapter = shared_library('pipewire-module-adapter', + install_dir : modules_install_dir, + dependencies : [mathlib, dl_lib, rt_lib, pipewire_dep], + ) ++ ++pipewire_module_session_manager = shared_library('pipewire-module-session-manager', ++ [ 'module-session-manager.c', ++ 'module-session-manager/client-endpoint.c', ++ 'module-session-manager/client-session.c', ++ 'module-session-manager/endpoint-link.c', ++ 'module-session-manager/endpoint-stream.c', ++ 'module-session-manager/endpoint.c', ++ 'module-session-manager/session.c', ++ 'module-session-manager/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], ++) +diff --git a/src/modules/module-session-manager.c b/src/modules/module-session-manager.c +new file mode 100644 +index 00000000..dbea3357 +--- /dev/null ++++ b/src/modules/module-session-manager.c +@@ -0,0 +1,56 @@ ++/* 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 <pipewire/pipewire.h> ++ ++/* client-endpoint.c */ ++int client_endpoint_factory_init(struct pw_module *module); ++/* client-session.c */ ++int client_session_factory_init(struct pw_module *module); ++/* protocol-native.c */ ++struct pw_protocol *pw_protocol_native_ext_session_manager_init(struct pw_core *core); ++ ++static const struct spa_dict_item module_props[] = { ++ { PW_KEY_MODULE_AUTHOR, "George Kiagiadakis <george.kiagiadakis@collabora.com>" }, ++ { PW_KEY_MODULE_DESCRIPTION, "Implements objects for session management" }, ++ { PW_KEY_MODULE_VERSION, PACKAGE_VERSION }, ++}; ++ ++SPA_EXPORT ++int pipewire__module_init(struct pw_module *module, const char *args) ++{ ++ struct pw_core *core = pw_module_get_core(module); ++ ++ client_endpoint_factory_init(module); ++ client_session_factory_init(module); ++ ++ pw_protocol_native_ext_session_manager_init(core); ++ ++ pw_module_update_properties(module, &SPA_DICT_INIT_ARRAY(module_props)); ++ ++ return 0; ++} +diff --git a/src/modules/module-session-manager/client-endpoint.c b/src/modules/module-session-manager/client-endpoint.c +new file mode 100644 +index 00000000..0e501c9f +--- /dev/null ++++ b/src/modules/module-session-manager/client-endpoint.c +@@ -0,0 +1,270 @@ ++/* 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 <stdbool.h> ++#include <string.h> ++ ++#include <pipewire/pipewire.h> ++#include <extensions/session-manager.h> ++ ++#include "client-endpoint.h" ++#include "endpoint.h" ++#include "endpoint-stream.h" ++ ++#include <pipewire/private.h> ++ ++#define NAME "client-endpoint" ++ ++struct factory_data { ++ struct pw_factory *factory; ++ struct pw_module *module; ++ struct spa_hook module_listener; ++}; ++ ++static struct endpoint_stream *find_stream(struct client_endpoint *this, uint32_t id) ++{ ++ struct endpoint_stream *s; ++ spa_list_for_each(s, &this->streams, link) { ++ if (s->id == id) ++ return s; ++ } ++ return NULL; ++} ++ ++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 client_endpoint *this = object; ++ struct endpoint *endpoint = &this->endpoint; ++ ++ return endpoint_update(endpoint, change_mask, n_params, params, info); ++} ++ ++static int client_endpoint_stream_update(void *object, ++ uint32_t stream_id, ++ uint32_t change_mask, ++ uint32_t n_params, ++ const struct spa_pod **params, ++ const struct pw_endpoint_stream_info *info) ++{ ++ struct client_endpoint *this = object; ++ struct endpoint *endpoint = &this->endpoint; ++ struct endpoint_stream *stream = find_stream(this, stream_id); ++ struct pw_properties *props = NULL; ++ ++ if (!stream) { ++ struct pw_core *core = pw_global_get_core(endpoint->global); ++ const char *keys[] = { ++ PW_KEY_FACTORY_ID, ++ PW_KEY_CLIENT_ID, ++ PW_KEY_ENDPOINT_ID, ++ NULL ++ }; ++ ++ stream = calloc(1, sizeof(struct endpoint_stream)); ++ if (!stream) ++ goto no_mem; ++ ++ props = pw_properties_new(NULL, NULL); ++ if (!props) ++ goto no_mem; ++ pw_properties_copy_keys (endpoint->props, props, keys); ++ ++ if (endpoint_stream_init(stream, stream_id, endpoint->info.id, ++ this, core, props) < 0) ++ goto no_mem; ++ ++ spa_list_append(&this->streams, &stream->link); ++ } ++ else if (change_mask & PW_CLIENT_ENDPOINT_STREAM_UPDATE_DESTROYED) { ++ endpoint_stream_clear(stream); ++ spa_list_remove(&stream->link); ++ free(stream); ++ stream = NULL; ++ } ++ ++ return stream ? ++ endpoint_stream_update(stream, change_mask, n_params, params, info) ++ : 0; ++ ++ no_mem: ++ if (props) ++ pw_properties_free(props); ++ free(stream); ++ pw_log_error(NAME" %p: cannot update stream: no memory", this); ++ pw_resource_error(this->resource, -ENOMEM, ++ NAME" %p: cannot update stream: no memory", this); ++ return -ENOMEM; ++} ++ ++static struct pw_client_endpoint_proxy_methods methods = { ++ PW_VERSION_CLIENT_ENDPOINT_PROXY_METHODS, ++ .update = client_endpoint_update, ++ .stream_update = client_endpoint_stream_update, ++}; ++ ++static void client_endpoint_destroy(void *data) ++{ ++ struct client_endpoint *this = data; ++ struct endpoint_stream *s; ++ ++ pw_log_debug(NAME" %p: destroy", this); ++ ++ spa_list_consume(s, &this->streams, link) { ++ endpoint_stream_clear(s); ++ spa_list_remove(&s->link); ++ free(s); ++ } ++ endpoint_clear(&this->endpoint); ++ spa_hook_remove(&this->resource_listener); ++ ++ free(this); ++} ++ ++static const struct pw_resource_events resource_events = { ++ PW_VERSION_RESOURCE_EVENTS, ++ .destroy = client_endpoint_destroy, ++}; ++ ++static void *create_object(void *data, ++ struct pw_resource *owner_resource, ++ uint32_t type, ++ uint32_t version, ++ struct pw_properties *properties, ++ uint32_t new_id) ++{ ++ struct factory_data *d = data; ++ struct pw_factory *factory = d->factory; ++ struct 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 client_endpoint)); ++ if (this == NULL) ++ goto no_mem; ++ ++ spa_list_init(&this->streams); ++ ++ pw_log_debug(NAME" %p: new", this); ++ ++ if (!properties) ++ properties = pw_properties_new(NULL, NULL); ++ if (!properties) ++ goto no_mem; ++ ++ pw_properties_setf(properties, PW_KEY_CLIENT_ID, "%d", owner->global->id); ++ pw_properties_setf(properties, PW_KEY_FACTORY_ID, "%d", factory->global->id); ++ ++ this->resource = pw_resource_new(owner, new_id, PW_PERM_RWX, type, version, 0); ++ if (this->resource == NULL) ++ goto no_mem; ++ ++ if (endpoint_init(&this->endpoint, this, core, properties) < 0) ++ goto no_mem; ++ ++ pw_resource_add_listener(this->resource, &this->resource_listener, ++ &resource_events, this); ++ pw_resource_add_object_listener(this->resource, &this->object_listener, ++ &methods, this); ++ ++ return this; ++ ++ no_mem: ++ if (properties) ++ pw_properties_free(properties); ++ if (this && this->resource) ++ pw_resource_destroy(this->resource); ++ free(this); ++ pw_log_error("can't create client endpoint: no memory"); ++ pw_resource_error(owner_resource, -ENOMEM, ++ "can't create client endpoint: no memory"); ++ 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); ++ pw_factory_destroy(d->factory); ++} ++ ++static void module_registered(void *data) ++{ ++ struct factory_data *d = data; ++ struct pw_module *module = d->module; ++ struct pw_factory *factory = d->factory; ++ struct spa_dict_item items[1]; ++ char id[16]; ++ int res; ++ ++ snprintf(id, sizeof(id), "%d", module->global->id); ++ items[0] = SPA_DICT_ITEM_INIT(PW_KEY_MODULE_ID, id); ++ pw_factory_update_properties(factory, &SPA_DICT_INIT(items, 1)); ++ ++ if ((res = pw_factory_register(factory, NULL)) < 0) { ++ pw_log_error(NAME" %p: can't register factory: %s", factory, spa_strerror(res)); ++ } ++} ++ ++static const struct pw_module_events module_events = { ++ PW_VERSION_MODULE_EVENTS, ++ .destroy = module_destroy, ++ .registered = module_registered, ++}; ++ ++int client_endpoint_factory_init(struct pw_module *module) ++{ ++ 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_PROXY, ++ NULL, ++ sizeof(*data)); ++ if (factory == NULL) ++ return -ENOMEM; ++ ++ data = pw_factory_get_user_data(factory); ++ data->factory = factory; ++ data->module = module; ++ ++ pw_factory_set_implementation(factory, &impl_factory, data); ++ ++ pw_module_add_listener(module, &data->module_listener, &module_events, data); ++ ++ return 0; ++} +diff --git a/src/modules/module-session-manager/client-endpoint.h b/src/modules/module-session-manager/client-endpoint.h +new file mode 100644 +index 00000000..394e9fa8 +--- /dev/null ++++ b/src/modules/module-session-manager/client-endpoint.h +@@ -0,0 +1,60 @@ ++/* 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 MODULE_SESSION_MANAGER_CLIENT_ENDPOINT_H ++#define MODULE_SESSION_MANAGER_CLIENT_ENDPOINT_H ++ ++#include "endpoint.h" ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++struct client_endpoint { ++ struct pw_resource *resource; ++ struct spa_hook resource_listener; ++ struct spa_hook object_listener; ++ struct endpoint endpoint; ++ struct spa_list streams; ++}; ++ ++#define pw_client_endpoint_resource(r,m,v,...) \ ++ pw_resource_call_res(r,struct pw_client_endpoint_proxy_events,m,v,__VA_ARGS__) ++#define pw_client_endpoint_resource_set_id(r,...) \ ++ pw_client_endpoint_resource(r,set_id,0,__VA_ARGS__) ++#define pw_client_endpoint_resource_set_session_id(r,...) \ ++ pw_client_endpoint_resource(r,set_session_id,0,__VA_ARGS__) ++#define pw_client_endpoint_resource_set_param(r,...) \ ++ pw_client_endpoint_resource(r,set_param,0,__VA_ARGS__) ++#define pw_client_endpoint_resource_stream_set_param(r,...) \ ++ pw_client_endpoint_resource(r,stream_set_param,0,__VA_ARGS__) ++ ++int client_endpoint_factory_init(struct pw_module *module); ++ ++#ifdef __cplusplus ++} /* extern "C" */ ++#endif ++ ++#endif /* MODULE_SESSION_MANAGER_CLIENT_ENDPOINT_H */ +diff --git a/src/modules/module-session-manager/client-session.c b/src/modules/module-session-manager/client-session.c +new file mode 100644 +index 00000000..9b20d833 +--- /dev/null ++++ b/src/modules/module-session-manager/client-session.c +@@ -0,0 +1,270 @@ ++/* 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 <stdbool.h> ++#include <string.h> ++ ++#include <pipewire/pipewire.h> ++#include <extensions/session-manager.h> ++ ++#include "client-session.h" ++#include "session.h" ++#include "endpoint-link.h" ++ ++#include <pipewire/private.h> ++ ++#define NAME "client-session" ++ ++struct factory_data { ++ struct pw_factory *factory; ++ struct pw_module *module; ++ struct spa_hook module_listener; ++}; ++ ++static struct endpoint_link *find_link(struct client_session *this, uint32_t id) ++{ ++ struct endpoint_link *l; ++ spa_list_for_each(l, &this->links, link) { ++ if (l->id == id) ++ return l; ++ } ++ return NULL; ++} ++ ++static int client_session_update(void *object, ++ uint32_t change_mask, ++ uint32_t n_params, ++ const struct spa_pod **params, ++ const struct pw_session_info *info) ++{ ++ struct client_session *this = object; ++ struct session *session = &this->session; ++ ++ return session_update(session, change_mask, n_params, params, info); ++} ++ ++static int client_session_link_update(void *object, ++ uint32_t link_id, ++ uint32_t change_mask, ++ uint32_t n_params, ++ const struct spa_pod **params, ++ const struct pw_endpoint_link_info *info) ++{ ++ struct client_session *this = object; ++ struct session *session = &this->session; ++ struct endpoint_link *link = find_link(this, link_id); ++ struct pw_properties *props = NULL; ++ ++ if (!link) { ++ struct pw_core *core = pw_global_get_core(session->global); ++ const char *keys[] = { ++ PW_KEY_FACTORY_ID, ++ PW_KEY_CLIENT_ID, ++ PW_KEY_SESSION_ID, ++ NULL ++ }; ++ ++ link = calloc(1, sizeof(struct endpoint_link)); ++ if (!link) ++ goto no_mem; ++ ++ props = pw_properties_new(NULL, NULL); ++ if (!props) ++ goto no_mem; ++ pw_properties_copy_keys (session->props, props, keys); ++ ++ if (endpoint_link_init(link, link_id, session->info.id, ++ this, core, props) < 0) ++ goto no_mem; ++ ++ spa_list_append(&this->links, &link->link); ++ } ++ else if (change_mask & PW_CLIENT_SESSION_LINK_UPDATE_DESTROYED) { ++ endpoint_link_clear(link); ++ spa_list_remove(&link->link); ++ free(link); ++ link = NULL; ++ } ++ ++ return link ? ++ endpoint_link_update(link, change_mask, n_params, params, info) ++ : 0; ++ ++ no_mem: ++ if (props) ++ pw_properties_free(props); ++ free(link); ++ pw_log_error(NAME" %p: cannot update link: no memory", this); ++ pw_resource_error(this->resource, -ENOMEM, ++ NAME" %p: cannot update link: no memory", this); ++ return -ENOMEM; ++} ++ ++static struct pw_client_session_proxy_methods methods = { ++ PW_VERSION_CLIENT_SESSION_PROXY_METHODS, ++ .update = client_session_update, ++ .link_update = client_session_link_update, ++}; ++ ++static void client_session_destroy(void *data) ++{ ++ struct client_session *this = data; ++ struct endpoint_link *l; ++ ++ pw_log_debug(NAME" %p: destroy", this); ++ ++ spa_list_consume(l, &this->links, link) { ++ endpoint_link_clear(l); ++ spa_list_remove(&l->link); ++ free(l); ++ } ++ session_clear(&this->session); ++ spa_hook_remove(&this->resource_listener); ++ ++ free(this); ++} ++ ++static const struct pw_resource_events resource_events = { ++ PW_VERSION_RESOURCE_EVENTS, ++ .destroy = client_session_destroy, ++}; ++ ++static void *create_object(void *data, ++ struct pw_resource *owner_resource, ++ uint32_t type, ++ uint32_t version, ++ struct pw_properties *properties, ++ uint32_t new_id) ++{ ++ struct factory_data *d = data; ++ struct pw_factory *factory = d->factory; ++ struct client_session *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 client_session)); ++ if (this == NULL) ++ goto no_mem; ++ ++ spa_list_init(&this->links); ++ ++ pw_log_debug(NAME" %p: new", this); ++ ++ if (!properties) ++ properties = pw_properties_new(NULL, NULL); ++ if (!properties) ++ goto no_mem; ++ ++ pw_properties_setf(properties, PW_KEY_CLIENT_ID, "%d", owner->global->id); ++ pw_properties_setf(properties, PW_KEY_FACTORY_ID, "%d", factory->global->id); ++ ++ this->resource = pw_resource_new(owner, new_id, PW_PERM_RWX, type, version, 0); ++ if (this->resource == NULL) ++ goto no_mem; ++ ++ if (session_init(&this->session, this, core, properties) < 0) ++ goto no_mem; ++ ++ pw_resource_add_listener(this->resource, &this->resource_listener, ++ &resource_events, this); ++ pw_resource_add_object_listener(this->resource, &this->object_listener, ++ &methods, this); ++ ++ return this; ++ ++ no_mem: ++ if (properties) ++ pw_properties_free(properties); ++ if (this && this->resource) ++ pw_resource_destroy(this->resource); ++ free(this); ++ pw_log_error("can't create client session: no memory"); ++ pw_resource_error(owner_resource, -ENOMEM, ++ "can't create client session: no memory"); ++ 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); ++ pw_factory_destroy(d->factory); ++} ++ ++static void module_registered(void *data) ++{ ++ struct factory_data *d = data; ++ struct pw_module *module = d->module; ++ struct pw_factory *factory = d->factory; ++ struct spa_dict_item items[1]; ++ char id[16]; ++ int res; ++ ++ snprintf(id, sizeof(id), "%d", module->global->id); ++ items[0] = SPA_DICT_ITEM_INIT(PW_KEY_MODULE_ID, id); ++ pw_factory_update_properties(factory, &SPA_DICT_INIT(items, 1)); ++ ++ if ((res = pw_factory_register(factory, NULL)) < 0) { ++ pw_log_error(NAME" %p: can't register factory: %s", factory, spa_strerror(res)); ++ } ++} ++ ++static const struct pw_module_events module_events = { ++ PW_VERSION_MODULE_EVENTS, ++ .destroy = module_destroy, ++ .registered = module_registered, ++}; ++ ++int client_session_factory_init(struct pw_module *module) ++{ ++ struct pw_core *core = pw_module_get_core(module); ++ struct pw_factory *factory; ++ struct factory_data *data; ++ ++ factory = pw_factory_new(core, ++ "client-session", ++ PW_TYPE_INTERFACE_ClientSession, ++ PW_VERSION_CLIENT_SESSION_PROXY, ++ NULL, ++ sizeof(*data)); ++ if (factory == NULL) ++ return -ENOMEM; ++ ++ data = pw_factory_get_user_data(factory); ++ data->factory = factory; ++ data->module = module; ++ ++ pw_factory_set_implementation(factory, &impl_factory, data); ++ ++ pw_module_add_listener(module, &data->module_listener, &module_events, data); ++ ++ return 0; ++} +diff --git a/src/modules/module-session-manager/client-session.h b/src/modules/module-session-manager/client-session.h +new file mode 100644 +index 00000000..c764564d +--- /dev/null ++++ b/src/modules/module-session-manager/client-session.h +@@ -0,0 +1,62 @@ ++/* 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 MODULE_SESSION_MANAGER_CLIENT_SESSION_H ++#define MODULE_SESSION_MANAGER_CLIENT_SESSION_H ++ ++#include "session.h" ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++struct client_session { ++ struct pw_resource *resource; ++ struct spa_hook resource_listener; ++ struct spa_hook object_listener; ++ struct session session; ++ struct spa_list links; ++}; ++ ++#define pw_client_session_resource(r,m,v,...) \ ++ pw_resource_call_res(r,struct pw_client_session_proxy_events,m,v,__VA_ARGS__) ++#define pw_client_session_resource_set_id(r,...) \ ++ pw_client_session_resource(r,set_id,0,__VA_ARGS__) ++#define pw_client_session_resource_set_param(r,...) \ ++ pw_client_session_resource(r,set_param,0,__VA_ARGS__) ++#define pw_client_session_resource_link_set_param(r,...) \ ++ pw_client_session_resource(r,link_set_param,0,__VA_ARGS__) ++#define pw_client_session_resource_create_link(r,...) \ ++ pw_client_session_resource(r,create_link,0,__VA_ARGS__) ++#define pw_client_session_resource_destroy_link(r,...) \ ++ pw_client_session_resource(r,destroy_link,0,__VA_ARGS__) ++#define pw_client_session_resource_link_request_state(r,...) \ ++ pw_client_session_resource(r,link_request_state,0,__VA_ARGS__) ++ ++#ifdef __cplusplus ++} /* extern "C" */ ++#endif ++ ++#endif /* MODULE_SESSION_MANAGER_CLIENT_SESSION_H */ +diff --git a/src/modules/module-session-manager/endpoint-link.c b/src/modules/module-session-manager/endpoint-link.c +new file mode 100644 +index 00000000..bce06598 +--- /dev/null ++++ b/src/modules/module-session-manager/endpoint-link.c +@@ -0,0 +1,359 @@ ++/* 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 <stdbool.h> ++#include <string.h> ++ ++#include <pipewire/pipewire.h> ++#include <extensions/session-manager.h> ++ ++#include <spa/pod/filter.h> ++ ++#include "endpoint-link.h" ++#include "client-session.h" ++ ++#include <pipewire/private.h> ++ ++#define NAME "endpoint-link" ++ ++struct resource_data { ++ struct endpoint_link *link; ++ struct spa_hook resource_listener; ++ struct spa_hook object_listener; ++ uint32_t n_subscribe_ids; ++ uint32_t subscribe_ids[32]; ++}; ++ ++#define pw_endpoint_link_resource(r,m,v,...) \ ++ pw_resource_call(r,struct pw_endpoint_link_proxy_events,m,v,__VA_ARGS__) ++#define pw_endpoint_link_resource_info(r,...) \ ++ pw_endpoint_link_resource(r,info,0,__VA_ARGS__) ++#define pw_endpoint_link_resource_param(r,...) \ ++ pw_endpoint_link_resource(r,param,0,__VA_ARGS__) ++ ++static int endpoint_link_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 endpoint_link *this = data->link; ++ 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(NAME" %p: %d param %u", this, seq, index); ++ ++ pw_endpoint_link_resource_param(resource, seq, id, index, next, result); ++ ++ if (++count == num) ++ break; ++ } ++ return 0; ++} ++ ++static int endpoint_link_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(NAME" %p: resource %d subscribe param %u", ++ data->link, resource->id, ids[i]); ++ endpoint_link_enum_params(resource, 1, ids[i], 0, UINT32_MAX, NULL); ++ } ++ return 0; ++} ++ ++static int endpoint_link_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 endpoint_link *this = data->link; ++ ++ pw_client_session_resource_set_param(this->client_sess->resource, ++ id, flags, param); ++ ++ return 0; ++} ++ ++static int endpoint_link_request_state(void *object, enum pw_endpoint_link_state state) ++{ ++ struct pw_resource *resource = object; ++ struct resource_data *data = pw_resource_get_user_data(resource); ++ struct endpoint_link *this = data->link; ++ ++ pw_client_session_resource_link_request_state(this->client_sess->resource, ++ this->id, state); ++ ++ return 0; ++} ++ ++static int endpoint_link_destroy(void *object) ++{ ++ struct pw_resource *resource = object; ++ struct resource_data *data = pw_resource_get_user_data(resource); ++ struct endpoint_link *this = data->link; ++ ++ pw_client_session_resource_destroy_link(this->client_sess->resource, ++ this->id); ++ ++ return 0; ++} ++ ++static const struct pw_endpoint_link_proxy_methods methods = { ++ PW_VERSION_ENDPOINT_LINK_PROXY_METHODS, ++ .subscribe_params = endpoint_link_subscribe_params, ++ .enum_params = endpoint_link_enum_params, ++ .set_param = endpoint_link_set_param, ++ .request_state = endpoint_link_request_state, ++ .destroy = endpoint_link_destroy, ++}; ++ ++static void endpoint_link_notify_subscribed(struct endpoint_link *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_link_resource_param(resource, 1, ++ id, index, next, param); ++ } ++ } ++ } ++} ++ ++int endpoint_link_update(struct endpoint_link *this, ++ uint32_t change_mask, ++ uint32_t n_params, ++ const struct spa_pod **params, ++ const struct pw_endpoint_link_info *info) ++{ ++ if (change_mask & PW_CLIENT_SESSION_UPDATE_PARAMS) { ++ uint32_t i; ++ size_t size = n_params * sizeof(struct spa_pod *); ++ ++ pw_log_debug(NAME" %p: update %d params", this, n_params); ++ ++ for (i = 0; i < this->n_params; i++) ++ free(this->params[i]); ++ this->params = realloc(this->params, size); ++ if (size > 0 && !this->params) { ++ this->n_params = 0; ++ goto no_mem; ++ } ++ this->n_params = n_params; ++ ++ for (i = 0; i < this->n_params; i++) { ++ this->params[i] = params[i] ? spa_pod_copy(params[i]) : NULL; ++ endpoint_link_notify_subscribed(this, i, i+1); ++ } ++ } ++ ++ if (change_mask & PW_CLIENT_SESSION_UPDATE_INFO) { ++ struct pw_resource *resource; ++ ++ if (info->change_mask & PW_ENDPOINT_LINK_CHANGE_MASK_STATE) { ++ this->info.state = info->state; ++ free(this->info.error); ++ this->info.error = info->error ? strdup(info->error) : NULL; ++ } ++ ++ if (info->change_mask & PW_ENDPOINT_LINK_CHANGE_MASK_PROPS) ++ pw_properties_update(this->props, info->props); ++ ++ if (info->change_mask & PW_ENDPOINT_LINK_CHANGE_MASK_PARAMS) { ++ size_t size = info->n_params * sizeof(struct spa_param_info); ++ ++ this->info.params = realloc(this->info.params, size); ++ if (size > 0 && !this->info.params) { ++ this->info.n_params = 0; ++ goto no_mem; ++ } ++ this->info.n_params = info->n_params; ++ ++ memcpy(this->info.params, info->params, size); ++ } ++ ++ if (!this->info.output_endpoint_id) { ++ this->info.output_endpoint_id = info->output_endpoint_id; ++ this->info.output_stream_id = info->output_stream_id; ++ this->info.input_endpoint_id = info->input_endpoint_id; ++ this->info.input_stream_id = info->input_stream_id; ++ } ++ ++ this->info.change_mask = info->change_mask; ++ spa_list_for_each(resource, &this->global->resource_list, link) { ++ pw_endpoint_link_resource_info(resource, &this->info); ++ } ++ this->info.change_mask = 0; ++ } ++ ++ return 0; ++ ++ no_mem: ++ pw_log_error(NAME" %p: can't update: no memory", this); ++ pw_resource_error(this->client_sess->resource, -ENOMEM, ++ NAME" %p: can't update: no memory", this); ++ return -ENOMEM; ++} ++ ++static void endpoint_link_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_link_unbind, ++}; ++ ++static int endpoint_link_bind(void *_data, struct pw_client *client, ++ uint32_t permissions, uint32_t version, uint32_t id) ++{ ++ struct endpoint_link *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->link = this; ++ pw_resource_add_listener(resource, &data->resource_listener, ++ &resource_events, resource); ++ pw_resource_add_object_listener(resource, &data->object_listener, ++ &methods, resource); ++ ++ pw_log_debug(NAME" %p: bound to %d", this, resource->id); ++ ++ spa_list_append(&global->resource_list, &resource->link); ++ ++ this->info.change_mask = PW_ENDPOINT_LINK_CHANGE_MASK_ALL; ++ pw_endpoint_link_resource_info(resource, &this->info); ++ this->info.change_mask = 0; ++ ++ return 0; ++ ++ no_mem: ++ pw_log_error(NAME" %p: can't create resource: no memory", this); ++ pw_resource_error(this->client_sess->resource, -ENOMEM, ++ NAME" %p: can't create resource: no memory", this); ++ return -ENOMEM; ++} ++ ++int endpoint_link_init(struct endpoint_link *this, ++ uint32_t id, uint32_t session_id, ++ struct client_session *client_sess, ++ struct pw_core *core, ++ struct pw_properties *properties) ++{ ++ pw_log_debug(NAME" %p: new", this); ++ ++ this->client_sess = client_sess; ++ this->id = id; ++ this->props = properties; ++ ++ properties = pw_properties_copy(properties); ++ if (!properties) ++ goto no_mem; ++ ++ this->global = pw_global_new (core, ++ PW_TYPE_INTERFACE_EndpointLink, ++ PW_VERSION_ENDPOINT_LINK_PROXY, ++ properties, endpoint_link_bind, this); ++ if (!this->global) ++ goto no_mem; ++ ++ this->info.version = PW_VERSION_ENDPOINT_LINK_INFO; ++ this->info.id = this->global->id; ++ this->info.session_id = session_id; ++ this->info.props = &this->props->dict; ++ ++ return pw_global_register(this->global); ++ ++ no_mem: ++ pw_log_error(NAME" - can't create - out of memory"); ++ return -ENOMEM; ++} ++ ++void endpoint_link_clear(struct endpoint_link *this) ++{ ++ uint32_t i; ++ ++ pw_log_debug(NAME" %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.error); ++ free(this->info.params); ++ ++ if (this->props) ++ pw_properties_free(this->props); ++} +diff --git a/src/modules/module-session-manager/endpoint-link.h b/src/modules/module-session-manager/endpoint-link.h +new file mode 100644 +index 00000000..a9c18d32 +--- /dev/null ++++ b/src/modules/module-session-manager/endpoint-link.h +@@ -0,0 +1,64 @@ ++/* 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 MODULE_SESSION_MANAGER_ENDPOINT_LINK_H ++#define MODULE_SESSION_MANAGER_ENDPOINT_LINK_H ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++struct client_session; ++ ++struct endpoint_link { ++ struct spa_list link; ++ struct client_session *client_sess; ++ struct pw_global *global; ++ uint32_t id; /* session-local link id */ ++ uint32_t n_params; ++ struct spa_pod **params; ++ struct pw_endpoint_link_info info; ++ struct pw_properties *props; /* wrapper of info.props */ ++}; ++ ++int endpoint_link_init(struct endpoint_link *this, ++ uint32_t id, uint32_t session_id, ++ struct client_session *client_sess, ++ struct pw_core *core, ++ struct pw_properties *properties); ++ ++void endpoint_link_clear(struct endpoint_link *this); ++ ++int endpoint_link_update(struct endpoint_link *this, ++ uint32_t change_mask, ++ uint32_t n_params, ++ const struct spa_pod **params, ++ const struct pw_endpoint_link_info *info); ++ ++#ifdef __cplusplus ++} /* extern "C" */ ++#endif ++ ++#endif /* MODULE_SESSION_MANAGER_ENDPOINT_LINK_H */ +diff --git a/src/modules/module-session-manager/endpoint-stream.c b/src/modules/module-session-manager/endpoint-stream.c +new file mode 100644 +index 00000000..47d2a4ea +--- /dev/null ++++ b/src/modules/module-session-manager/endpoint-stream.c +@@ -0,0 +1,329 @@ ++/* 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 <stdbool.h> ++#include <string.h> ++ ++#include <pipewire/pipewire.h> ++#include <extensions/session-manager.h> ++ ++#include <spa/pod/filter.h> ++ ++#include "endpoint-stream.h" ++#include "client-endpoint.h" ++ ++#include <pipewire/private.h> ++ ++#define NAME "endpoint-stream" ++ ++struct resource_data { ++ struct endpoint_stream *stream; ++ struct spa_hook resource_listener; ++ struct spa_hook object_listener; ++ uint32_t n_subscribe_ids; ++ uint32_t subscribe_ids[32]; ++}; ++ ++#define pw_endpoint_stream_resource(r,m,v,...) \ ++ pw_resource_call(r,struct pw_endpoint_stream_proxy_events,m,v,__VA_ARGS__) ++#define pw_endpoint_stream_resource_info(r,...) \ ++ pw_endpoint_stream_resource(r,info,0,__VA_ARGS__) ++#define pw_endpoint_stream_resource_param(r,...) \ ++ pw_endpoint_stream_resource(r,param,0,__VA_ARGS__) ++ ++static int endpoint_stream_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 endpoint_stream *this = data->stream; ++ 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(NAME" %p: %d param %u", this, seq, index); ++ ++ pw_endpoint_stream_resource_param(resource, seq, id, index, next, result); ++ ++ if (++count == num) ++ break; ++ } ++ return 0; ++} ++ ++static int endpoint_stream_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(NAME" %p: resource %d subscribe param %u", ++ data->stream, resource->id, ids[i]); ++ endpoint_stream_enum_params(resource, 1, ids[i], 0, UINT32_MAX, NULL); ++ } ++ return 0; ++} ++ ++static int endpoint_stream_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 endpoint_stream *this = data->stream; ++ ++ pw_client_endpoint_resource_set_param(this->client_ep->resource, ++ id, flags, param); ++ ++ return 0; ++} ++ ++static const struct pw_endpoint_stream_proxy_methods methods = { ++ PW_VERSION_ENDPOINT_STREAM_PROXY_METHODS, ++ .subscribe_params = endpoint_stream_subscribe_params, ++ .enum_params = endpoint_stream_enum_params, ++ .set_param = endpoint_stream_set_param, ++}; ++ ++static void endpoint_stream_notify_subscribed(struct endpoint_stream *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_stream_resource_param(resource, 1, ++ id, index, next, param); ++ } ++ } ++ } ++} ++ ++int endpoint_stream_update(struct endpoint_stream *this, ++ uint32_t change_mask, ++ uint32_t n_params, ++ const struct spa_pod **params, ++ const struct pw_endpoint_stream_info *info) ++{ ++ if (change_mask & PW_CLIENT_ENDPOINT_UPDATE_PARAMS) { ++ uint32_t i; ++ size_t size = n_params * sizeof(struct spa_pod *); ++ ++ pw_log_debug(NAME" %p: update %d params", this, n_params); ++ ++ for (i = 0; i < this->n_params; i++) ++ free(this->params[i]); ++ this->params = realloc(this->params, size); ++ if (size > 0 && !this->params) { ++ this->n_params = 0; ++ goto no_mem; ++ } ++ this->n_params = n_params; ++ ++ for (i = 0; i < this->n_params; i++) { ++ this->params[i] = params[i] ? spa_pod_copy(params[i]) : NULL; ++ endpoint_stream_notify_subscribed(this, i, i+1); ++ } ++ } ++ ++ if (change_mask & PW_CLIENT_ENDPOINT_UPDATE_INFO) { ++ struct pw_resource *resource; ++ ++ if (info->change_mask & PW_ENDPOINT_STREAM_CHANGE_MASK_LINK_PARAMS) { ++ free(this->info.link_params); ++ this->info.link_params = spa_pod_copy(info->link_params); ++ } ++ ++ if (info->change_mask & PW_ENDPOINT_STREAM_CHANGE_MASK_PROPS) ++ pw_properties_update(this->props, info->props); ++ ++ if (info->change_mask & PW_ENDPOINT_STREAM_CHANGE_MASK_PARAMS) { ++ size_t size = info->n_params * sizeof(struct spa_param_info); ++ ++ this->info.params = realloc(this->info.params, size); ++ if (size > 0 && !this->info.params) { ++ this->info.n_params = 0; ++ goto no_mem; ++ } ++ this->info.n_params = info->n_params; ++ ++ memcpy(this->info.params, info->params, size); ++ } ++ ++ if (!this->info.name) ++ this->info.name = strdup(info->name); ++ ++ this->info.change_mask = info->change_mask; ++ spa_list_for_each(resource, &this->global->resource_list, link) { ++ pw_endpoint_stream_resource_info(resource, &this->info); ++ } ++ this->info.change_mask = 0; ++ } ++ ++ return 0; ++ ++ no_mem: ++ pw_log_error(NAME" can't update: no memory"); ++ pw_resource_error(this->client_ep->resource, -ENOMEM, ++ NAME" can't update: no memory"); ++ return -ENOMEM; ++} ++ ++static void endpoint_stream_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_stream_unbind, ++}; ++ ++static int endpoint_stream_bind(void *_data, struct pw_client *client, ++ uint32_t permissions, uint32_t version, uint32_t id) ++{ ++ struct endpoint_stream *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->stream = this; ++ pw_resource_add_listener(resource, &data->resource_listener, ++ &resource_events, resource); ++ pw_resource_add_object_listener(resource, &data->object_listener, ++ &methods, resource); ++ ++ pw_log_debug(NAME" %p: bound to %d", this, resource->id); ++ ++ spa_list_append(&global->resource_list, &resource->link); ++ ++ this->info.change_mask = PW_ENDPOINT_STREAM_CHANGE_MASK_ALL; ++ pw_endpoint_stream_resource_info(resource, &this->info); ++ this->info.change_mask = 0; ++ ++ return 0; ++ ++ no_mem: ++ pw_log_error(NAME" can't create resource: no memory"); ++ pw_resource_error(this->client_ep->resource, -ENOMEM, ++ NAME" can't create resource: no memory"); ++ return -ENOMEM; ++} ++ ++int endpoint_stream_init(struct endpoint_stream *this, ++ uint32_t id, uint32_t endpoint_id, ++ struct client_endpoint *client_ep, ++ struct pw_core *core, ++ struct pw_properties *properties) ++{ ++ pw_log_debug(NAME" %p: new", this); ++ ++ this->client_ep = client_ep; ++ this->id = id; ++ this->props = properties; ++ ++ properties = pw_properties_copy(properties); ++ if (!properties) ++ goto no_mem; ++ ++ this->global = pw_global_new (core, ++ PW_TYPE_INTERFACE_EndpointStream, ++ PW_VERSION_ENDPOINT_STREAM_PROXY, ++ properties, endpoint_stream_bind, this); ++ if (!this->global) ++ goto no_mem; ++ ++ this->info.version = PW_VERSION_ENDPOINT_STREAM_INFO; ++ this->info.id = this->global->id; ++ this->info.endpoint_id = endpoint_id; ++ this->info.props = &this->props->dict; ++ ++ return pw_global_register(this->global); ++ ++ no_mem: ++ pw_log_error(NAME" - can't create - out of memory"); ++ return -ENOMEM; ++} ++ ++void endpoint_stream_clear(struct endpoint_stream *this) ++{ ++ uint32_t i; ++ ++ pw_log_debug(NAME" %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.name); ++ free(this->info.link_params); ++ free(this->info.params); ++ ++ if (this->props) ++ pw_properties_free(this->props); ++} +diff --git a/src/modules/module-session-manager/endpoint-stream.h b/src/modules/module-session-manager/endpoint-stream.h +new file mode 100644 +index 00000000..99bd4836 +--- /dev/null ++++ b/src/modules/module-session-manager/endpoint-stream.h +@@ -0,0 +1,64 @@ ++/* 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 MODULE_SESSION_MANAGER_ENDPOINT_STREAM_H ++#define MODULE_SESSION_MANAGER_ENDPOINT_STREAM_H ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++struct client_endpoint; ++ ++struct endpoint_stream { ++ struct spa_list link; ++ struct client_endpoint *client_ep; ++ struct pw_global *global; ++ uint32_t id; /* endpoint-local stream id */ ++ uint32_t n_params; ++ struct spa_pod **params; ++ struct pw_endpoint_stream_info info; ++ struct pw_properties *props; /* wrapper of info.props */ ++}; ++ ++int endpoint_stream_init(struct endpoint_stream *this, ++ uint32_t id, uint32_t endpoint_id, ++ struct client_endpoint *client_ep, ++ struct pw_core *core, ++ struct pw_properties *properties); ++ ++void endpoint_stream_clear(struct endpoint_stream *this); ++ ++int endpoint_stream_update(struct endpoint_stream *this, ++ uint32_t change_mask, ++ uint32_t n_params, ++ const struct spa_pod **params, ++ const struct pw_endpoint_stream_info *info); ++ ++#ifdef __cplusplus ++} /* extern "C" */ ++#endif ++ ++#endif /* MODULE_SESSION_MANAGER_ENDPOINT_STREAM_H */ +diff --git a/src/modules/module-session-manager/endpoint.c b/src/modules/module-session-manager/endpoint.c +new file mode 100644 +index 00000000..0866e71d +--- /dev/null ++++ b/src/modules/module-session-manager/endpoint.c +@@ -0,0 +1,343 @@ ++/* 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 <stdbool.h> ++#include <string.h> ++ ++#include <pipewire/pipewire.h> ++#include <extensions/session-manager.h> ++ ++#include <spa/pod/filter.h> ++ ++#include "endpoint.h" ++#include "client-endpoint.h" ++ ++#include <pipewire/private.h> ++ ++#define NAME "endpoint" ++ ++struct resource_data { ++ struct endpoint *endpoint; ++ struct spa_hook resource_listener; ++ struct spa_hook object_listener; ++ uint32_t n_subscribe_ids; ++ uint32_t subscribe_ids[32]; ++}; ++ ++#define pw_endpoint_resource(r,m,v,...) \ ++ pw_resource_call(r,struct pw_endpoint_proxy_events,m,v,__VA_ARGS__) ++#define pw_endpoint_resource_info(r,...) \ ++ pw_endpoint_resource(r,info,0,__VA_ARGS__) ++#define pw_endpoint_resource_param(r,...) \ ++ pw_endpoint_resource(r,param,0,__VA_ARGS__) ++ ++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 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(NAME" %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(NAME" %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 endpoint *this = data->endpoint; ++ ++ pw_client_endpoint_resource_set_param(this->client_ep->resource, ++ id, flags, param); ++ ++ return 0; ++} ++ ++static const struct pw_endpoint_proxy_methods methods = { ++ PW_VERSION_ENDPOINT_PROXY_METHODS, ++ .subscribe_params = endpoint_subscribe_params, ++ .enum_params = endpoint_enum_params, ++ .set_param = endpoint_set_param, ++}; ++ ++static void endpoint_notify_subscribed(struct 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); ++ } ++ } ++ } ++} ++ ++int endpoint_update(struct endpoint *this, ++ uint32_t change_mask, ++ uint32_t n_params, ++ const struct spa_pod **params, ++ const struct pw_endpoint_info *info) ++{ ++ if (change_mask & PW_CLIENT_ENDPOINT_UPDATE_PARAMS) { ++ uint32_t i; ++ size_t size = n_params * sizeof(struct spa_pod *); ++ ++ pw_log_debug(NAME" %p: update %d params", this, n_params); ++ ++ for (i = 0; i < this->n_params; i++) ++ free(this->params[i]); ++ this->params = realloc(this->params, size); ++ if (size > 0 && !this->params) { ++ this->n_params = 0; ++ goto no_mem; ++ } ++ this->n_params = n_params; ++ ++ 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); ++ } ++ } ++ ++ if (change_mask & PW_CLIENT_ENDPOINT_UPDATE_INFO) { ++ struct pw_resource *resource; ++ ++ if (info->change_mask & PW_ENDPOINT_CHANGE_MASK_STREAMS) ++ this->info.n_streams = info->n_streams; ++ ++ if (info->change_mask & PW_ENDPOINT_CHANGE_MASK_SESSION) ++ this->info.session_id = info->session_id; ++ ++ if (info->change_mask & PW_ENDPOINT_CHANGE_MASK_PROPS) ++ pw_properties_update(this->props, info->props); ++ ++ if (info->change_mask & PW_ENDPOINT_CHANGE_MASK_PARAMS) { ++ size_t size = info->n_params * sizeof(struct spa_param_info); ++ ++ this->info.params = realloc(this->info.params, size); ++ if (size > 0 && !this->info.params) { ++ this->info.n_params = 0; ++ goto no_mem; ++ } ++ this->info.n_params = info->n_params; ++ ++ memcpy(this->info.params, info->params, size); ++ } ++ ++ if (!this->info.name) { ++ this->info.name = strdup(info->name); ++ this->info.media_class = strdup(info->media_class); ++ this->info.direction = info->direction; ++ this->info.flags = info->flags; ++ } ++ ++ this->info.change_mask = info->change_mask; ++ spa_list_for_each(resource, &this->global->resource_list, link) { ++ pw_endpoint_resource_info(resource, &this->info); ++ } ++ this->info.change_mask = 0; ++ } ++ ++ return 0; ++ ++ no_mem: ++ pw_log_error(NAME" can't update: no memory"); ++ pw_resource_error(this->client_ep->resource, -ENOMEM, ++ NAME" can't update: no memory"); ++ return -ENOMEM; ++} ++ ++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 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; ++ pw_resource_add_listener(resource, &data->resource_listener, ++ &resource_events, resource); ++ pw_resource_add_object_listener(resource, &data->object_listener, ++ &methods, resource); ++ ++ pw_log_debug(NAME" %p: bound to %d", this, resource->id); ++ ++ spa_list_append(&global->resource_list, &resource->link); ++ ++ this->info.change_mask = PW_ENDPOINT_CHANGE_MASK_ALL; ++ pw_endpoint_resource_info(resource, &this->info); ++ this->info.change_mask = 0; ++ ++ return 0; ++ ++ no_mem: ++ pw_log_error(NAME" can't create resource: no memory"); ++ pw_resource_error(this->client_ep->resource, -ENOMEM, ++ NAME" can't create resource: no memory"); ++ return -ENOMEM; ++} ++ ++int endpoint_init(struct endpoint *this, ++ struct client_endpoint *client_ep, ++ struct pw_core *core, ++ struct pw_properties *properties) ++{ ++ const char *keys[] = { ++ PW_KEY_FACTORY_ID, ++ PW_KEY_CLIENT_ID, ++ NULL ++ }; ++ ++ pw_log_debug(NAME" %p: new", this); ++ ++ this->client_ep = client_ep; ++ this->props = properties; ++ ++ properties = pw_properties_new(NULL, NULL); ++ if (!properties) ++ goto no_mem; ++ ++ pw_properties_copy_keys(this->props, properties, keys); ++ ++ this->global = pw_global_new (core, ++ PW_TYPE_INTERFACE_Endpoint, ++ PW_VERSION_ENDPOINT_PROXY, ++ properties, endpoint_bind, this); ++ if (!this->global) ++ goto no_mem; ++ ++ pw_properties_setf(this->props, PW_KEY_ENDPOINT_ID, "%u", this->global->id); ++ ++ this->info.version = PW_VERSION_ENDPOINT_INFO; ++ this->info.id = this->global->id; ++ this->info.props = &this->props->dict; ++ ++ pw_client_endpoint_resource_set_id(client_ep->resource, this->global->id); ++ ++ return pw_global_register(this->global); ++ ++ no_mem: ++ pw_log_error(NAME" - can't create - out of memory"); ++ return -ENOMEM; ++} ++ ++void endpoint_clear(struct endpoint *this) ++{ ++ uint32_t i; ++ ++ pw_log_debug(NAME" %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.name); ++ free(this->info.media_class); ++ free(this->info.params); ++ ++ if (this->props) ++ pw_properties_free(this->props); ++} +diff --git a/src/modules/module-session-manager/endpoint.h b/src/modules/module-session-manager/endpoint.h +new file mode 100644 +index 00000000..89d26028 +--- /dev/null ++++ b/src/modules/module-session-manager/endpoint.h +@@ -0,0 +1,61 @@ ++/* 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 MODULE_SESSION_MANAGER_ENDPOINT_H ++#define MODULE_SESSION_MANAGER_ENDPOINT_H ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++struct client_endpoint; ++ ++struct endpoint { ++ struct client_endpoint *client_ep; ++ struct pw_global *global; ++ uint32_t n_params; ++ struct spa_pod **params; ++ struct pw_endpoint_info info; ++ struct pw_properties *props; /* wrapper of info.props */ ++}; ++ ++int endpoint_init(struct endpoint *this, ++ struct client_endpoint *client_ep, ++ struct pw_core *core, ++ struct pw_properties *properties); ++ ++void endpoint_clear(struct endpoint *this); ++ ++int endpoint_update(struct endpoint *this, ++ uint32_t change_mask, ++ uint32_t n_params, ++ const struct spa_pod **params, ++ const struct pw_endpoint_info *info); ++ ++#ifdef __cplusplus ++} /* extern "C" */ ++#endif ++ ++#endif /* MODULE_SESSION_MANAGER_ENDPOINT_H */ +diff --git a/src/modules/module-session-manager/protocol-native.c b/src/modules/module-session-manager/protocol-native.c +new file mode 100644 +index 00000000..2c791ffc +--- /dev/null ++++ b/src/modules/module-session-manager/protocol-native.c +@@ -0,0 +1,2125 @@ ++/* 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/session-manager.h> ++#include <extensions/protocol-native.h> ++ ++static void push_dict(struct spa_pod_builder *b, const struct spa_dict *dict) ++{ ++ struct spa_pod_frame f; ++ uint32_t n_items; ++ uint32_t i; ++ ++ n_items = dict ? dict->n_items : 0; ++ ++ spa_pod_builder_push_struct(b, &f); ++ spa_pod_builder_add(b, SPA_POD_Int(n_items), NULL); ++ for (i = 0; i < n_items; i++) { ++ spa_pod_builder_add(b, ++ SPA_POD_String(dict->items[i].key), ++ SPA_POD_String(dict->items[i].value), ++ NULL); ++ } ++ spa_pod_builder_pop(b, &f); ++} ++ ++/* macro because of alloca() */ ++#define parse_dict(p, f, dict) \ ++do { \ ++ uint32_t i; \ ++ \ ++ if (spa_pod_parser_push_struct(p, f) < 0 || \ ++ spa_pod_parser_get(p, SPA_POD_Int(&(dict)->n_items), NULL) < 0) \ ++ return -EINVAL; \ ++ \ ++ if ((dict)->n_items > 0) { \ ++ (dict)->items = alloca((dict)->n_items * sizeof(struct spa_dict_item)); \ ++ for (i = 0; i < (dict)->n_items; i++) { \ ++ if (spa_pod_parser_get(p, \ ++ SPA_POD_String(&(dict)->items[i].key), \ ++ SPA_POD_String(&(dict)->items[i].value), \ ++ NULL) < 0) \ ++ return -EINVAL; \ ++ } \ ++ } \ ++ spa_pod_parser_pop(p, f); \ ++} while(0) ++ ++static void push_param_infos(struct spa_pod_builder *b, uint32_t n_params, ++ const struct spa_param_info *params) ++{ ++ struct spa_pod_frame f; ++ uint32_t i; ++ ++ spa_pod_builder_push_struct(b, &f); ++ spa_pod_builder_add(b, SPA_POD_Int(n_params), NULL); ++ for (i = 0; i < n_params; i++) { ++ spa_pod_builder_add(b, ++ SPA_POD_Id(params[i].id), ++ SPA_POD_Int(params[i].flags), ++ NULL); ++ } ++ spa_pod_builder_pop(b, &f); ++} ++ ++/* macro because of alloca() */ ++#define parse_param_infos(p, f, n_params_p, params_p) \ ++do { \ ++ uint32_t i; \ ++ \ ++ if (spa_pod_parser_push_struct(p, f) < 0 || \ ++ spa_pod_parser_get(p, SPA_POD_Int(n_params_p), NULL) < 0) \ ++ return -EINVAL; \ ++ \ ++ if (*(n_params_p) > 0) { \ ++ *(params_p) = alloca(*(n_params_p) * sizeof(struct spa_param_info)); \ ++ for (i = 0; i < *(n_params_p); i++) { \ ++ if (spa_pod_parser_get(p, \ ++ SPA_POD_Id(&(*(params_p))[i].id), \ ++ SPA_POD_Int(&(*(params_p))[i].flags), \ ++ NULL) < 0) \ ++ return -EINVAL; \ ++ } \ ++ } \ ++ spa_pod_parser_pop(p, f); \ ++} while(0) ++ ++/*********************************************** ++ * INFO STRUCTURES ++ ***********************************************/ ++ ++static void ++marshal_pw_session_info(struct spa_pod_builder *b, ++ const struct pw_session_info *info) ++{ ++ struct spa_pod_frame f; ++ ++ spa_pod_builder_push_struct(b, &f); ++ spa_pod_builder_add(b, ++ SPA_POD_Int(info->version), ++ SPA_POD_Int(info->id), ++ SPA_POD_Int(info->change_mask), ++ NULL); ++ push_dict(b, info->props); ++ push_param_infos(b, info->n_params, info->params); ++ spa_pod_builder_pop(b, &f); ++} ++ ++/* macro because of alloca() */ ++#define demarshal_pw_session_info(p, f, info) \ ++do { \ ++ struct spa_pod_frame sub_f; \ ++ uint32_t version; \ ++ \ ++ if (spa_pod_parser_push_struct(p, f) < 0 || \ ++ spa_pod_parser_get(p, \ ++ SPA_POD_Int(&version), \ ++ SPA_POD_Int(&(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; \ ++ \ ++ (info)->change_mask &= PW_SESSION_CHANGE_MASK_ALL; \ ++ \ ++ parse_dict(p, &sub_f, (info)->props); \ ++ parse_param_infos(p, &sub_f, &(info)->n_params, &(info)->params); \ ++ \ ++ spa_pod_parser_pop(p, f); \ ++} while(0) ++ ++static void ++marshal_pw_endpoint_info(struct spa_pod_builder *b, ++ const struct pw_endpoint_info *info) ++{ ++ struct spa_pod_frame f; ++ ++ spa_pod_builder_push_struct(b, &f); ++ spa_pod_builder_add(b, ++ SPA_POD_Int(info->version), ++ SPA_POD_Int(info->id), ++ SPA_POD_String(info->name), ++ SPA_POD_String(info->media_class), ++ SPA_POD_Int(info->direction), ++ SPA_POD_Int(info->flags), ++ SPA_POD_Int(info->change_mask), ++ NULL); ++ push_dict(b, info->props); ++ push_param_infos(b, info->n_params, info->params); ++ spa_pod_builder_pop(b, &f); ++} ++ ++/* macro because of alloca() */ ++#define demarshal_pw_endpoint_info(p, f, info) \ ++do { \ ++ struct spa_pod_frame sub_f; \ ++ uint32_t version; \ ++ \ ++ if (spa_pod_parser_push_struct(p, f) < 0 || \ ++ spa_pod_parser_get(p, \ ++ SPA_POD_Int(&version), \ ++ SPA_POD_Int(&(info)->id), \ ++ SPA_POD_String(&(info)->name), \ ++ SPA_POD_String(&(info)->media_class), \ ++ SPA_POD_Int(&(info)->direction), \ ++ SPA_POD_Int(&(info)->flags), \ ++ SPA_POD_Int(&(info)->change_mask), \ ++ NULL) < 0) \ ++ return -EINVAL; \ ++ \ ++ (info)->change_mask &= PW_ENDPOINT_CHANGE_MASK_ALL; \ ++ \ ++ parse_dict(p, &sub_f, (info)->props); \ ++ parse_param_infos(p, &sub_f, &(info)->n_params, &(info)->params); \ ++ \ ++ spa_pod_parser_pop(p, f); \ ++} while(0) ++ ++static void ++marshal_pw_endpoint_stream_info(struct spa_pod_builder *b, ++ const struct pw_endpoint_stream_info *info) ++{ ++ struct spa_pod_frame f; ++ ++ spa_pod_builder_push_struct(b, &f); ++ spa_pod_builder_add(b, ++ SPA_POD_Int(info->version), ++ SPA_POD_Int(info->id), ++ SPA_POD_Int(info->endpoint_id), ++ SPA_POD_String(info->name), ++ SPA_POD_Int(info->change_mask), ++ SPA_POD_Pod(info->link_params), ++ NULL); ++ push_dict(b, info->props); ++ push_param_infos(b, info->n_params, info->params); ++ spa_pod_builder_pop(b, &f); ++} ++ ++/* macro because of alloca() */ ++#define demarshal_pw_endpoint_stream_info(p, f, info) \ ++do { \ ++ struct spa_pod_frame sub_f; \ ++ uint32_t version; \ ++ \ ++ if (spa_pod_parser_push_struct(p, f) < 0 || \ ++ spa_pod_parser_get(p, \ ++ SPA_POD_Int(&version), \ ++ SPA_POD_Int(&(info)->id), \ ++ SPA_POD_Int(&(info)->endpoint_id), \ ++ SPA_POD_String(&(info)->name), \ ++ SPA_POD_Int(&(info)->change_mask), \ ++ SPA_POD_Pod(&(info)->link_params), \ ++ NULL) < 0) \ ++ return -EINVAL; \ ++ \ ++ (info)->change_mask &= PW_ENDPOINT_STREAM_CHANGE_MASK_ALL; \ ++ \ ++ parse_dict(p, &sub_f, (info)->props); \ ++ parse_param_infos(p, &sub_f, &(info)->n_params, &(info)->params); \ ++ \ ++ spa_pod_parser_pop(p, f); \ ++} while(0) ++ ++static void ++marshal_pw_endpoint_link_info(struct spa_pod_builder *b, ++ const struct pw_endpoint_link_info *info) ++{ ++ struct spa_pod_frame f; ++ ++ spa_pod_builder_push_struct(b, &f); ++ spa_pod_builder_add(b, ++ SPA_POD_Int(info->version), ++ SPA_POD_Int(info->id), ++ SPA_POD_Int(info->session_id), ++ SPA_POD_Int(info->output_endpoint_id), ++ SPA_POD_Int(info->output_stream_id), ++ SPA_POD_Int(info->input_endpoint_id), ++ SPA_POD_Int(info->input_stream_id), ++ SPA_POD_Int(info->change_mask), ++ SPA_POD_Int(info->state), ++ SPA_POD_String(info->error), ++ NULL); ++ push_dict(b, info->props); ++ push_param_infos(b, info->n_params, info->params); ++ spa_pod_builder_pop(b, &f); ++} ++ ++/* macro because of alloca() */ ++#define demarshal_pw_endpoint_link_info(p, f, info) \ ++do { \ ++ struct spa_pod_frame sub_f; \ ++ uint32_t version; \ ++ \ ++ if (spa_pod_parser_push_struct(p, f) < 0 || \ ++ spa_pod_parser_get(p, \ ++ SPA_POD_Int(&version), \ ++ SPA_POD_Int(&(info)->id), \ ++ SPA_POD_Int(&(info)->session_id), \ ++ SPA_POD_Int(&(info)->output_endpoint_id), \ ++ SPA_POD_Int(&(info)->output_stream_id), \ ++ SPA_POD_Int(&(info)->input_endpoint_id), \ ++ SPA_POD_Int(&(info)->input_stream_id), \ ++ SPA_POD_Int(&(info)->change_mask), \ ++ SPA_POD_Int(&(info)->state), \ ++ SPA_POD_String(&(info)->error), \ ++ NULL) < 0) \ ++ return -EINVAL; \ ++ \ ++ (info)->change_mask &= PW_ENDPOINT_LINK_CHANGE_MASK_ALL; \ ++ \ ++ parse_dict(p, &sub_f, (info)->props); \ ++ parse_param_infos(p, &sub_f, &(info)->n_params, &(info)->params); \ ++ \ ++ spa_pod_parser_pop(p, f); \ ++} while(0) ++ ++/*********************************************** ++ * CLIENT ENDPOINT ++ ***********************************************/ ++ ++static int client_endpoint_marshal_set_id (void *object, uint32_t id) ++{ ++ struct pw_resource *resource = object; ++ struct spa_pod_builder *b; ++ ++ b = pw_protocol_native_begin_resource(resource, ++ PW_CLIENT_ENDPOINT_PROXY_EVENT_SET_ID, NULL); ++ ++ spa_pod_builder_add (b, SPA_POD_Int(id), NULL); ++ ++ return pw_protocol_native_end_resource(resource, b); ++} ++ ++static int client_endpoint_marshal_set_session_id (void *object, uint32_t id) ++{ ++ struct pw_resource *resource = object; ++ struct spa_pod_builder *b; ++ ++ b = pw_protocol_native_begin_resource(resource, ++ PW_CLIENT_ENDPOINT_PROXY_EVENT_SET_SESSION_ID, NULL); ++ ++ spa_pod_builder_add (b, SPA_POD_Int(id), NULL); ++ ++ return pw_protocol_native_end_resource(resource, b); ++} ++ ++static int 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)); ++ ++ return pw_protocol_native_end_resource(resource, b); ++} ++ ++static int client_endpoint_marshal_stream_set_param (void *object, ++ uint32_t stream_id, 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_STREAM_SET_PARAM, NULL); ++ ++ spa_pod_builder_add_struct(b, ++ SPA_POD_Int(stream_id), ++ SPA_POD_Id(id), ++ SPA_POD_Int(flags), ++ SPA_POD_Pod(param)); ++ ++ return pw_protocol_native_end_resource(resource, b); ++} ++ ++static int client_endpoint_marshal_add_listener(void *object, ++ struct spa_hook *listener, ++ const struct pw_client_endpoint_proxy_events *events, ++ void *data) ++{ ++ struct pw_proxy *proxy = object; ++ pw_proxy_add_object_listener(proxy, listener, events, data); ++ return 0; ++} ++ ++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 (info) ++ marshal_pw_endpoint_info(b, info); ++ else ++ spa_pod_builder_add(b, SPA_POD_Pod(NULL), NULL); ++ ++ spa_pod_builder_pop(b, &f); ++ ++ return pw_protocol_native_end_proxy(proxy, b); ++} ++ ++static int client_endpoint_marshal_stream_update(void *object, ++ uint32_t stream_id, ++ uint32_t change_mask, ++ uint32_t n_params, ++ const struct spa_pod **params, ++ const struct pw_endpoint_stream_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_STREAM_UPDATE, NULL); ++ ++ spa_pod_builder_push_struct(b, &f); ++ spa_pod_builder_add(b, ++ SPA_POD_Int(stream_id), ++ 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 (info) ++ marshal_pw_endpoint_stream_info(b, info); ++ else ++ spa_pod_builder_add(b, SPA_POD_Pod(NULL), NULL); ++ ++ spa_pod_builder_pop(b, &f); ++ ++ return pw_protocol_native_end_proxy(proxy, b); ++} ++ ++static int client_endpoint_demarshal_set_id(void *object, ++ const struct pw_protocol_native_message *msg) ++{ ++ struct pw_proxy *proxy = object; ++ struct spa_pod_parser prs; ++ uint32_t id; ++ ++ spa_pod_parser_init(&prs, msg->data, msg->size); ++ if (spa_pod_parser_get_struct(&prs, ++ SPA_POD_Int(&id)) < 0) ++ return -EINVAL; ++ ++ return pw_proxy_notify(proxy, struct pw_client_endpoint_proxy_events, ++ set_id, 0, id); ++} ++ ++static int client_endpoint_demarshal_set_session_id(void *object, ++ const struct pw_protocol_native_message *msg) ++{ ++ struct pw_proxy *proxy = object; ++ struct spa_pod_parser prs; ++ uint32_t id; ++ ++ spa_pod_parser_init(&prs, msg->data, msg->size); ++ if (spa_pod_parser_get_struct(&prs, ++ SPA_POD_Int(&id)) < 0) ++ return -EINVAL; ++ ++ return pw_proxy_notify(proxy, struct pw_client_endpoint_proxy_events, ++ set_session_id, 0, id); ++} ++ ++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_Int(&id), ++ SPA_POD_Int(&flags), ++ SPA_POD_PodObject(¶m)) < 0) ++ return -EINVAL; ++ ++ return pw_proxy_notify(proxy, struct pw_client_endpoint_proxy_events, ++ set_param, 0, id, flags, param); ++} ++ ++static int client_endpoint_demarshal_stream_set_param(void *object, ++ const struct pw_protocol_native_message *msg) ++{ ++ struct pw_proxy *proxy = object; ++ struct spa_pod_parser prs; ++ uint32_t stream_id, 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_Int(&stream_id), ++ SPA_POD_Int(&id), ++ SPA_POD_Int(&flags), ++ SPA_POD_PodObject(¶m)) < 0) ++ return -EINVAL; ++ ++ return pw_proxy_notify(proxy, struct pw_client_endpoint_proxy_events, ++ stream_set_param, 0, stream_id, id, flags, param); ++} ++ ++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[2]; ++ 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 }, *infop = NULL; ++ struct spa_pod *ipod; ++ uint32_t i; ++ ++ spa_pod_parser_init(&prs[0], msg->data, msg->size); ++ if (spa_pod_parser_push_struct(&prs[0], &f[0]) < 0 || ++ spa_pod_parser_get(&prs[0], ++ 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[0], ++ SPA_POD_PodObject(¶ms[i]), NULL) < 0) ++ return -EINVAL; ++ ++ if (spa_pod_parser_get(&prs[0], SPA_POD_PodStruct(&ipod), NULL) < 0) ++ return -EINVAL; ++ if (ipod) { ++ infop = &info; ++ spa_pod_parser_pod(&prs[1], ipod); ++ demarshal_pw_endpoint_info(&prs[1], &f[1], infop); ++ } ++ ++ return pw_resource_notify(resource, struct pw_client_endpoint_proxy_methods, ++ update, 0, change_mask, n_params, params, infop); ++} ++ ++static int client_endpoint_demarshal_stream_update(void *object, ++ const struct pw_protocol_native_message *msg) ++{ ++ struct pw_resource *resource = object; ++ struct spa_pod_parser prs[2]; ++ struct spa_pod_frame f[2]; ++ uint32_t stream_id, change_mask, n_params; ++ const struct spa_pod **params = NULL; ++ struct spa_dict props = SPA_DICT_INIT(NULL, 0); ++ struct pw_endpoint_stream_info info = { .props = &props }, *infop = NULL; ++ struct spa_pod *ipod; ++ uint32_t i; ++ ++ spa_pod_parser_init(&prs[0], msg->data, msg->size); ++ if (spa_pod_parser_push_struct(&prs[0], &f[0]) < 0 || ++ spa_pod_parser_get(&prs[0], ++ SPA_POD_Int(&stream_id), ++ 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[0], ++ SPA_POD_PodObject(¶ms[i]), NULL) < 0) ++ return -EINVAL; ++ ++ if (spa_pod_parser_get(&prs[0], SPA_POD_PodStruct(&ipod), NULL) < 0) ++ return -EINVAL; ++ if (ipod) { ++ infop = &info; ++ spa_pod_parser_pod(&prs[1], ipod); ++ demarshal_pw_endpoint_stream_info(&prs[1], &f[1], infop); ++ } ++ ++ return pw_resource_notify(resource, struct pw_client_endpoint_proxy_methods, ++ stream_update, 0, stream_id, change_mask, n_params, params, infop); ++} ++ ++static const struct pw_client_endpoint_proxy_events pw_protocol_native_client_endpoint_event_marshal = { ++ PW_VERSION_CLIENT_ENDPOINT_PROXY_EVENTS, ++ .set_id = client_endpoint_marshal_set_id, ++ .set_session_id = client_endpoint_marshal_set_session_id, ++ .set_param = client_endpoint_marshal_set_param, ++ .stream_set_param = client_endpoint_marshal_stream_set_param, ++}; ++ ++static const struct pw_protocol_native_demarshal ++pw_protocol_native_client_endpoint_event_demarshal[PW_CLIENT_ENDPOINT_PROXY_EVENT_NUM] = ++{ ++ [PW_CLIENT_ENDPOINT_PROXY_EVENT_SET_ID] = { client_endpoint_demarshal_set_id, 0 }, ++ [PW_CLIENT_ENDPOINT_PROXY_EVENT_SET_SESSION_ID] = { client_endpoint_demarshal_set_session_id, 0 }, ++ [PW_CLIENT_ENDPOINT_PROXY_EVENT_SET_PARAM] = { client_endpoint_demarshal_set_param, 0 }, ++ [PW_CLIENT_ENDPOINT_PROXY_EVENT_STREAM_SET_PARAM] = { client_endpoint_demarshal_stream_set_param, 0 }, ++}; ++ ++static const struct pw_client_endpoint_proxy_methods pw_protocol_native_client_endpoint_method_marshal = { ++ PW_VERSION_CLIENT_ENDPOINT_PROXY_METHODS, ++ .add_listener = client_endpoint_marshal_add_listener, ++ .update = client_endpoint_marshal_update, ++ .stream_update = client_endpoint_marshal_stream_update, ++}; ++ ++static const struct pw_protocol_native_demarshal ++pw_protocol_native_client_endpoint_method_demarshal[PW_CLIENT_ENDPOINT_PROXY_METHOD_NUM] = ++{ ++ [PW_CLIENT_ENDPOINT_PROXY_METHOD_ADD_LISTENER] = { NULL, 0 }, ++ [PW_CLIENT_ENDPOINT_PROXY_METHOD_UPDATE] = { client_endpoint_demarshal_update, 0 }, ++ [PW_CLIENT_ENDPOINT_PROXY_METHOD_STREAM_UPDATE] = { client_endpoint_demarshal_stream_update, 0 }, ++}; ++ ++static const struct pw_protocol_marshal pw_protocol_native_client_endpoint_marshal = { ++ PW_TYPE_INTERFACE_ClientEndpoint, ++ PW_VERSION_CLIENT_ENDPOINT_PROXY, ++ 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, ++}; ++ ++/*********************************************** ++ * CLIENT SESSION ++ ***********************************************/ ++ ++static int client_session_marshal_set_id (void *object, uint32_t id) ++{ ++ struct pw_resource *resource = object; ++ struct spa_pod_builder *b; ++ ++ b = pw_protocol_native_begin_resource(resource, ++ PW_CLIENT_SESSION_PROXY_EVENT_SET_ID, NULL); ++ ++ spa_pod_builder_add (b, SPA_POD_Int(id), NULL); ++ ++ return pw_protocol_native_end_resource(resource, b); ++} ++ ++static int client_session_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_SESSION_PROXY_EVENT_SET_PARAM, NULL); ++ ++ spa_pod_builder_add_struct(b, ++ SPA_POD_Int(id), ++ SPA_POD_Int(flags), ++ SPA_POD_Pod(param)); ++ ++ return pw_protocol_native_end_resource(resource, b); ++} ++ ++static int client_session_marshal_link_set_param (void *object, ++ uint32_t link_id, 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_SESSION_PROXY_EVENT_LINK_SET_PARAM, NULL); ++ ++ spa_pod_builder_add_struct(b, ++ SPA_POD_Int(link_id), ++ SPA_POD_Id(id), ++ SPA_POD_Int(flags), ++ SPA_POD_Pod(param)); ++ ++ return pw_protocol_native_end_resource(resource, b); ++} ++ ++static int client_session_marshal_create_link(void *object, ++ const struct spa_dict *props) ++{ ++ struct pw_resource *resource = object; ++ struct spa_pod_builder *b; ++ ++ spa_return_val_if_fail(props, -EINVAL); ++ ++ b = pw_protocol_native_begin_resource(resource, ++ PW_CLIENT_SESSION_PROXY_EVENT_CREATE_LINK, NULL); ++ ++ push_dict(b, props); ++ ++ return pw_protocol_native_end_resource(resource, b); ++} ++ ++static int client_session_marshal_destroy_link (void *object, uint32_t link_id) ++{ ++ struct pw_resource *resource = object; ++ struct spa_pod_builder *b; ++ ++ b = pw_protocol_native_begin_resource(resource, ++ PW_CLIENT_SESSION_PROXY_EVENT_DESTROY_LINK, NULL); ++ ++ spa_pod_builder_add(b, SPA_POD_Int(link_id), NULL); ++ ++ return pw_protocol_native_end_resource(resource, b); ++} ++ ++static int client_session_marshal_link_request_state (void *object, ++ uint32_t link_id, uint32_t state) ++{ ++ struct pw_resource *resource = object; ++ struct spa_pod_builder *b; ++ ++ b = pw_protocol_native_begin_resource(resource, ++ PW_CLIENT_SESSION_PROXY_EVENT_LINK_REQUEST_STATE, NULL); ++ ++ spa_pod_builder_add_struct(b, ++ SPA_POD_Int(link_id), ++ SPA_POD_Int(state)); ++ ++ return pw_protocol_native_end_resource(resource, b); ++} ++ ++static int client_session_marshal_add_listener(void *object, ++ struct spa_hook *listener, ++ const struct pw_client_session_proxy_events *events, ++ void *data) ++{ ++ struct pw_proxy *proxy = object; ++ pw_proxy_add_object_listener(proxy, listener, events, data); ++ return 0; ++} ++ ++static int client_session_marshal_update(void *object, ++ uint32_t change_mask, ++ uint32_t n_params, ++ const struct spa_pod **params, ++ const struct pw_session_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_SESSION_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 (info) ++ marshal_pw_session_info(b, info); ++ else ++ spa_pod_builder_add(b, SPA_POD_Pod(NULL), NULL); ++ ++ spa_pod_builder_pop(b, &f); ++ ++ return pw_protocol_native_end_proxy(proxy, b); ++} ++ ++static int client_session_marshal_link_update(void *object, ++ uint32_t link_id, ++ uint32_t change_mask, ++ uint32_t n_params, ++ const struct spa_pod **params, ++ const struct pw_endpoint_link_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_SESSION_PROXY_METHOD_LINK_UPDATE, NULL); ++ ++ spa_pod_builder_push_struct(b, &f); ++ spa_pod_builder_add(b, ++ SPA_POD_Int(link_id), ++ 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 (info) ++ marshal_pw_endpoint_link_info(b, info); ++ else ++ spa_pod_builder_add(b, SPA_POD_Pod(NULL), NULL); ++ ++ spa_pod_builder_pop(b, &f); ++ ++ return pw_protocol_native_end_proxy(proxy, b); ++} ++ ++static int client_session_demarshal_set_id(void *object, ++ const struct pw_protocol_native_message *msg) ++{ ++ struct pw_proxy *proxy = object; ++ struct spa_pod_parser prs; ++ uint32_t id; ++ ++ spa_pod_parser_init(&prs, msg->data, msg->size); ++ if (spa_pod_parser_get_struct(&prs, ++ SPA_POD_Int(&id)) < 0) ++ return -EINVAL; ++ ++ return pw_proxy_notify(proxy, struct pw_client_session_proxy_events, ++ set_id, 0, id); ++} ++ ++static int client_session_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_Int(&id), ++ SPA_POD_Int(&flags), ++ SPA_POD_PodObject(¶m)) < 0) ++ return -EINVAL; ++ ++ return pw_proxy_notify(proxy, struct pw_client_session_proxy_events, ++ set_param, 0, id, flags, param); ++} ++ ++static int client_session_demarshal_link_set_param(void *object, ++ const struct pw_protocol_native_message *msg) ++{ ++ struct pw_proxy *proxy = object; ++ struct spa_pod_parser prs; ++ uint32_t link_id, 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_Int(&link_id), ++ SPA_POD_Int(&id), ++ SPA_POD_Int(&flags), ++ SPA_POD_PodObject(¶m)) < 0) ++ return -EINVAL; ++ ++ return pw_proxy_notify(proxy, struct pw_client_session_proxy_events, ++ link_set_param, 0, link_id, id, flags, param); ++} ++ ++static int client_session_demarshal_create_link(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); ++ ++ spa_pod_parser_init(&prs, msg->data, msg->size); ++ parse_dict(&prs, &f, &props); ++ ++ return pw_proxy_notify(proxy, struct pw_client_session_proxy_events, ++ create_link, 0, &props); ++} ++ ++static int client_session_demarshal_destroy_link(void *object, ++ const struct pw_protocol_native_message *msg) ++{ ++ struct pw_proxy *proxy = object; ++ struct spa_pod_parser prs; ++ uint32_t link_id; ++ ++ spa_pod_parser_init(&prs, msg->data, msg->size); ++ if (spa_pod_parser_get_struct(&prs, ++ SPA_POD_Int(&link_id)) < 0) ++ return -EINVAL; ++ ++ return pw_proxy_notify(proxy, struct pw_client_session_proxy_events, ++ destroy_link, 0, link_id); ++} ++ ++static int client_session_demarshal_link_request_state(void *object, ++ const struct pw_protocol_native_message *msg) ++{ ++ struct pw_proxy *proxy = object; ++ struct spa_pod_parser prs; ++ uint32_t link_id, state; ++ ++ spa_pod_parser_init(&prs, msg->data, msg->size); ++ if (spa_pod_parser_get_struct(&prs, ++ SPA_POD_Int(&link_id), ++ SPA_POD_Int(&state)) < 0) ++ return -EINVAL; ++ ++ return pw_proxy_notify(proxy, struct pw_client_session_proxy_events, ++ link_request_state, 0, link_id, state); ++} ++ ++static int client_session_demarshal_update(void *object, ++ const struct pw_protocol_native_message *msg) ++{ ++ struct pw_resource *resource = object; ++ struct spa_pod_parser prs[2]; ++ 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_session_info info = { .props = &props }, *infop = NULL; ++ struct spa_pod *ipod; ++ uint32_t i; ++ ++ spa_pod_parser_init(&prs[0], msg->data, msg->size); ++ if (spa_pod_parser_push_struct(&prs[0], &f[0]) < 0 || ++ spa_pod_parser_get(&prs[0], ++ 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[0], ++ SPA_POD_PodObject(¶ms[i]), NULL) < 0) ++ return -EINVAL; ++ ++ if (spa_pod_parser_get(&prs[0], SPA_POD_PodStruct(&ipod), NULL) < 0) ++ return -EINVAL; ++ if (ipod) { ++ infop = &info; ++ spa_pod_parser_pod(&prs[1], ipod); ++ demarshal_pw_session_info(&prs[1], &f[1], infop); ++ } ++ ++ return pw_resource_notify(resource, struct pw_client_session_proxy_methods, ++ update, 0, change_mask, n_params, params, infop); ++} ++ ++static int client_session_demarshal_link_update(void *object, ++ const struct pw_protocol_native_message *msg) ++{ ++ struct pw_resource *resource = object; ++ struct spa_pod_parser prs[2]; ++ struct spa_pod_frame f[2]; ++ uint32_t link_id, change_mask, n_params; ++ const struct spa_pod **params = NULL; ++ struct spa_dict props = SPA_DICT_INIT(NULL, 0); ++ struct pw_endpoint_link_info info = { .props = &props }, *infop = NULL; ++ struct spa_pod *ipod; ++ uint32_t i; ++ ++ spa_pod_parser_init(&prs[0], msg->data, msg->size); ++ if (spa_pod_parser_push_struct(&prs[0], &f[0]) < 0 || ++ spa_pod_parser_get(&prs[0], ++ SPA_POD_Int(&link_id), ++ 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[0], ++ SPA_POD_PodObject(¶ms[i]), NULL) < 0) ++ return -EINVAL; ++ ++ if (spa_pod_parser_get(&prs[0], SPA_POD_PodStruct(&ipod), NULL) < 0) ++ return -EINVAL; ++ if (ipod) { ++ infop = &info; ++ spa_pod_parser_pod(&prs[1], ipod); ++ demarshal_pw_endpoint_link_info(&prs[1], &f[1], infop); ++ } ++ ++ return pw_resource_notify(resource, struct pw_client_session_proxy_methods, ++ link_update, 0, link_id, change_mask, n_params, params, infop); ++} ++ ++static const struct pw_client_session_proxy_events pw_protocol_native_client_session_event_marshal = { ++ PW_VERSION_CLIENT_SESSION_PROXY_EVENTS, ++ .set_id = client_session_marshal_set_id, ++ .set_param = client_session_marshal_set_param, ++ .link_set_param = client_session_marshal_link_set_param, ++ .create_link = client_session_marshal_create_link, ++ .destroy_link = client_session_marshal_destroy_link, ++ .link_request_state = client_session_marshal_link_request_state, ++}; ++ ++static const struct pw_protocol_native_demarshal ++pw_protocol_native_client_session_event_demarshal[PW_CLIENT_SESSION_PROXY_EVENT_NUM] = ++{ ++ [PW_CLIENT_SESSION_PROXY_EVENT_SET_ID] = { client_session_demarshal_set_id, 0 }, ++ [PW_CLIENT_SESSION_PROXY_EVENT_SET_PARAM] = { client_session_demarshal_set_param, 0 }, ++ [PW_CLIENT_SESSION_PROXY_EVENT_LINK_SET_PARAM] = { client_session_demarshal_link_set_param, 0 }, ++ [PW_CLIENT_SESSION_PROXY_EVENT_CREATE_LINK] = { client_session_demarshal_create_link, 0 }, ++ [PW_CLIENT_SESSION_PROXY_EVENT_DESTROY_LINK] = { client_session_demarshal_destroy_link, 0 }, ++ [PW_CLIENT_SESSION_PROXY_EVENT_LINK_REQUEST_STATE] = { client_session_demarshal_link_request_state, 0 }, ++}; ++ ++static const struct pw_client_session_proxy_methods pw_protocol_native_client_session_method_marshal = { ++ PW_VERSION_CLIENT_SESSION_PROXY_METHODS, ++ .add_listener = client_session_marshal_add_listener, ++ .update = client_session_marshal_update, ++ .link_update = client_session_marshal_link_update, ++}; ++ ++static const struct pw_protocol_native_demarshal ++pw_protocol_native_client_session_method_demarshal[PW_CLIENT_SESSION_PROXY_METHOD_NUM] = ++{ ++ [PW_CLIENT_SESSION_PROXY_METHOD_ADD_LISTENER] = { NULL, 0 }, ++ [PW_CLIENT_SESSION_PROXY_METHOD_UPDATE] = { client_session_demarshal_update, 0 }, ++ [PW_CLIENT_SESSION_PROXY_METHOD_LINK_UPDATE] = { client_session_demarshal_link_update, 0 }, ++}; ++ ++static const struct pw_protocol_marshal pw_protocol_native_client_session_marshal = { ++ PW_TYPE_INTERFACE_ClientSession, ++ PW_VERSION_CLIENT_SESSION_PROXY, ++ PW_CLIENT_SESSION_PROXY_METHOD_NUM, ++ PW_CLIENT_SESSION_PROXY_EVENT_NUM, ++ &pw_protocol_native_client_session_method_marshal, ++ &pw_protocol_native_client_session_method_demarshal, ++ &pw_protocol_native_client_session_event_marshal, ++ &pw_protocol_native_client_session_event_demarshal, ++}; ++ ++/*********************************************** ++ * ENDPOINT LINK ++ ***********************************************/ ++ ++static void endpoint_link_marshal_info (void *object, ++ const struct pw_endpoint_link_info *info) ++{ ++ struct pw_resource *resource = object; ++ struct spa_pod_builder *b; ++ ++ b = pw_protocol_native_begin_resource(resource, ++ PW_ENDPOINT_LINK_PROXY_EVENT_INFO, NULL); ++ ++ marshal_pw_endpoint_link_info(b, info); ++ ++ pw_protocol_native_end_resource(resource, b); ++} ++ ++static void endpoint_link_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_LINK_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_link_marshal_add_listener(void *object, ++ struct spa_hook *listener, ++ const struct pw_endpoint_link_proxy_events *events, ++ void *data) ++{ ++ struct pw_proxy *proxy = object; ++ pw_proxy_add_object_listener(proxy, listener, events, data); ++ return 0; ++} ++ ++static int endpoint_link_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_LINK_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_link_marshal_enum_params(void *object, ++ int seq, uint32_t id, ++ uint32_t start, uint32_t num, ++ const struct spa_pod *filter) ++{ ++ struct pw_protocol_native_message *msg; ++ struct pw_proxy *proxy = object; ++ struct spa_pod_builder *b; ++ ++ b = pw_protocol_native_begin_proxy(proxy, ++ PW_ENDPOINT_LINK_PROXY_METHOD_ENUM_PARAMS, &msg); ++ ++ spa_pod_builder_add_struct(b, ++ SPA_POD_Int(SPA_RESULT_RETURN_ASYNC(msg->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_link_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_LINK_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_link_marshal_request_state(void *object, ++ enum pw_endpoint_link_state state) ++{ ++ struct pw_proxy *proxy = object; ++ struct spa_pod_builder *b; ++ ++ b = pw_protocol_native_begin_proxy(proxy, ++ PW_ENDPOINT_LINK_PROXY_METHOD_REQUEST_STATE, NULL); ++ ++ spa_pod_builder_add_struct(b, SPA_POD_Int(state)); ++ ++ return pw_protocol_native_end_proxy(proxy, b); ++} ++ ++static int endpoint_link_marshal_destroy(void *object) ++{ ++ struct pw_proxy *proxy = object; ++ struct spa_pod_builder *b; ++ ++ b = pw_protocol_native_begin_proxy(proxy, ++ PW_ENDPOINT_LINK_PROXY_METHOD_DESTROY, NULL); ++ ++ return pw_protocol_native_end_proxy(proxy, b); ++} ++ ++static int endpoint_link_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_link_info info = { .props = &props }; ++ ++ spa_pod_parser_init(&prs, msg->data, msg->size); ++ ++ demarshal_pw_endpoint_link_info(&prs, &f, &info); ++ ++ return pw_proxy_notify(proxy, struct pw_endpoint_link_proxy_events, ++ info, 0, &info); ++} ++ ++static int endpoint_link_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(¶m)) < 0) ++ return -EINVAL; ++ ++ return pw_proxy_notify(proxy, struct pw_endpoint_link_proxy_events, ++ param, 0, seq, id, index, next, param); ++} ++ ++static int endpoint_link_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_notify(resource, struct pw_endpoint_link_proxy_methods, ++ subscribe_params, 0, ids, n_ids); ++} ++ ++static int endpoint_link_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_notify(resource, struct pw_endpoint_link_proxy_methods, ++ enum_params, 0, seq, id, index, num, filter); ++} ++ ++static int endpoint_link_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(¶m)) < 0) ++ return -EINVAL; ++ ++ return pw_resource_notify(resource, struct pw_endpoint_link_proxy_methods, ++ set_param, 0, id, flags, param); ++} ++ ++static int endpoint_link_demarshal_request_state(void *object, ++ const struct pw_protocol_native_message *msg) ++{ ++ struct pw_resource *resource = object; ++ struct spa_pod_parser prs; ++ enum pw_endpoint_link_state state; ++ ++ spa_pod_parser_init(&prs, msg->data, msg->size); ++ if (spa_pod_parser_get_struct(&prs, ++ SPA_POD_Int(&state)) < 0) ++ return -EINVAL; ++ ++ return pw_resource_notify(resource, struct pw_endpoint_link_proxy_methods, ++ request_state, 0, state); ++} ++ ++static int endpoint_link_demarshal_destroy(void *object, ++ const struct pw_protocol_native_message *msg) ++{ ++ struct pw_resource *resource = object; ++ ++ return pw_resource_notify(resource, struct pw_endpoint_link_proxy_methods, ++ destroy, 0); ++} ++ ++static const struct pw_endpoint_link_proxy_events pw_protocol_native_endpoint_link_event_marshal = { ++ PW_VERSION_ENDPOINT_LINK_PROXY_EVENTS, ++ .info = endpoint_link_marshal_info, ++ .param = endpoint_link_marshal_param, ++}; ++ ++static const struct pw_protocol_native_demarshal ++pw_protocol_native_endpoint_link_event_demarshal[PW_ENDPOINT_LINK_PROXY_EVENT_NUM] = ++{ ++ [PW_ENDPOINT_LINK_PROXY_EVENT_INFO] = { endpoint_link_demarshal_info, 0 }, ++ [PW_ENDPOINT_LINK_PROXY_EVENT_PARAM] = { endpoint_link_demarshal_param, 0 }, ++}; ++ ++static const struct pw_endpoint_link_proxy_methods pw_protocol_native_endpoint_link_method_marshal = { ++ PW_VERSION_ENDPOINT_LINK_PROXY_METHODS, ++ .add_listener = endpoint_link_marshal_add_listener, ++ .subscribe_params = endpoint_link_marshal_subscribe_params, ++ .enum_params = endpoint_link_marshal_enum_params, ++ .set_param = endpoint_link_marshal_set_param, ++ .request_state = endpoint_link_marshal_request_state, ++ .destroy = endpoint_link_marshal_destroy, ++}; ++ ++static const struct pw_protocol_native_demarshal ++pw_protocol_native_endpoint_link_method_demarshal[PW_ENDPOINT_LINK_PROXY_METHOD_NUM] = ++{ ++ [PW_ENDPOINT_LINK_PROXY_METHOD_ADD_LISTENER] = { NULL, 0 }, ++ [PW_ENDPOINT_LINK_PROXY_METHOD_SUBSCRIBE_PARAMS] = { endpoint_link_demarshal_subscribe_params, 0 }, ++ [PW_ENDPOINT_LINK_PROXY_METHOD_ENUM_PARAMS] = { endpoint_link_demarshal_enum_params, 0 }, ++ [PW_ENDPOINT_LINK_PROXY_METHOD_SET_PARAM] = { endpoint_link_demarshal_set_param, PW_PERM_W }, ++ [PW_ENDPOINT_LINK_PROXY_METHOD_REQUEST_STATE] = { endpoint_link_demarshal_request_state, PW_PERM_W }, ++ [PW_ENDPOINT_LINK_PROXY_METHOD_DESTROY] = { endpoint_link_demarshal_destroy, PW_PERM_W }, ++}; ++ ++static const struct pw_protocol_marshal pw_protocol_native_endpoint_link_marshal = { ++ PW_TYPE_INTERFACE_EndpointLink, ++ PW_VERSION_ENDPOINT_LINK_PROXY, ++ PW_ENDPOINT_LINK_PROXY_METHOD_NUM, ++ PW_ENDPOINT_LINK_PROXY_EVENT_NUM, ++ &pw_protocol_native_endpoint_link_method_marshal, ++ &pw_protocol_native_endpoint_link_method_demarshal, ++ &pw_protocol_native_endpoint_link_event_marshal, ++ &pw_protocol_native_endpoint_link_event_demarshal, ++}; ++ ++/*********************************************** ++ * ENDPOINT STREAM ++ ***********************************************/ ++ ++static void endpoint_stream_marshal_info (void *object, ++ const struct pw_endpoint_stream_info *info) ++{ ++ struct pw_resource *resource = object; ++ struct spa_pod_builder *b; ++ ++ b = pw_protocol_native_begin_resource(resource, ++ PW_ENDPOINT_STREAM_PROXY_EVENT_INFO, NULL); ++ ++ marshal_pw_endpoint_stream_info(b, info); ++ ++ pw_protocol_native_end_resource(resource, b); ++} ++ ++static void endpoint_stream_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_STREAM_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_stream_marshal_add_listener(void *object, ++ struct spa_hook *listener, ++ const struct pw_endpoint_stream_proxy_events *events, ++ void *data) ++{ ++ struct pw_proxy *proxy = object; ++ pw_proxy_add_object_listener(proxy, listener, events, data); ++ return 0; ++} ++ ++static int endpoint_stream_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_STREAM_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_stream_marshal_enum_params(void *object, ++ int seq, uint32_t id, ++ uint32_t start, uint32_t num, ++ const struct spa_pod *filter) ++{ ++ struct pw_protocol_native_message *msg; ++ struct pw_proxy *proxy = object; ++ struct spa_pod_builder *b; ++ ++ b = pw_protocol_native_begin_proxy(proxy, ++ PW_ENDPOINT_STREAM_PROXY_METHOD_ENUM_PARAMS, &msg); ++ ++ spa_pod_builder_add_struct(b, ++ SPA_POD_Int(SPA_RESULT_RETURN_ASYNC(msg->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_stream_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_STREAM_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_stream_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_stream_info info = { .props = &props }; ++ ++ spa_pod_parser_init(&prs, msg->data, msg->size); ++ ++ demarshal_pw_endpoint_stream_info(&prs, &f, &info); ++ ++ return pw_proxy_notify(proxy, struct pw_endpoint_stream_proxy_events, ++ info, 0, &info); ++} ++ ++static int endpoint_stream_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(¶m)) < 0) ++ return -EINVAL; ++ ++ return pw_proxy_notify(proxy, struct pw_endpoint_stream_proxy_events, ++ param, 0, seq, id, index, next, param); ++} ++ ++static int endpoint_stream_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_notify(resource, struct pw_endpoint_stream_proxy_methods, ++ subscribe_params, 0, ids, n_ids); ++} ++ ++static int endpoint_stream_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_notify(resource, struct pw_endpoint_stream_proxy_methods, ++ enum_params, 0, seq, id, index, num, filter); ++} ++ ++static int endpoint_stream_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(¶m)) < 0) ++ return -EINVAL; ++ ++ return pw_resource_notify(resource, struct pw_endpoint_stream_proxy_methods, ++ set_param, 0, id, flags, param); ++} ++ ++static const struct pw_endpoint_stream_proxy_events pw_protocol_native_endpoint_stream_event_marshal = { ++ PW_VERSION_ENDPOINT_STREAM_PROXY_EVENTS, ++ .info = endpoint_stream_marshal_info, ++ .param = endpoint_stream_marshal_param, ++}; ++ ++static const struct pw_protocol_native_demarshal ++pw_protocol_native_endpoint_stream_event_demarshal[PW_ENDPOINT_STREAM_PROXY_EVENT_NUM] = ++{ ++ [PW_ENDPOINT_STREAM_PROXY_EVENT_INFO] = { endpoint_stream_demarshal_info, 0 }, ++ [PW_ENDPOINT_STREAM_PROXY_EVENT_PARAM] = { endpoint_stream_demarshal_param, 0 }, ++}; ++ ++static const struct pw_endpoint_stream_proxy_methods pw_protocol_native_endpoint_stream_method_marshal = { ++ PW_VERSION_ENDPOINT_STREAM_PROXY_METHODS, ++ .add_listener = endpoint_stream_marshal_add_listener, ++ .subscribe_params = endpoint_stream_marshal_subscribe_params, ++ .enum_params = endpoint_stream_marshal_enum_params, ++ .set_param = endpoint_stream_marshal_set_param, ++}; ++ ++static const struct pw_protocol_native_demarshal ++pw_protocol_native_endpoint_stream_method_demarshal[PW_ENDPOINT_STREAM_PROXY_METHOD_NUM] = ++{ ++ [PW_ENDPOINT_STREAM_PROXY_METHOD_ADD_LISTENER] = { NULL, 0 }, ++ [PW_ENDPOINT_STREAM_PROXY_METHOD_SUBSCRIBE_PARAMS] = { endpoint_stream_demarshal_subscribe_params, 0 }, ++ [PW_ENDPOINT_STREAM_PROXY_METHOD_ENUM_PARAMS] = { endpoint_stream_demarshal_enum_params, 0 }, ++ [PW_ENDPOINT_STREAM_PROXY_METHOD_SET_PARAM] = { endpoint_stream_demarshal_set_param, PW_PERM_W }, ++}; ++ ++static const struct pw_protocol_marshal pw_protocol_native_endpoint_stream_marshal = { ++ PW_TYPE_INTERFACE_EndpointStream, ++ PW_VERSION_ENDPOINT_STREAM_PROXY, ++ PW_ENDPOINT_STREAM_PROXY_METHOD_NUM, ++ PW_ENDPOINT_STREAM_PROXY_EVENT_NUM, ++ &pw_protocol_native_endpoint_stream_method_marshal, ++ &pw_protocol_native_endpoint_stream_method_demarshal, ++ &pw_protocol_native_endpoint_stream_event_marshal, ++ &pw_protocol_native_endpoint_stream_event_demarshal, ++}; ++ ++/*********************************************** ++ * ENDPOINT ++ ***********************************************/ ++ ++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); ++ ++ marshal_pw_endpoint_info(b, info); ++ ++ pw_protocol_native_end_resource(resource, b); ++} ++ ++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_marshal_add_listener(void *object, ++ struct spa_hook *listener, ++ const struct pw_endpoint_proxy_events *events, ++ void *data) ++{ ++ struct pw_proxy *proxy = object; ++ pw_proxy_add_object_listener(proxy, listener, events, data); ++ return 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_marshal_enum_params(void *object, ++ int seq, uint32_t id, ++ uint32_t start, uint32_t num, ++ const struct spa_pod *filter) ++{ ++ struct pw_protocol_native_message *msg; ++ struct pw_proxy *proxy = object; ++ struct spa_pod_builder *b; ++ ++ b = pw_protocol_native_begin_proxy(proxy, ++ PW_ENDPOINT_PROXY_METHOD_ENUM_PARAMS, &msg); ++ ++ spa_pod_builder_add_struct(b, ++ SPA_POD_Int(SPA_RESULT_RETURN_ASYNC(msg->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_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_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 }; ++ ++ spa_pod_parser_init(&prs, msg->data, msg->size); ++ ++ demarshal_pw_endpoint_info(&prs, &f, &info); ++ ++ return pw_proxy_notify(proxy, struct pw_endpoint_proxy_events, ++ info, 0, &info); ++} ++ ++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(¶m)) < 0) ++ return -EINVAL; ++ ++ return pw_proxy_notify(proxy, struct pw_endpoint_proxy_events, ++ param, 0, seq, id, index, next, param); ++} ++ ++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_notify(resource, struct pw_endpoint_proxy_methods, ++ subscribe_params, 0, ids, n_ids); ++} ++ ++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_notify(resource, struct pw_endpoint_proxy_methods, ++ enum_params, 0, seq, id, index, num, filter); ++} ++ ++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(¶m)) < 0) ++ return -EINVAL; ++ ++ return pw_resource_notify(resource, struct pw_endpoint_proxy_methods, ++ set_param, 0, id, flags, param); ++} ++ ++static const struct pw_endpoint_proxy_events pw_protocol_native_endpoint_event_marshal = { ++ PW_VERSION_ENDPOINT_PROXY_EVENTS, ++ .info = endpoint_marshal_info, ++ .param = endpoint_marshal_param, ++}; ++ ++static const struct pw_protocol_native_demarshal ++pw_protocol_native_endpoint_event_demarshal[PW_ENDPOINT_PROXY_EVENT_NUM] = ++{ ++ [PW_ENDPOINT_PROXY_EVENT_INFO] = { endpoint_demarshal_info, 0 }, ++ [PW_ENDPOINT_PROXY_EVENT_PARAM] = { endpoint_demarshal_param, 0 }, ++}; ++ ++static const struct pw_endpoint_proxy_methods pw_protocol_native_endpoint_method_marshal = { ++ PW_VERSION_ENDPOINT_PROXY_METHODS, ++ .add_listener = endpoint_marshal_add_listener, ++ .subscribe_params = endpoint_marshal_subscribe_params, ++ .enum_params = endpoint_marshal_enum_params, ++ .set_param = endpoint_marshal_set_param, ++}; ++ ++static const struct pw_protocol_native_demarshal ++pw_protocol_native_endpoint_method_demarshal[PW_ENDPOINT_PROXY_METHOD_NUM] = ++{ ++ [PW_ENDPOINT_PROXY_METHOD_ADD_LISTENER] = { NULL, 0 }, ++ [PW_ENDPOINT_PROXY_METHOD_SUBSCRIBE_PARAMS] = { endpoint_demarshal_subscribe_params, 0 }, ++ [PW_ENDPOINT_PROXY_METHOD_ENUM_PARAMS] = { endpoint_demarshal_enum_params, 0 }, ++ [PW_ENDPOINT_PROXY_METHOD_SET_PARAM] = { endpoint_demarshal_set_param, PW_PERM_W }, ++}; ++ ++static const struct pw_protocol_marshal pw_protocol_native_endpoint_marshal = { ++ PW_TYPE_INTERFACE_Endpoint, ++ PW_VERSION_ENDPOINT_PROXY, ++ 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, ++}; ++ ++/*********************************************** ++ * SESSION ++ ***********************************************/ ++ ++static void session_marshal_info (void *object, ++ const struct pw_session_info *info) ++{ ++ struct pw_resource *resource = object; ++ struct spa_pod_builder *b; ++ ++ b = pw_protocol_native_begin_resource(resource, ++ PW_SESSION_PROXY_EVENT_INFO, NULL); ++ ++ marshal_pw_session_info(b, info); ++ ++ pw_protocol_native_end_resource(resource, b); ++} ++ ++static void session_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_SESSION_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 session_marshal_add_listener(void *object, ++ struct spa_hook *listener, ++ const struct pw_session_proxy_events *events, ++ void *data) ++{ ++ struct pw_proxy *proxy = object; ++ pw_proxy_add_object_listener(proxy, listener, events, data); ++ return 0; ++} ++ ++static int session_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_SESSION_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 session_marshal_enum_params(void *object, ++ int seq, uint32_t id, ++ uint32_t start, uint32_t num, ++ const struct spa_pod *filter) ++{ ++ struct pw_protocol_native_message *msg; ++ struct pw_proxy *proxy = object; ++ struct spa_pod_builder *b; ++ ++ b = pw_protocol_native_begin_proxy(proxy, ++ PW_SESSION_PROXY_METHOD_ENUM_PARAMS, &msg); ++ ++ spa_pod_builder_add_struct(b, ++ SPA_POD_Int(SPA_RESULT_RETURN_ASYNC(msg->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 session_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_SESSION_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 session_marshal_create_link(void *object, ++ const struct spa_dict *props) ++{ ++ struct pw_proxy *proxy = object; ++ struct spa_pod_builder *b; ++ ++ b = pw_protocol_native_begin_proxy(proxy, ++ PW_SESSION_PROXY_METHOD_CREATE_LINK, NULL); ++ ++ push_dict(b, props); ++ ++ return pw_protocol_native_end_proxy(proxy, b); ++} ++ ++static int session_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_session_info info = { .props = &props }; ++ ++ spa_pod_parser_init(&prs, msg->data, msg->size); ++ ++ demarshal_pw_session_info(&prs, &f, &info); ++ ++ return pw_proxy_notify(proxy, struct pw_session_proxy_events, ++ info, 0, &info); ++} ++ ++static int session_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(¶m)) < 0) ++ return -EINVAL; ++ ++ return pw_proxy_notify(proxy, struct pw_session_proxy_events, ++ param, 0, seq, id, index, next, param); ++} ++ ++static int session_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_notify(resource, struct pw_session_proxy_methods, ++ subscribe_params, 0, ids, n_ids); ++} ++ ++static int session_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_notify(resource, struct pw_session_proxy_methods, ++ enum_params, 0, seq, id, index, num, filter); ++} ++ ++static int session_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(¶m)) < 0) ++ return -EINVAL; ++ ++ return pw_resource_notify(resource, struct pw_session_proxy_methods, ++ set_param, 0, id, flags, param); ++} ++ ++static int session_demarshal_create_link(void *object, ++ const struct pw_protocol_native_message *msg) ++{ ++ struct pw_resource *resource = object; ++ struct spa_pod_parser prs; ++ struct spa_pod_frame f; ++ struct spa_dict props = SPA_DICT_INIT(NULL, 0); ++ ++ spa_pod_parser_init(&prs, msg->data, msg->size); ++ ++ parse_dict(&prs, &f, &props); ++ ++ return pw_resource_notify(resource, struct pw_session_proxy_methods, ++ create_link, 0, &props); ++} ++ ++static const struct pw_session_proxy_events pw_protocol_native_session_event_marshal = { ++ PW_VERSION_SESSION_PROXY_EVENTS, ++ .info = session_marshal_info, ++ .param = session_marshal_param, ++}; ++ ++static const struct pw_protocol_native_demarshal ++pw_protocol_native_session_event_demarshal[PW_SESSION_PROXY_EVENT_NUM] = ++{ ++ [PW_SESSION_PROXY_EVENT_INFO] = { session_demarshal_info, 0 }, ++ [PW_SESSION_PROXY_EVENT_PARAM] = { session_demarshal_param, 0 }, ++}; ++ ++static const struct pw_session_proxy_methods pw_protocol_native_session_method_marshal = { ++ PW_VERSION_SESSION_PROXY_METHODS, ++ .add_listener = session_marshal_add_listener, ++ .subscribe_params = session_marshal_subscribe_params, ++ .enum_params = session_marshal_enum_params, ++ .set_param = session_marshal_set_param, ++ .create_link = session_marshal_create_link, ++}; ++ ++static const struct pw_protocol_native_demarshal ++pw_protocol_native_session_method_demarshal[PW_SESSION_PROXY_METHOD_NUM] = ++{ ++ [PW_SESSION_PROXY_METHOD_ADD_LISTENER] = { NULL, 0 }, ++ [PW_SESSION_PROXY_METHOD_SUBSCRIBE_PARAMS] = { session_demarshal_subscribe_params, 0 }, ++ [PW_SESSION_PROXY_METHOD_ENUM_PARAMS] = { session_demarshal_enum_params, 0 }, ++ [PW_SESSION_PROXY_METHOD_SET_PARAM] = { session_demarshal_set_param, PW_PERM_W }, ++ [PW_SESSION_PROXY_METHOD_CREATE_LINK] = { session_demarshal_create_link, PW_PERM_W }, ++}; ++ ++static const struct pw_protocol_marshal pw_protocol_native_session_marshal = { ++ PW_TYPE_INTERFACE_Session, ++ PW_VERSION_SESSION_PROXY, ++ PW_SESSION_PROXY_METHOD_NUM, ++ PW_SESSION_PROXY_EVENT_NUM, ++ &pw_protocol_native_session_method_marshal, ++ &pw_protocol_native_session_method_demarshal, ++ &pw_protocol_native_session_event_marshal, ++ &pw_protocol_native_session_event_demarshal, ++}; ++ ++struct pw_protocol *pw_protocol_native_ext_session_manager_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_client_session_marshal); ++ pw_protocol_add_marshal(protocol, &pw_protocol_native_endpoint_link_marshal); ++ pw_protocol_add_marshal(protocol, &pw_protocol_native_endpoint_stream_marshal); ++ pw_protocol_add_marshal(protocol, &pw_protocol_native_endpoint_marshal); ++ pw_protocol_add_marshal(protocol, &pw_protocol_native_session_marshal); ++ ++ return protocol; ++} +diff --git a/src/modules/module-session-manager/session.c b/src/modules/module-session-manager/session.c +new file mode 100644 +index 00000000..226eba4e +--- /dev/null ++++ b/src/modules/module-session-manager/session.c +@@ -0,0 +1,341 @@ ++/* 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 <stdbool.h> ++#include <string.h> ++ ++#include <pipewire/pipewire.h> ++#include <extensions/session-manager.h> ++ ++#include <spa/pod/filter.h> ++ ++#include "session.h" ++#include "client-session.h" ++ ++#include <pipewire/private.h> ++ ++#define NAME "session" ++ ++struct resource_data { ++ struct session *session; ++ struct spa_hook resource_listener; ++ struct spa_hook object_listener; ++ uint32_t n_subscribe_ids; ++ uint32_t subscribe_ids[32]; ++}; ++ ++#define pw_session_resource(r,m,v,...) \ ++ pw_resource_call(r,struct pw_session_proxy_events,m,v,__VA_ARGS__) ++#define pw_session_resource_info(r,...) \ ++ pw_session_resource(r,info,0,__VA_ARGS__) ++#define pw_session_resource_param(r,...) \ ++ pw_session_resource(r,param,0,__VA_ARGS__) ++ ++static int session_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 session *this = data->session; ++ 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(NAME" %p: %d param %u", this, seq, index); ++ ++ pw_session_resource_param(resource, seq, id, index, next, result); ++ ++ if (++count == num) ++ break; ++ } ++ return 0; ++} ++ ++static int session_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(NAME" %p: resource %d subscribe param %u", ++ data->session, resource->id, ids[i]); ++ session_enum_params(resource, 1, ids[i], 0, UINT32_MAX, NULL); ++ } ++ return 0; ++} ++ ++static int session_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 session *this = data->session; ++ ++ pw_client_session_resource_set_param(this->client_sess->resource, ++ id, flags, param); ++ ++ return 0; ++} ++ ++static int session_create_link(void *object, const struct spa_dict *props) ++{ ++ struct pw_resource *resource = object; ++ struct resource_data *data = pw_resource_get_user_data(resource); ++ struct session *this = data->session; ++ ++ pw_client_session_resource_create_link(this->client_sess->resource, ++ props); ++ ++ return 0; ++} ++ ++static const struct pw_session_proxy_methods methods = { ++ PW_VERSION_SESSION_PROXY_METHODS, ++ .subscribe_params = session_subscribe_params, ++ .enum_params = session_enum_params, ++ .set_param = session_set_param, ++ .create_link = session_create_link, ++}; ++ ++static void session_notify_subscribed(struct session *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_session_resource_param(resource, 1, id, ++ index, next, param); ++ } ++ } ++ } ++} ++ ++int session_update(struct session *this, ++ uint32_t change_mask, ++ uint32_t n_params, ++ const struct spa_pod **params, ++ const struct pw_session_info *info) ++{ ++ if (change_mask & PW_CLIENT_SESSION_UPDATE_PARAMS) { ++ uint32_t i; ++ size_t size = n_params * sizeof(struct spa_pod *); ++ ++ pw_log_debug(NAME" %p: update %d params", this, n_params); ++ ++ for (i = 0; i < this->n_params; i++) ++ free(this->params[i]); ++ this->params = realloc(this->params, size); ++ if (size > 0 && !this->params) { ++ this->n_params = 0; ++ goto no_mem; ++ } ++ this->n_params = n_params; ++ ++ for (i = 0; i < this->n_params; i++) { ++ this->params[i] = params[i] ? spa_pod_copy(params[i]) : NULL; ++ session_notify_subscribed(this, i, i+1); ++ } ++ } ++ ++ if (change_mask & PW_CLIENT_SESSION_UPDATE_INFO) { ++ struct pw_resource *resource; ++ ++ if (info->change_mask & PW_SESSION_CHANGE_MASK_PROPS) ++ pw_properties_update(this->props, info->props); ++ ++ if (info->change_mask & PW_SESSION_CHANGE_MASK_PARAMS) { ++ size_t size = info->n_params * sizeof(struct spa_param_info); ++ ++ this->info.params = realloc(this->info.params, size); ++ if (size > 0 && !this->info.params) { ++ this->info.n_params = 0; ++ goto no_mem; ++ } ++ this->info.n_params = info->n_params; ++ ++ memcpy(this->info.params, info->params, size); ++ } ++ ++ this->info.change_mask = info->change_mask; ++ spa_list_for_each(resource, &this->global->resource_list, link) { ++ pw_session_resource_info(resource, &this->info); ++ } ++ this->info.change_mask = 0; ++ } ++ ++ return 0; ++ ++ no_mem: ++ pw_log_error(NAME" can't update: no memory"); ++ pw_resource_error(this->client_sess->resource, -ENOMEM, ++ NAME" can't update: no memory"); ++ return -ENOMEM; ++} ++ ++static void session_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 = session_unbind, ++}; ++ ++static int session_bind(void *_data, struct pw_client *client, ++ uint32_t permissions, uint32_t version, uint32_t id) ++{ ++ struct session *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->session = this; ++ pw_resource_add_listener(resource, &data->resource_listener, ++ &resource_events, resource); ++ pw_resource_add_object_listener(resource, &data->object_listener, ++ &methods, resource); ++ ++ pw_log_debug(NAME" %p: bound to %d", this, resource->id); ++ ++ spa_list_append(&global->resource_list, &resource->link); ++ ++ this->info.change_mask = PW_SESSION_CHANGE_MASK_ALL; ++ pw_session_resource_info(resource, &this->info); ++ this->info.change_mask = 0; ++ ++ return 0; ++ ++ no_mem: ++ pw_log_error(NAME" can't create resource: no memory"); ++ pw_resource_error(this->client_sess->resource, -ENOMEM, ++ NAME" can't create resource: no memory"); ++ return -ENOMEM; ++} ++ ++int session_init(struct session *this, ++ struct client_session *client_sess, ++ struct pw_core *core, ++ struct pw_properties *properties) ++{ ++ const char *keys[] = { ++ PW_KEY_FACTORY_ID, ++ PW_KEY_CLIENT_ID, ++ NULL ++ }; ++ ++ pw_log_debug(NAME" %p: new", this); ++ ++ this->client_sess = client_sess; ++ this->props = properties; ++ ++ properties = pw_properties_new(NULL, NULL); ++ if (!properties) ++ goto no_mem; ++ ++ pw_properties_copy_keys(this->props, properties, keys); ++ ++ this->global = pw_global_new (core, ++ PW_TYPE_INTERFACE_Session, ++ PW_VERSION_SESSION_PROXY, ++ properties, session_bind, this); ++ if (!this->global) ++ goto no_mem; ++ ++ pw_properties_setf(this->props, PW_KEY_SESSION_ID, "%u", this->global->id); ++ ++ this->info.version = PW_VERSION_SESSION_INFO; ++ this->info.id = this->global->id; ++ this->info.props = &this->props->dict; ++ ++ pw_client_session_resource_set_id(client_sess->resource, this->global->id); ++ ++ return pw_global_register(this->global); ++ ++ no_mem: ++ pw_log_error(NAME" - can't create - out of memory"); ++ return -ENOMEM; ++} ++ ++void session_clear(struct session *this) ++{ ++ uint32_t i; ++ ++ pw_log_debug(NAME" %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); ++} +diff --git a/src/modules/module-session-manager/session.h b/src/modules/module-session-manager/session.h +new file mode 100644 +index 00000000..ad0b9b1b +--- /dev/null ++++ b/src/modules/module-session-manager/session.h +@@ -0,0 +1,61 @@ ++/* 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 MODULE_SESSION_MANAGER_SESSION_H ++#define MODULE_SESSION_MANAGER_SESSION_H ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++struct client_session; ++ ++struct session { ++ struct client_session *client_sess; ++ struct pw_global *global; ++ uint32_t n_params; ++ struct spa_pod **params; ++ struct pw_session_info info; ++ struct pw_properties *props; /* wrapper of info.props */ ++}; ++ ++int session_init(struct session *this, ++ struct client_session *client_sess, ++ struct pw_core *core, ++ struct pw_properties *properties); ++ ++void session_clear(struct session *this); ++ ++int session_update(struct session *this, ++ uint32_t change_mask, ++ uint32_t n_params, ++ const struct spa_pod **params, ++ const struct pw_session_info *info); ++ ++#ifdef __cplusplus ++} /* extern "C" */ ++#endif ++ ++#endif /* MODULE_SESSION_MANAGER_SESSION_H */ +diff --git a/src/pipewire/pipewire.c b/src/pipewire/pipewire.c +index a7012ab2..ec5f1f85 100644 +--- a/src/pipewire/pipewire.c ++++ b/src/pipewire/pipewire.c +@@ -575,6 +575,12 @@ 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_ClientEndpoint, SPA_TYPE_Pointer, PW_TYPE_INFO_INTERFACE_BASE "ClientEndpoint", NULL}, ++ { PW_TYPE_INTERFACE_Endpoint, SPA_TYPE_Pointer, PW_TYPE_INFO_INTERFACE_BASE "Endpoint", NULL}, ++ { PW_TYPE_INTERFACE_EndpointStream, SPA_TYPE_Pointer, PW_TYPE_INFO_INTERFACE_BASE "EndpointStream", NULL}, ++ { PW_TYPE_INTERFACE_ClientSession, SPA_TYPE_Pointer, PW_TYPE_INFO_INTERFACE_BASE "ClientSession", NULL}, ++ { PW_TYPE_INTERFACE_Session, SPA_TYPE_Pointer, PW_TYPE_INFO_INTERFACE_BASE "Session", NULL}, ++ { PW_TYPE_INTERFACE_EndpointLink, SPA_TYPE_Pointer, PW_TYPE_INFO_INTERFACE_BASE "EndpointLink", 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..6b1b8b50 100644 +--- a/src/pipewire/type.h ++++ b/src/pipewire/type.h +@@ -48,7 +48,12 @@ enum { + /* extensions */ + PW_TYPE_INTERFACE_EXTENSIONS = PW_TYPE_INTERFACE_START + 0x1000, + PW_TYPE_INTERFACE_ClientNode, +- ++ PW_TYPE_INTERFACE_ClientEndpoint, ++ PW_TYPE_INTERFACE_Endpoint, ++ PW_TYPE_INTERFACE_EndpointStream, ++ PW_TYPE_INTERFACE_ClientSession, ++ PW_TYPE_INTERFACE_Session, ++ PW_TYPE_INTERFACE_EndpointLink, + }; + + #define PW_TYPE_INFO_BASE "PipeWire:" +-- +2.23.0 + diff --git a/meta-pipewire/recipes-multimedia/pipewire/pipewire/0003-pipewire-cli-add-support-for-printing-endpoint-info-.patch b/meta-pipewire/recipes-multimedia/pipewire/pipewire/0013-pipewire-cli-add-support-for-printing-endpoint-info-.patch index a709abdf..8c71afb3 100644 --- a/meta-pipewire/recipes-multimedia/pipewire/pipewire/0003-pipewire-cli-add-support-for-printing-endpoint-info-.patch +++ b/meta-pipewire/recipes-multimedia/pipewire/pipewire/0013-pipewire-cli-add-support-for-printing-endpoint-info-.patch @@ -1,37 +1,31 @@ -From 0e9fe3cfb19c751655f16ce4b8c6100269f23ff2 Mon Sep 17 00:00:00 2001 +From 1249a42d1380945fd8dc7924c1ac912570bef501 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] 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 - Upstream-Status: Pending --- - src/tools/pipewire-cli.c | 78 +++++++++++++++++++++++++++++++++++++++- - 1 file changed, 77 insertions(+), 1 deletion(-) + src/tools/pipewire-cli.c | 107 ++++++++++++++++++++++++++++++++++++++- + 1 file changed, 106 insertions(+), 1 deletion(-) diff --git a/src/tools/pipewire-cli.c b/src/tools/pipewire-cli.c -index 521739f6..9511db82 100644 +index 6110d170..0dbc8368 100644 --- a/src/tools/pipewire-cli.c +++ b/src/tools/pipewire-cli.c -@@ -38,6 +38,8 @@ +@@ -37,6 +37,8 @@ #include <pipewire/type.h> #include <pipewire/permission.h> -+#include <extensions/endpoint.h> ++#include <extensions/session-manager.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 +@@ -174,8 +176,10 @@ 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), @@ -39,39 +33,55 @@ index 521739f6..9511db82 100644 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) +@@ -652,6 +656,40 @@ 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; ++ const char *direction; + + info_global(pd); -+ print_properties(info->props, MARK_CHANGE(1), true); -+ print_params(info->params, info->n_params, MARK_CHANGE(0), true); ++ fprintf(stdout, "\tname: %s\n", info->name); ++ fprintf(stdout, "\tmedia-class: %s\n", info->media_class); ++ switch(info->direction) { ++ case PW_ENDPOINT_DIRECTION_SINK_INPUT: ++ direction = "sink-input"; ++ break; ++ case PW_ENDPOINT_DIRECTION_SOURCE_OUTPUT: ++ direction = "source-output"; ++ break; ++ case PW_ENDPOINT_DIRECTION_SOURCE: ++ direction = "source"; ++ break; ++ case PW_ENDPOINT_DIRECTION_SINK: ++ direction = "sink"; ++ break; ++ default: ++ direction = "invalid"; ++ break; ++ } ++ fprintf(stdout, "\tdirection: %s\n", direction); ++ fprintf(stdout, "\tflags: 0x%x\n", info->flags); ++ fprintf(stdout, "%c\tstreams: %u\n", MARK_CHANGE(0), info->n_streams); ++ fprintf(stdout, "%c\tsession: %u\n", MARK_CHANGE(0), info->session_id); ++ print_properties(info->props, MARK_CHANGE(2), true); ++ print_params(info->params, info->n_params, MARK_CHANGE(3), 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 = { +@@ -853,6 +891,63 @@ static const struct pw_device_proxy_events device_events = { .param = event_param }; +static void endpoint_info_free(struct pw_endpoint_info *info) +{ ++ free(info->name); ++ free(info->media_class); + free(info->params); + if (info->props) + pw_properties_free ((struct pw_properties *)info->props); @@ -88,7 +98,15 @@ index 521739f6..9511db82 100644 + if (!info) { + info = pd->info = calloc(1, sizeof(*info)); + info->id = update->id; ++ info->name = strdup(update->name); ++ info->media_class = strdup(update->media_class); ++ info->direction = update->direction; ++ info->flags = update->flags; + } ++ if (update->change_mask & PW_ENDPOINT_CHANGE_MASK_STREAMS) ++ info->n_streams = update->n_streams; ++ if (update->change_mask & PW_ENDPOINT_CHANGE_MASK_SESSION) ++ info->session_id = update->session_id; + if (update->change_mask & PW_ENDPOINT_CHANGE_MASK_PARAMS) { + info->n_params = update->n_params; + free(info->params); @@ -120,20 +138,20 @@ index 521739f6..9511db82 100644 static void destroy_proxy (void *data) { -@@ -928,6 +994,12 @@ static bool bind_global(struct remote_data *rd, struct global *global, char **er +@@ -939,6 +1034,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; ++ client_version = PW_VERSION_ENDPOINT_PROXY; + 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 +@@ -1213,6 +1314,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; @@ -145,5 +163,5 @@ index 521739f6..9511db82 100644 asprintf(error, "enum-params not implemented on object %d", atoi(a[0])); return false; -- -2.20.1 +2.23.0 diff --git a/meta-pipewire/recipes-multimedia/pipewire/pipewire/0014-daemon-config-remote-load-module-session-manager-by-.patch b/meta-pipewire/recipes-multimedia/pipewire/pipewire/0014-daemon-config-remote-load-module-session-manager-by-.patch new file mode 100644 index 00000000..30066451 --- /dev/null +++ b/meta-pipewire/recipes-multimedia/pipewire/pipewire/0014-daemon-config-remote-load-module-session-manager-by-.patch @@ -0,0 +1,37 @@ +From a7379880b3cb14ce81cbb3475b1b251df60c86f8 Mon Sep 17 00:00:00 2001 +From: George Kiagiadakis <george.kiagiadakis@collabora.com> +Date: Fri, 9 Aug 2019 13:26:15 +0300 +Subject: [PATCH] daemon config & remote: load module-session-manager by + default + +Upstream-Status: Pending +--- + src/daemon/pipewire.conf.in | 1 + + src/pipewire/remote.c | 1 + + 2 files changed, 2 insertions(+) + +diff --git a/src/daemon/pipewire.conf.in b/src/daemon/pipewire.conf.in +index 079ec730..19c898b9 100644 +--- a/src/daemon/pipewire.conf.in ++++ b/src/daemon/pipewire.conf.in +@@ -21,4 +21,5 @@ load-module libpipewire-module-client-device + load-module libpipewire-module-access + load-module libpipewire-module-adapter + load-module libpipewire-module-link-factory ++load-module libpipewire-module-session-manager + exec build/src/examples/media-session +diff --git a/src/pipewire/remote.c b/src/pipewire/remote.c +index 2b545f79..62841e58 100644 +--- a/src/pipewire/remote.c ++++ b/src/pipewire/remote.c +@@ -242,6 +242,7 @@ struct pw_remote *pw_remote_new(struct pw_core *core, + pw_module_load(core, "libpipewire-module-rtkit", NULL, NULL); + pw_module_load(core, "libpipewire-module-client-node", NULL, NULL); + pw_module_load(core, "libpipewire-module-adapter", NULL, NULL); ++ pw_module_load(core, "libpipewire-module-session-manager", NULL, NULL); + + spa_list_append(&core->remote_list, &this->link); + +-- +2.23.0 + diff --git a/meta-pipewire/recipes-multimedia/pipewire/pipewire/0015-audioconvert-fmtconvert-assume-F32-on-the-other-port.patch b/meta-pipewire/recipes-multimedia/pipewire/pipewire/0015-audioconvert-fmtconvert-assume-F32-on-the-other-port.patch deleted file mode 100644 index ed3c1b06..00000000 --- a/meta-pipewire/recipes-multimedia/pipewire/pipewire/0015-audioconvert-fmtconvert-assume-F32-on-the-other-port.patch +++ /dev/null @@ -1,44 +0,0 @@ -From aa5de0cfc31df9cd8fb6d24367d2852dbbc8dcb9 Mon Sep 17 00:00:00 2001 -From: George Kiagiadakis <george.kiagiadakis@collabora.com> -Date: Mon, 29 Jul 2019 16:12:45 +0300 -Subject: [PATCH] audioconvert/fmtconvert: assume F32 on the other port when - listing formats - -This allows picking F32LE as the default format on links that have -no restriction and it avoids failing negotiation when the restricted -end cannot handle S16/F32/F32P - -For instance this pipeline would previously fail: - - audio-dsp mode=merge ! audio-dsp mode=convert ! alsa-sink -old negotiation: S16LE S24_32LE -new negotiation: F32LE S24_32LE - -The link between the audio-dsp nodes has no restriction, so previously -it would negotiate S16LE, which would then fail to negotiate with alsa-sink -because fmtconvert does not know how to convert S16LE to S24_32LE directly. - -With this change, the middle link negotiates to F32LE, which can be -converted to anything. - -Upstream-Status: Submitted [https://github.com/PipeWire/pipewire/pull/169] ---- - spa/plugins/audioconvert/fmtconvert.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/spa/plugins/audioconvert/fmtconvert.c b/spa/plugins/audioconvert/fmtconvert.c -index df4ffb6b..a330f68f 100644 ---- a/spa/plugins/audioconvert/fmtconvert.c -+++ b/spa/plugins/audioconvert/fmtconvert.c -@@ -338,7 +338,7 @@ static int port_enum_formats(struct spa_node *node, - if (other->have_format) - info = other->format; - else -- info.info.raw.format = SPA_AUDIO_FORMAT_S16; -+ info.info.raw.format = SPA_AUDIO_FORMAT_F32; - - if (info.info.raw.format == SPA_AUDIO_FORMAT_F32P || - info.info.raw.format == SPA_AUDIO_FORMAT_F32) { --- -2.20.1 - diff --git a/meta-pipewire/recipes-multimedia/pipewire/pipewire/0016-a2dpsink-fix-infinite-loop-when-buffer-could-not-be-.patch b/meta-pipewire/recipes-multimedia/pipewire/pipewire/0016-a2dpsink-fix-infinite-loop-when-buffer-could-not-be-.patch deleted file mode 100644 index d747a7a9..00000000 --- a/meta-pipewire/recipes-multimedia/pipewire/pipewire/0016-a2dpsink-fix-infinite-loop-when-buffer-could-not-be-.patch +++ /dev/null @@ -1,32 +0,0 @@ -From c186e40905f78f41cbc015da0e204735a0398450 Mon Sep 17 00:00:00 2001 -From: Julian Bouzas <julian.bouzas@collabora.com> -Date: Fri, 19 Jul 2019 08:38:21 -0400 -Subject: [PATCH] a2dpsink: fix infinite loop when buffer could not be encoded - -Upstream-Status: Backport [4b202b965665bbcb55194b4ab827984e5804e3e0] ---- - spa/plugins/bluez5/a2dp-sink.c | 7 ++++++- - 1 file changed, 6 insertions(+), 1 deletion(-) - -diff --git a/spa/plugins/bluez5/a2dp-sink.c b/spa/plugins/bluez5/a2dp-sink.c -index 731577e5..d6d9e7d6 100644 ---- a/spa/plugins/bluez5/a2dp-sink.c -+++ b/spa/plugins/bluez5/a2dp-sink.c -@@ -558,8 +558,13 @@ static int flush_data(struct impl *this, uint64_t now_time) - n_bytes = add_data(this, src + offs, l0); - if (n_bytes > 0 && l1 > 0) - n_bytes += add_data(this, src, l1); -- if (n_bytes <= 0) -+ if (n_bytes <= 0) { -+ spa_list_remove(&b->link); -+ b->outstanding = true; -+ spa_node_call_reuse_buffer(&this->callbacks, 0, b->id); -+ port->ready_offset = 0; - break; -+ } - - n_frames = n_bytes / port->frame_size; - --- -2.23.0.rc1 - diff --git a/meta-pipewire/recipes-multimedia/pipewire/pipewire/0017-bluez5-add-sco-sink-and-sco-src-nodes.patch b/meta-pipewire/recipes-multimedia/pipewire/pipewire/0017-bluez5-add-sco-sink-and-sco-src-nodes.patch deleted file mode 100644 index 389ebbda..00000000 --- a/meta-pipewire/recipes-multimedia/pipewire/pipewire/0017-bluez5-add-sco-sink-and-sco-src-nodes.patch +++ /dev/null @@ -1,2620 +0,0 @@ -From c1d18d46b48d056b6458ec6ac1075cb74411145a Mon Sep 17 00:00:00 2001 -From: Julian Bouzas <julian.bouzas@collabora.com> -Date: Tue, 23 Jul 2019 12:52:46 -0400 -Subject: [PATCH] bluez5: add sco-sink and sco-src nodes - -Upstream-Status: Backport [de031b42b1d5e89dfb23d39317955c4b56f17c1b] ---- - spa/plugins/bluez5/bluez5-device.c | 140 +--- - spa/plugins/bluez5/bluez5-monitor.c | 24 +- - spa/plugins/bluez5/meson.build | 2 + - spa/plugins/bluez5/sco-sink.c | 1200 +++++++++++++++++++++++++++ - spa/plugins/bluez5/sco-source.c | 1136 +++++++++++++++++++++++++ - 5 files changed, 2396 insertions(+), 106 deletions(-) - create mode 100644 spa/plugins/bluez5/sco-sink.c - create mode 100644 spa/plugins/bluez5/sco-source.c - -diff --git a/spa/plugins/bluez5/bluez5-device.c b/spa/plugins/bluez5/bluez5-device.c -index d6ea467e..40a340c9 100644 ---- a/spa/plugins/bluez5/bluez5-device.c -+++ b/spa/plugins/bluez5/bluez5-device.c -@@ -44,6 +44,8 @@ - - extern const struct spa_handle_factory spa_a2dp_source_factory; - extern const struct spa_handle_factory spa_a2dp_sink_factory; -+extern const struct spa_handle_factory spa_sco_sink_factory; -+extern const struct spa_handle_factory spa_sco_source_factory; - - static const char default_device[] = ""; - -@@ -68,117 +70,61 @@ struct impl { - struct props props; - - struct spa_bt_device *bt_dev; -+ -+ uint32_t next_id; - }; - --static int emit_source_node(struct impl *this) -+static void emit_node (struct impl *this, struct spa_bt_transport *t, const struct spa_handle_factory *factory) - { -- struct spa_dict_item items[1]; -- struct spa_bt_transport *t; -- struct spa_bt_device *device = this->bt_dev; -- enum spa_bt_profile profile = SPA_BT_PROFILE_NULL; -- -- if (device->connected_profiles & SPA_BT_PROFILE_A2DP_SOURCE) { -- spa_log_info(this->log, "A2DP (source) profile found"); -- profile = SPA_BT_PROFILE_A2DP_SOURCE; -- } else if (device->connected_profiles & SPA_BT_PROFILE_HSP_HS) { -- spa_log_info(this->log, "HSP (source) profile found (Not implemented yet)"); -- profile = SPA_BT_PROFILE_HSP_HS; -- return -ENODEV; -- } else if (device->connected_profiles & SPA_BT_PROFILE_HFP_HF) { -- spa_log_info(this->log, "HFP (source) profile found (Not implemented yet)"); -- profile = SPA_BT_PROFILE_HFP_HF; -- return -ENODEV; -- } -- -- /* Return if no profiles are connected */ -- if (profile == SPA_BT_PROFILE_NULL) -- return -ENODEV; -- -- spa_list_for_each(t, &device->transport_list, device_link) { -- if (t->profile == profile) { -- struct spa_device_object_info info; -- char transport[16]; -- -- snprintf(transport, 16, "%p", t); -- items[0] = SPA_DICT_ITEM_INIT("bluez5.transport", transport); -- -- spa_bt_transport_acquire(t, true); -- -- info = SPA_DEVICE_OBJECT_INFO_INIT(); -- info.type = SPA_TYPE_INTERFACE_Node; -- info.factory = &spa_a2dp_source_factory; -- info.change_mask = SPA_DEVICE_OBJECT_CHANGE_MASK_PROPS; -- info.props = &SPA_DICT_INIT_ARRAY(items); -- -- spa_device_emit_object_info(&this->hooks, 0, &info); -- break; -- } -- } -- -- spa_log_info (this->log, "bluez5 source nodes emitted"); -- return 0; -+ struct spa_device_object_info info; -+ struct spa_dict_item items[1]; -+ char transport[16]; -+ -+ /* Set the info */ -+ info = SPA_DEVICE_OBJECT_INFO_INIT(); -+ info.type = SPA_TYPE_INTERFACE_Node; -+ info.factory = factory; -+ info.change_mask = SPA_DEVICE_OBJECT_CHANGE_MASK_PROPS; -+ -+ /* Pass the transport pointer as a property */ -+ snprintf(transport, 16, "%p", t); -+ items[0] = SPA_DICT_ITEM_INIT("bluez5.transport", transport); -+ info.props = &SPA_DICT_INIT_ARRAY(items); -+ -+ /* Emit the node */ -+ spa_device_emit_object_info(&this->hooks, this->next_id++, &info); - } - --static int emit_sink_node(struct impl *this) -+static int emit_nodes(struct impl *this) - { -- struct spa_dict_item items[1]; -- struct spa_bt_transport *t; - struct spa_bt_device *device = this->bt_dev; -- enum spa_bt_profile profile = SPA_BT_PROFILE_NULL; -- -- if (device->connected_profiles & SPA_BT_PROFILE_A2DP_SINK) { -- spa_log_info(this->log, "A2DP (sink) profile found"); -- profile = SPA_BT_PROFILE_A2DP_SINK; -- } else if (device->connected_profiles & SPA_BT_PROFILE_HSP_AG) { -- spa_log_info(this->log, "HSP (sink) profile found (Not implemented yet)"); -- profile = SPA_BT_PROFILE_HSP_AG; -- return -ENODEV; -- } else if (device->connected_profiles & SPA_BT_PROFILE_HFP_AG) { -- spa_log_info(this->log, "HFP (sink) profile found (Not implemented yet)"); -- profile = SPA_BT_PROFILE_HFP_AG; -- return -ENODEV; -- } -- -- /* Return if no profiles are connected */ -- if (profile == SPA_BT_PROFILE_NULL) -- return -ENODEV; -+ struct spa_bt_transport *t; - - spa_list_for_each(t, &device->transport_list, device_link) { -- if (t->profile == profile) { -- struct spa_device_object_info info; -- char transport[16]; -- -- snprintf(transport, 16, "%p", t); -- items[0] = SPA_DICT_ITEM_INIT("bluez5.transport", transport); -- -- info = SPA_DEVICE_OBJECT_INFO_INIT(); -- info.type = SPA_TYPE_INTERFACE_Node; -- info.factory = &spa_a2dp_sink_factory; -- info.change_mask = SPA_DEVICE_OBJECT_CHANGE_MASK_PROPS; -- info.props = &SPA_DICT_INIT_ARRAY(items); -- -- spa_device_emit_object_info(&this->hooks, 0, &info); -- break; -+ if (t->profile & device->connected_profiles) { -+ switch (t->profile) { -+ case SPA_BT_PROFILE_A2DP_SOURCE: -+ emit_node (this, t, &spa_a2dp_source_factory); -+ break; -+ case SPA_BT_PROFILE_A2DP_SINK: -+ emit_node (this, t, &spa_a2dp_sink_factory); -+ break; -+ case SPA_BT_PROFILE_HSP_HS: -+ case SPA_BT_PROFILE_HSP_AG: -+ case SPA_BT_PROFILE_HFP_HF: -+ case SPA_BT_PROFILE_HFP_AG: -+ emit_node (this, t, &spa_sco_source_factory); -+ emit_node (this, t, &spa_sco_sink_factory); -+ break; -+ default: -+ return -EINVAL; -+ } - } - } - -- spa_log_info(this->log, "bluez5 sink nodes emitted"); - return 0; - } - --static int emit_nodes(struct impl *this) --{ -- int sink, src; -- -- sink = emit_sink_node(this); -- src = emit_source_node(this); -- -- if (sink == -ENODEV && src == -ENODEV) -- spa_log_warn(this->log, "no profile available"); -- -- return SPA_MAX(sink, src); --} -- - static const struct spa_dict_item info_items[] = { - { "media.class", "Audio/Device" }, - }; -@@ -314,6 +260,8 @@ impl_init(const struct spa_handle_factory *factory, - - reset_props(&this->props); - -+ this->next_id = 0; -+ - return 0; - } - -diff --git a/spa/plugins/bluez5/bluez5-monitor.c b/spa/plugins/bluez5/bluez5-monitor.c -index 5b8ff495..2a243715 100644 ---- a/spa/plugins/bluez5/bluez5-monitor.c -+++ b/spa/plugins/bluez5/bluez5-monitor.c -@@ -1392,7 +1392,10 @@ static void rfcomm_event(struct spa_source *source) - { - struct spa_bt_transport *t = source->data; - struct spa_bt_monitor *monitor = t->monitor; -+ char buf[512]; -+ ssize_t len; - -+ /* Check for errors */ - if (source->rmask & (SPA_IO_HUP | SPA_IO_ERR)) { - spa_log_info(monitor->log, "lost RFCOMM connection."); - if (source->loop) -@@ -1400,20 +1403,19 @@ static void rfcomm_event(struct spa_source *source) - goto fail; - } - -+ /* Read the command */ -+ len = read(source->fd, buf, 511); -+ if (len < 0) { -+ spa_log_error(monitor->log, "RFCOMM read error: %s", strerror(errno)); -+ goto fail; -+ } -+ buf[len] = 0; -+ printf ("RFCOMM AT COMMAND: %s\n", buf); -+ - if (source->rmask & SPA_IO_IN) { -- char buf[512]; -- ssize_t len; - int gain, dummy; - bool do_reply = false; - -- len = read(source->fd, buf, 511); -- if (len < 0) { -- spa_log_error(monitor->log, "RFCOMM read error: %s", strerror(errno)); -- goto fail; -- } -- buf[len] = 0; -- spa_log_debug(monitor->log, "RFCOMM << %s", buf); -- - /* There are only four HSP AT commands: - * AT+VGS=value: value between 0 and 15, sent by the HS to AG to set the speaker gain. - * +VGS=value is sent by AG to HS as a response to an AT+VGS command or when the gain -@@ -1669,12 +1671,14 @@ static int sco_destroy_cb(void *data) - spa_loop_remove_source(td->sco.loop, &td->sco); - shutdown(td->sco.fd, SHUT_RDWR); - close (td->sco.fd); -+ td->sco.fd = -1; - } - if (td->rfcomm.data) { - if (td->rfcomm.loop) - spa_loop_remove_source(td->rfcomm.loop, &td->rfcomm); - shutdown(td->rfcomm.fd, SHUT_RDWR); - close (td->rfcomm.fd); -+ td->rfcomm.fd = -1; - } - return 0; - } -diff --git a/spa/plugins/bluez5/meson.build b/spa/plugins/bluez5/meson.build -index 5fb285ec..ddcc74fa 100644 ---- a/spa/plugins/bluez5/meson.build -+++ b/spa/plugins/bluez5/meson.build -@@ -3,6 +3,8 @@ bluez5_sources = ['plugin.c', - 'a2dp-codecs.c', - 'a2dp-sink.c', - 'a2dp-source.c', -+ 'sco-sink.c', -+ 'sco-source.c', - 'bluez5-device.c', - 'bluez5-monitor.c'] - -diff --git a/spa/plugins/bluez5/sco-sink.c b/spa/plugins/bluez5/sco-sink.c -new file mode 100644 -index 00000000..52307e10 ---- /dev/null -+++ b/spa/plugins/bluez5/sco-sink.c -@@ -0,0 +1,1200 @@ -+/* Spa SCO Sink -+ * -+ * Copyright © 2018 Wim Taymans -+ * Copyright © 2019 Collabora Ltd. -+ * -+ * 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 <unistd.h> -+#include <stddef.h> -+#include <stdio.h> -+#include <sys/timerfd.h> -+#include <arpa/inet.h> -+#include <sys/ioctl.h> -+ -+#include <spa/support/loop.h> -+#include <spa/support/log.h> -+#include <spa/utils/list.h> -+ -+#include <spa/node/node.h> -+#include <spa/node/io.h> -+#include <spa/param/param.h> -+#include <spa/param/audio/format.h> -+#include <spa/param/audio/format-utils.h> -+#include <spa/pod/filter.h> -+ -+#include <sbc/sbc.h> -+ -+#include "defs.h" -+ -+struct props { -+ uint32_t min_latency; -+ uint32_t max_latency; -+}; -+ -+#define MAX_BUFFERS 32 -+ -+struct buffer { -+ uint32_t id; -+ unsigned int outstanding:1; -+ struct spa_buffer *buf; -+ struct spa_meta_header *h; -+ struct spa_list link; -+}; -+ -+struct port { -+ struct spa_audio_info current_format; -+ int frame_size; -+ unsigned int have_format:1; -+ -+ uint64_t info_all; -+ struct spa_port_info info; -+ struct spa_io_buffers *io; -+ struct spa_param_info params[8]; -+ -+ struct buffer buffers[MAX_BUFFERS]; -+ uint32_t n_buffers; -+ -+ struct spa_list free; -+ struct spa_list ready; -+}; -+ -+struct impl { -+ struct spa_handle handle; -+ struct spa_node node; -+ -+ /* Support */ -+ struct spa_log *log; -+ struct spa_loop *main_loop; -+ struct spa_loop *data_loop; -+ -+ /* Hooks and callbacks */ -+ struct spa_hook_list hooks; -+ struct spa_callbacks callbacks; -+ -+ /* Info */ -+ uint64_t info_all; -+ struct spa_node_info info; -+ struct spa_param_info params[8]; -+ struct props props; -+ -+ /* Transport */ -+ struct spa_bt_transport *transport; -+ struct spa_hook transport_listener; -+ int sock_fd; -+ -+ /* Port */ -+ struct port port; -+ -+ /* Flags */ -+ unsigned int started:1; -+ unsigned int slaved:1; -+ -+ /* Sources */ -+ struct spa_source source; -+ struct spa_source flush_source; -+ -+ /* Timer */ -+ int timerfd; -+ struct timespec now; -+ struct spa_io_clock *clock; -+ struct spa_io_position *position; -+ int threshold; -+ -+ /* Times */ -+ uint64_t start_time; -+ -+ /* Counts */ -+ uint64_t sample_count; -+}; -+ -+#define NAME "sco-sink" -+ -+#define CHECK_PORT(this,d,p) ((d) == SPA_DIRECTION_INPUT && (p) == 0) -+ -+static const uint32_t default_min_latency = 128; -+static const uint32_t default_max_latency = 1024; -+ -+static void reset_props(struct props *props) -+{ -+ props->min_latency = default_min_latency; -+ props->max_latency = default_max_latency; -+} -+ -+static int impl_node_enum_params(struct spa_node *node, int seq, -+ uint32_t id, uint32_t start, uint32_t num, -+ const struct spa_pod *filter) -+{ -+ struct impl *this; -+ struct spa_pod *param; -+ struct spa_pod_builder b = { 0 }; -+ uint8_t buffer[1024]; -+ struct spa_result_node_params result; -+ uint32_t count = 0; -+ -+ spa_return_val_if_fail(node != NULL, -EINVAL); -+ spa_return_val_if_fail(num != 0, -EINVAL); -+ -+ this = SPA_CONTAINER_OF(node, struct impl, node); -+ -+ result.id = id; -+ result.next = start; -+ next: -+ result.index = result.next++; -+ -+ spa_pod_builder_init(&b, buffer, sizeof(buffer)); -+ -+ switch (id) { -+ case SPA_PARAM_PropInfo: -+ { -+ struct props *p = &this->props; -+ -+ switch (result.index) { -+ case 0: -+ param = spa_pod_builder_add_object(&b, -+ SPA_TYPE_OBJECT_PropInfo, id, -+ SPA_PROP_INFO_id, SPA_POD_Id(SPA_PROP_minLatency), -+ SPA_PROP_INFO_name, SPA_POD_String("The minimum latency"), -+ SPA_PROP_INFO_type, SPA_POD_CHOICE_RANGE_Int(p->min_latency, 1, INT32_MAX)); -+ break; -+ case 1: -+ param = spa_pod_builder_add_object(&b, -+ SPA_TYPE_OBJECT_PropInfo, id, -+ SPA_PROP_INFO_id, SPA_POD_Id(SPA_PROP_maxLatency), -+ SPA_PROP_INFO_name, SPA_POD_String("The maximum latency"), -+ SPA_PROP_INFO_type, SPA_POD_CHOICE_RANGE_Int(p->max_latency, 1, INT32_MAX)); -+ break; -+ default: -+ return 0; -+ } -+ break; -+ } -+ case SPA_PARAM_Props: -+ { -+ struct props *p = &this->props; -+ -+ switch (result.index) { -+ case 0: -+ param = spa_pod_builder_add_object(&b, -+ SPA_TYPE_OBJECT_Props, id, -+ SPA_PROP_minLatency, SPA_POD_Int(p->min_latency), -+ SPA_PROP_maxLatency, SPA_POD_Int(p->max_latency)); -+ break; -+ default: -+ return 0; -+ } -+ break; -+ } -+ default: -+ return -ENOENT; -+ } -+ -+ if (spa_pod_filter(&b, &result.param, param, filter) < 0) -+ goto next; -+ -+ spa_node_emit_result(&this->hooks, seq, 0, &result); -+ -+ if (++count != num) -+ goto next; -+ -+ return 0; -+} -+ -+static void set_timeout(struct impl *this, time_t sec, long nsec) -+{ -+ struct itimerspec ts; -+ -+ ts.it_value.tv_sec = sec; -+ ts.it_value.tv_nsec = nsec; -+ ts.it_interval.tv_sec = 0; -+ ts.it_interval.tv_nsec = 0; -+ -+ timerfd_settime(this->timerfd, TFD_TIMER_ABSTIME, &ts, NULL); -+ this->source.mask = SPA_IO_IN; -+ spa_loop_update_source(this->data_loop, &this->source); -+} -+ -+static void reset_timeout(struct impl *this) -+{ -+ set_timeout(this, 0, this->slaved ? 0 : 1); -+} -+ -+ -+static void set_next_timeout(struct impl *this, uint64_t now_time) -+{ -+ struct port *port = &this->port; -+ -+ /* Set the next timeout if not slaved, otherwise reset values */ -+ if (!this->slaved) { -+ /* Get the elapsed time */ -+ const uint64_t elapsed_time = now_time - this->start_time; -+ -+ /* Get the elapsed samples */ -+ const uint64_t elapsed_samples = elapsed_time * port->current_format.info.raw.rate / SPA_NSEC_PER_SEC; -+ -+ /* Get the queued samples (processed - elapsed) */ -+ const uint64_t queued_samples = this->sample_count - elapsed_samples; -+ -+ /* Get the queued time */ -+ const uint64_t queued_time = (queued_samples * SPA_NSEC_PER_SEC) / port->current_format.info.raw.rate; -+ -+ /* Get the next time */ -+ const uint64_t next_time = now_time + queued_time; -+ -+ /* Set the next timeout */ -+ set_timeout (this, next_time / SPA_NSEC_PER_SEC, next_time % SPA_NSEC_PER_SEC); -+ } else { -+ this->start_time = now_time; -+ this->sample_count = 0; -+ } -+} -+ -+static int do_reslave(struct spa_loop *loop, -+ bool async, -+ uint32_t seq, -+ const void *data, -+ size_t size, -+ void *user_data) -+{ -+ struct impl *this = user_data; -+ reset_timeout(this); -+ return 0; -+} -+ -+static inline bool is_slaved(struct impl *this) -+{ -+ return this->position && this->clock && this->position->clock.id != this->clock->id; -+} -+ -+static int impl_node_set_io(struct spa_node *node, uint32_t id, void *data, size_t size) -+{ -+ struct impl *this; -+ bool slaved; -+ -+ spa_return_val_if_fail(node != NULL, -EINVAL); -+ -+ this = SPA_CONTAINER_OF(node, struct impl, node); -+ -+ switch (id) { -+ case SPA_IO_Clock: -+ this->clock = data; -+ break; -+ case SPA_IO_Position: -+ this->position = data; -+ break; -+ default: -+ return -ENOENT; -+ } -+ -+ slaved = is_slaved(this); -+ if (this->started && slaved != this->slaved) { -+ spa_log_debug(this->log, "sco-sink %p: reslave %d->%d", this, this->slaved, slaved); -+ this->slaved = slaved; -+ spa_loop_invoke(this->data_loop, do_reslave, 0, NULL, 0, true, this); -+ } -+ return 0; -+} -+ -+static int impl_node_set_param(struct spa_node *node, uint32_t id, uint32_t flags, -+ const struct spa_pod *param) -+{ -+ struct impl *this; -+ -+ spa_return_val_if_fail(node != NULL, -EINVAL); -+ -+ this = SPA_CONTAINER_OF(node, struct impl, node); -+ -+ switch (id) { -+ case SPA_PARAM_Props: -+ { -+ struct props *p = &this->props; -+ -+ if (param == NULL) { -+ reset_props(p); -+ return 0; -+ } -+ spa_pod_parse_object(param, -+ SPA_TYPE_OBJECT_Props, NULL, -+ SPA_PROP_minLatency, SPA_POD_OPT_Int(&p->min_latency), -+ SPA_PROP_maxLatency, SPA_POD_OPT_Int(&p->max_latency)); -+ break; -+ } -+ default: -+ return -ENOENT; -+ } -+ -+ return 0; -+} -+ -+static bool write_data(struct impl *this, uint8_t *data, uint32_t size, uint32_t *total_written) -+{ -+ uint32_t local_total_written = 0; -+ const uint32_t mtu_size = this->transport->write_mtu; -+ -+ /* TODO: For now we assume the size is always a mutliple of mtu_size */ -+ while (local_total_written < (size - mtu_size)) { -+ const int bytes_written = write(this->sock_fd, data, mtu_size); -+ if (bytes_written < 0) { -+ spa_log_warn(this->log, "error writting data: %s", strerror(errno)); -+ return false; -+ } -+ -+ data += bytes_written; -+ local_total_written += bytes_written; -+ } -+ -+ if (total_written) -+ *total_written = local_total_written; -+ return true; -+} -+ -+static int render_buffers(struct impl *this, uint64_t now_time) -+{ -+ struct port *port = &this->port; -+ -+ /* Render the buffer */ -+ while (!spa_list_is_empty(&port->ready)) { -+ uint8_t *src; -+ struct buffer *b; -+ struct spa_data *d; -+ uint32_t offset, size; -+ uint32_t total_written = 0; -+ -+ /* Get the buffer and datas */ -+ b = spa_list_first(&port->ready, struct buffer, link); -+ d = b->buf->datas; -+ -+ /* Get the data, offset and size */ -+ src = d[0].data; -+ offset = d[0].chunk->offset; -+ size = d[0].chunk->size; -+ -+ /* Write data */ -+ write_data(this, src + offset, size, &total_written); -+ -+ /* Update the cample count */ -+ this->sample_count += total_written / port->frame_size; -+ -+ /* Remove the buffer and mark it as reusable */ -+ spa_list_remove(&b->link); -+ b->outstanding = true; -+ spa_node_call_reuse_buffer(&this->callbacks, 0, b->id); -+ } -+ -+ /* Set next timeout */ -+ set_next_timeout(this, now_time); -+ -+ return 0; -+} -+ -+static void sco_on_flush(struct spa_source *source) -+{ -+ struct impl *this = source->data; -+ uint64_t now_time; -+ -+ if ((source->rmask & SPA_IO_OUT) == 0) { -+ spa_log_warn(this->log, "error %d", source->rmask); -+ if (this->flush_source.loop) -+ spa_loop_remove_source(this->data_loop, &this->flush_source); -+ this->source.mask = 0; -+ spa_loop_update_source(this->data_loop, &this->source); -+ return; -+ } -+ -+ /* Get the current time */ -+ clock_gettime(CLOCK_MONOTONIC, &this->now); -+ now_time = SPA_TIMESPEC_TO_NSEC(&this->now); -+ -+ /* Render buffers */ -+ render_buffers(this, now_time); -+} -+ -+static void sco_on_timeout(struct spa_source *source) -+{ -+ struct impl *this = source->data; -+ struct port *port = &this->port; -+ uint64_t exp, now_time; -+ struct spa_io_buffers *io = port->io; -+ -+ /* Read the timerfd */ -+ if (this->started && read(this->timerfd, &exp, sizeof(uint64_t)) != sizeof(uint64_t)) -+ spa_log_warn(this->log, "error reading timerfd: %s", strerror(errno)); -+ -+ /* Get the current time */ -+ clock_gettime(CLOCK_MONOTONIC, &this->now); -+ now_time = SPA_TIMESPEC_TO_NSEC(&this->now); -+ -+ /* Set the start time to the current time */ -+ if (this->start_time == 0) -+ this->start_time = now_time; -+ -+ /* Notify we need a new buffer if we have processed all of them */ -+ if (spa_list_is_empty(&port->ready)) { -+ io->status = SPA_STATUS_NEED_BUFFER; -+ spa_node_call_ready(&this->callbacks, SPA_STATUS_NEED_BUFFER); -+ } -+ -+ /* Render the buffers */ -+ render_buffers(this, now_time); -+} -+ -+static int do_start(struct impl *this) -+{ -+ int val; -+ bool do_accept; -+ -+ /* Dont do anything if the node has already started */ -+ if (this->started) -+ return 0; -+ -+ /* Make sure the transport is valid */ -+ spa_return_val_if_fail (this->transport != NULL, -EIO); -+ -+ /* Set the slaved flag */ -+ this->slaved = is_slaved(this); -+ -+ /* Do accept if Gateway; otherwise do connect for Head Unit */ -+ do_accept = this->transport->profile & SPA_BT_PROFILE_HEADSET_AUDIO_GATEWAY; -+ -+ /* acquire the socked fd (false -> connect | true -> accept) */ -+ this->sock_fd = spa_bt_transport_acquire(this->transport, do_accept); -+ if (this->sock_fd < 0) -+ return -1; -+ -+ /* Set the write MTU */ -+ val = this->transport->write_mtu; -+ if (setsockopt(this->sock_fd, SOL_SOCKET, SO_SNDBUF, &val, sizeof(val)) < 0) -+ spa_log_warn(this->log, "sco-sink %p: SO_SNDBUF %m", this); -+ -+ /* Set the read MTU */ -+ val = this->transport->read_mtu; -+ if (setsockopt(this->sock_fd, SOL_SOCKET, SO_RCVBUF, &val, sizeof(val)) < 0) -+ spa_log_warn(this->log, "sco-sink %p: SO_RCVBUF %m", this); -+ -+ /* Set the priority */ -+ val = 6; -+ if (setsockopt(this->sock_fd, SOL_SOCKET, SO_PRIORITY, &val, sizeof(val)) < 0) -+ spa_log_warn(this->log, "SO_PRIORITY failed: %m"); -+ -+ /* Add the timeout callback */ -+ this->source.data = this; -+ this->source.fd = this->timerfd; -+ this->source.func = sco_on_timeout; -+ this->source.mask = SPA_IO_IN; -+ this->source.rmask = 0; -+ spa_loop_add_source(this->data_loop, &this->source); -+ -+ /* Add the flush callback */ -+ this->flush_source.data = this; -+ this->flush_source.fd = this->sock_fd; -+ this->flush_source.func = sco_on_flush; -+ this->flush_source.mask = 0; -+ this->flush_source.rmask = 0; -+ spa_loop_add_source(this->data_loop, &this->flush_source); -+ -+ /* Reset timeout to start processing */ -+ reset_timeout(this); -+ -+ /* Set the started flag */ -+ this->started = true; -+ -+ return 0; -+} -+ -+static int do_remove_source(struct spa_loop *loop, -+ bool async, -+ uint32_t seq, -+ const void *data, -+ size_t size, -+ void *user_data) -+{ -+ struct impl *this = user_data; -+ struct itimerspec ts; -+ -+ if (this->source.loop) -+ spa_loop_remove_source(this->data_loop, &this->source); -+ ts.it_value.tv_sec = 0; -+ ts.it_value.tv_nsec = 0; -+ ts.it_interval.tv_sec = 0; -+ ts.it_interval.tv_nsec = 0; -+ timerfd_settime(this->timerfd, 0, &ts, NULL); -+ if (this->flush_source.loop) -+ spa_loop_remove_source(this->data_loop, &this->flush_source); -+ -+ return 0; -+} -+ -+static int do_stop(struct impl *this) -+{ -+ int res = 0; -+ -+ if (!this->started) -+ return 0; -+ -+ spa_log_trace(this->log, "sco-sink %p: stop", this); -+ -+ spa_loop_invoke(this->data_loop, do_remove_source, 0, NULL, 0, true, this); -+ -+ this->started = false; -+ -+ if (this->transport) { -+ /* Release the transport */ -+ res = spa_bt_transport_release(this->transport); -+ -+ /* Shutdown and close the socket */ -+ shutdown(this->sock_fd, SHUT_RDWR); -+ close(this->sock_fd); -+ this->sock_fd = -1; -+ } -+ -+ return res; -+} -+ -+static int impl_node_send_command(struct spa_node *node, const struct spa_command *command) -+{ -+ struct impl *this; -+ struct port *port; -+ int res; -+ -+ spa_return_val_if_fail(node != NULL, -EINVAL); -+ spa_return_val_if_fail(command != NULL, -EINVAL); -+ -+ this = SPA_CONTAINER_OF(node, struct impl, node); -+ port = &this->port; -+ -+ switch (SPA_NODE_COMMAND_ID(command)) { -+ case SPA_NODE_COMMAND_Start: -+ if (!port->have_format) -+ return -EIO; -+ if (port->n_buffers == 0) -+ return -EIO; -+ if ((res = do_start(this)) < 0) -+ return res; -+ break; -+ case SPA_NODE_COMMAND_Pause: -+ if ((res = do_stop(this)) < 0) -+ return res; -+ break; -+ default: -+ return -ENOTSUP; -+ } -+ return 0; -+} -+ -+static const struct spa_dict_item node_info_items[] = { -+ { "media.class", "Audio/Sink" }, -+ { "node.driver", "true" }, -+}; -+ -+static void emit_node_info(struct impl *this, bool full) -+{ -+ if (full) -+ this->info.change_mask = this->info_all; -+ if (this->info.change_mask) { -+ this->info.props = &SPA_DICT_INIT_ARRAY(node_info_items); -+ spa_node_emit_info(&this->hooks, &this->info); -+ this->info.change_mask = 0; -+ } -+} -+ -+static void emit_port_info(struct impl *this, struct port *port, bool full) -+{ -+ if (full) -+ port->info.change_mask = port->info_all; -+ if (port->info.change_mask) { -+ spa_node_emit_port_info(&this->hooks, -+ SPA_DIRECTION_INPUT, 0, &port->info); -+ port->info.change_mask = 0; -+ } -+} -+ -+static int -+impl_node_add_listener(struct spa_node *node, -+ struct spa_hook *listener, -+ const struct spa_node_events *events, -+ void *data) -+{ -+ struct impl *this; -+ struct spa_hook_list save; -+ -+ spa_return_val_if_fail(node != NULL, -EINVAL); -+ -+ this = SPA_CONTAINER_OF(node, struct impl, node); -+ spa_hook_list_isolate(&this->hooks, &save, listener, events, data); -+ -+ emit_node_info(this, true); -+ emit_port_info(this, &this->port, true); -+ -+ spa_hook_list_join(&this->hooks, &save); -+ -+ return 0; -+} -+ -+static int -+impl_node_set_callbacks(struct spa_node *node, -+ const struct spa_node_callbacks *callbacks, -+ void *data) -+{ -+ struct impl *this; -+ -+ spa_return_val_if_fail(node != NULL, -EINVAL); -+ -+ this = SPA_CONTAINER_OF(node, struct impl, node); -+ -+ this->callbacks = SPA_CALLBACKS_INIT(callbacks, data); -+ -+ return 0; -+} -+ -+static int impl_node_add_port(struct spa_node *node, enum spa_direction direction, uint32_t port_id, -+ const struct spa_dict *props) -+{ -+ return -ENOTSUP; -+} -+ -+static int impl_node_remove_port(struct spa_node *node, enum spa_direction direction, uint32_t port_id) -+{ -+ return -ENOTSUP; -+} -+ -+static int -+impl_node_port_enum_params(struct spa_node *node, int seq, -+ enum spa_direction direction, uint32_t port_id, -+ uint32_t id, uint32_t start, uint32_t num, -+ const struct spa_pod *filter) -+{ -+ -+ struct impl *this; -+ struct port *port; -+ struct spa_pod *param; -+ struct spa_pod_builder b = { 0 }; -+ uint8_t buffer[1024]; -+ struct spa_result_node_params result; -+ uint32_t count = 0; -+ -+ spa_return_val_if_fail(node != NULL, -EINVAL); -+ spa_return_val_if_fail(num != 0, -EINVAL); -+ -+ this = SPA_CONTAINER_OF(node, struct impl, node); -+ -+ spa_return_val_if_fail(CHECK_PORT(this, direction, port_id), -EINVAL); -+ port = &this->port; -+ -+ result.id = id; -+ result.next = start; -+ next: -+ result.index = result.next++; -+ -+ spa_pod_builder_init(&b, buffer, sizeof(buffer)); -+ -+ switch (id) { -+ case SPA_PARAM_EnumFormat: -+ if (result.index > 0) -+ return 0; -+ -+ /* set the info structure */ -+ struct spa_audio_info_raw info = { 0, }; -+ info.format = SPA_AUDIO_FORMAT_S16; -+ info.channels = 1; -+ info.position[0] = SPA_AUDIO_CHANNEL_MONO; -+ -+ /* TODO: For now we only handle HSP profiles which has always CVSD format, -+ * but we eventually need to support HFP that can have both CVSD and MSBC formats */ -+ -+ /* CVSD format has a rate of 8kHz -+ * MSBC format has a rate of 16kHz */ -+ info.rate = 8000; -+ -+ /* build the param */ -+ param = spa_format_audio_raw_build(&b, id, &info); -+ -+ break; -+ -+ case SPA_PARAM_Format: -+ if (!port->have_format) -+ return -EIO; -+ if (result.index > 0) -+ return 0; -+ -+ param = spa_format_audio_raw_build(&b, id, &port->current_format.info.raw); -+ break; -+ -+ case SPA_PARAM_Buffers: -+ if (!port->have_format) -+ return -EIO; -+ if (result.index > 0) -+ return 0; -+ -+ param = spa_pod_builder_add_object(&b, -+ SPA_TYPE_OBJECT_ParamBuffers, id, -+ SPA_PARAM_BUFFERS_buffers, SPA_POD_CHOICE_RANGE_Int(2, 2, MAX_BUFFERS), -+ SPA_PARAM_BUFFERS_blocks, SPA_POD_Int(1), -+ SPA_PARAM_BUFFERS_size, SPA_POD_CHOICE_RANGE_Int( -+ this->props.min_latency * port->frame_size, -+ this->props.min_latency * port->frame_size, -+ INT32_MAX), -+ SPA_PARAM_BUFFERS_stride, SPA_POD_Int(port->frame_size), -+ SPA_PARAM_BUFFERS_align, SPA_POD_Int(16)); -+ break; -+ -+ case SPA_PARAM_Meta: -+ switch (result.index) { -+ case 0: -+ param = spa_pod_builder_add_object(&b, -+ SPA_TYPE_OBJECT_ParamMeta, id, -+ SPA_PARAM_META_type, SPA_POD_Id(SPA_META_Header), -+ SPA_PARAM_META_size, SPA_POD_Int(sizeof(struct spa_meta_header))); -+ break; -+ default: -+ return 0; -+ } -+ break; -+ -+ default: -+ return -ENOENT; -+ } -+ -+ if (spa_pod_filter(&b, &result.param, param, filter) < 0) -+ goto next; -+ -+ spa_node_emit_result(&this->hooks, seq, 0, &result); -+ -+ if (++count != num) -+ goto next; -+ -+ return 0; -+} -+ -+static int clear_buffers(struct impl *this, struct port *port) -+{ -+ do_stop(this); -+ if (port->n_buffers > 0) { -+ spa_list_init(&port->ready); -+ port->n_buffers = 0; -+ } -+ return 0; -+} -+ -+static int port_set_format(struct impl *this, struct port *port, -+ uint32_t flags, -+ const struct spa_pod *format) -+{ -+ int err; -+ -+ if (format == NULL) { -+ spa_log_info(this->log, "clear format"); -+ clear_buffers(this, port); -+ port->have_format = false; -+ } else { -+ struct spa_audio_info info = { 0 }; -+ -+ if ((err = spa_format_parse(format, &info.media_type, &info.media_subtype)) < 0) -+ return err; -+ -+ if (info.media_type != SPA_MEDIA_TYPE_audio || -+ info.media_subtype != SPA_MEDIA_SUBTYPE_raw) -+ return -EINVAL; -+ -+ if (spa_format_audio_raw_parse(format, &info.info.raw) < 0) -+ return -EINVAL; -+ -+ port->frame_size = info.info.raw.channels * 2; -+ port->current_format = info; -+ port->have_format = true; -+ this->threshold = this->props.min_latency; -+ } -+ -+ port->info.change_mask |= SPA_PORT_CHANGE_MASK_PARAMS; -+ if (port->have_format) { -+ port->info.change_mask |= SPA_PORT_CHANGE_MASK_FLAGS; -+ port->info.flags = SPA_PORT_FLAG_CAN_USE_BUFFERS | SPA_PORT_FLAG_LIVE; -+ port->info.change_mask |= SPA_PORT_CHANGE_MASK_RATE; -+ port->info.rate = SPA_FRACTION(1, port->current_format.info.raw.rate); -+ port->params[3] = SPA_PARAM_INFO(SPA_PARAM_Format, SPA_PARAM_INFO_READWRITE); -+ port->params[4] = SPA_PARAM_INFO(SPA_PARAM_Buffers, SPA_PARAM_INFO_READ); -+ } else { -+ port->params[3] = SPA_PARAM_INFO(SPA_PARAM_Format, SPA_PARAM_INFO_WRITE); -+ port->params[4] = SPA_PARAM_INFO(SPA_PARAM_Buffers, 0); -+ } -+ emit_port_info(this, port, false); -+ -+ return 0; -+} -+ -+static int -+impl_node_port_set_param(struct spa_node *node, -+ enum spa_direction direction, uint32_t port_id, -+ uint32_t id, uint32_t flags, -+ const struct spa_pod *param) -+{ -+ struct impl *this; -+ struct port *port; -+ int res; -+ -+ spa_return_val_if_fail(node != NULL, -EINVAL); -+ this = SPA_CONTAINER_OF(node, struct impl, node); -+ -+ spa_return_val_if_fail(CHECK_PORT(node, direction, port_id), -EINVAL); -+ port = &this->port; -+ -+ switch (id) { -+ case SPA_PARAM_Format: -+ res = port_set_format(this, port, flags, param); -+ break; -+ default: -+ res = -ENOENT; -+ break; -+ } -+ return res; -+} -+ -+static int -+impl_node_port_use_buffers(struct spa_node *node, -+ enum spa_direction direction, -+ uint32_t port_id, struct spa_buffer **buffers, uint32_t n_buffers) -+{ -+ struct impl *this; -+ struct port *port; -+ uint32_t i; -+ -+ spa_return_val_if_fail(node != NULL, -EINVAL); -+ this = SPA_CONTAINER_OF(node, struct impl, node); -+ -+ spa_return_val_if_fail(CHECK_PORT(this, direction, port_id), -EINVAL); -+ port = &this->port; -+ -+ spa_log_info(this->log, "use buffers %d", n_buffers); -+ -+ if (!port->have_format) -+ return -EIO; -+ -+ clear_buffers(this, port); -+ -+ for (i = 0; i < n_buffers; i++) { -+ struct buffer *b = &port->buffers[i]; -+ uint32_t type; -+ -+ b->buf = buffers[i]; -+ b->id = i; -+ b->outstanding = true; -+ -+ b->h = spa_buffer_find_meta_data(buffers[i], SPA_META_Header, sizeof(*b->h)); -+ -+ type = buffers[i]->datas[0].type; -+ if ((type == SPA_DATA_MemFd || -+ type == SPA_DATA_DmaBuf || -+ type == SPA_DATA_MemPtr) && buffers[i]->datas[0].data == NULL) { -+ spa_log_error(this->log, NAME " %p: need mapped memory", this); -+ return -EINVAL; -+ } -+ this->threshold = buffers[i]->datas[0].maxsize / port->frame_size; -+ } -+ port->n_buffers = n_buffers; -+ -+ return 0; -+} -+ -+static int -+impl_node_port_alloc_buffers(struct spa_node *node, -+ enum spa_direction direction, -+ uint32_t port_id, -+ struct spa_pod **params, -+ uint32_t n_params, -+ struct spa_buffer **buffers, -+ uint32_t *n_buffers) -+{ -+ struct impl *this; -+ struct port *port; -+ -+ spa_return_val_if_fail(node != NULL, -EINVAL); -+ spa_return_val_if_fail(buffers != NULL, -EINVAL); -+ -+ this = SPA_CONTAINER_OF(node, struct impl, node); -+ -+ spa_return_val_if_fail(CHECK_PORT(this, direction, port_id), -EINVAL); -+ port = &this->port; -+ -+ if (!port->have_format) -+ return -EIO; -+ -+ return -ENOTSUP; -+} -+ -+static int -+impl_node_port_set_io(struct spa_node *node, -+ enum spa_direction direction, -+ uint32_t port_id, -+ uint32_t id, -+ void *data, size_t size) -+{ -+ struct impl *this; -+ struct port *port; -+ -+ spa_return_val_if_fail(node != NULL, -EINVAL); -+ -+ this = SPA_CONTAINER_OF(node, struct impl, node); -+ -+ spa_return_val_if_fail(CHECK_PORT(this, direction, port_id), -EINVAL); -+ port = &this->port; -+ -+ switch (id) { -+ case SPA_IO_Buffers: -+ port->io = data; -+ break; -+ default: -+ return -ENOENT; -+ } -+ return 0; -+} -+ -+static int impl_node_port_reuse_buffer(struct spa_node *node, uint32_t port_id, uint32_t buffer_id) -+{ -+ return -ENOTSUP; -+} -+ -+static int impl_node_process(struct spa_node *node) -+{ -+ struct impl *this; -+ struct port *port; -+ struct spa_io_buffers *io; -+ uint64_t now_time; -+ -+ spa_return_val_if_fail(node != NULL, -EINVAL); -+ -+ this = SPA_CONTAINER_OF(node, struct impl, node); -+ port = &this->port; -+ io = port->io; -+ spa_return_val_if_fail(io != NULL, -EIO); -+ -+ /* Get the current time */ -+ clock_gettime(CLOCK_MONOTONIC, &this->now); -+ now_time = SPA_TIMESPEC_TO_NSEC(&this->now); -+ -+ /* Make sure we process all the previous buffers */ -+ if (!spa_list_is_empty(&port->ready)) -+ render_buffers(this, now_time); -+ -+ /* Process the new buffers */ -+ if (io->status == SPA_STATUS_HAVE_BUFFER && io->buffer_id < port->n_buffers) { -+ struct buffer *b = &port->buffers[io->buffer_id]; -+ -+ if (!b->outstanding) { -+ spa_log_warn(this->log, NAME " %p: buffer %u in use", this, io->buffer_id); -+ io->status = -EINVAL; -+ return -EINVAL; -+ } -+ -+ spa_log_trace(this->log, NAME " %p: queue buffer %u", this, io->buffer_id); -+ -+ spa_list_append(&port->ready, &b->link); -+ b->outstanding = false; -+ -+ this->threshold = SPA_MIN(b->buf->datas[0].chunk->size / port->frame_size, -+ this->props.max_latency); -+ -+ render_buffers(this, now_time); -+ -+ io->status = SPA_STATUS_OK; -+ } -+ -+ return SPA_STATUS_HAVE_BUFFER; -+} -+ -+static const struct spa_node impl_node = { -+ SPA_VERSION_NODE, -+ .add_listener = impl_node_add_listener, -+ .set_callbacks = impl_node_set_callbacks, -+ .enum_params = impl_node_enum_params, -+ .set_param = impl_node_set_param, -+ .set_io = impl_node_set_io, -+ .send_command = impl_node_send_command, -+ .add_port = impl_node_add_port, -+ .remove_port = impl_node_remove_port, -+ .port_enum_params = impl_node_port_enum_params, -+ .port_set_param = impl_node_port_set_param, -+ .port_use_buffers = impl_node_port_use_buffers, -+ .port_alloc_buffers = impl_node_port_alloc_buffers, -+ .port_set_io = impl_node_port_set_io, -+ .port_reuse_buffer = impl_node_port_reuse_buffer, -+ .process = impl_node_process, -+}; -+ -+static void transport_destroy(void *data) -+{ -+ struct impl *this = data; -+ spa_log_debug(this->log, "transport %p destroy", this->transport); -+ this->transport = NULL; -+} -+ -+static const struct spa_bt_transport_events transport_events = { -+ SPA_VERSION_BT_TRANSPORT_EVENTS, -+ .destroy = transport_destroy, -+}; -+ -+static int impl_get_interface(struct spa_handle *handle, uint32_t type, void **interface) -+{ -+ struct impl *this; -+ -+ spa_return_val_if_fail(handle != NULL, -EINVAL); -+ spa_return_val_if_fail(interface != NULL, -EINVAL); -+ -+ this = (struct impl *) handle; -+ -+ if (type == SPA_TYPE_INTERFACE_Node) -+ *interface = &this->node; -+ else -+ return -ENOENT; -+ -+ return 0; -+} -+ -+static int impl_clear(struct spa_handle *handle) -+{ -+ return 0; -+} -+ -+static size_t -+impl_get_size(const struct spa_handle_factory *factory, -+ const struct spa_dict *params) -+{ -+ return sizeof(struct impl); -+} -+ -+static int -+impl_init(const struct spa_handle_factory *factory, -+ struct spa_handle *handle, -+ const struct spa_dict *info, -+ const struct spa_support *support, -+ uint32_t n_support) -+{ -+ struct impl *this; -+ struct port *port; -+ uint32_t i; -+ -+ spa_return_val_if_fail(factory != NULL, -EINVAL); -+ spa_return_val_if_fail(handle != NULL, -EINVAL); -+ -+ handle->get_interface = impl_get_interface; -+ handle->clear = impl_clear; -+ -+ this = (struct impl *) handle; -+ -+ for (i = 0; i < n_support; i++) { -+ if (support[i].type == SPA_TYPE_INTERFACE_Log) -+ this->log = support[i].data; -+ else if (support[i].type == SPA_TYPE_INTERFACE_DataLoop) -+ this->data_loop = support[i].data; -+ else if (support[i].type == SPA_TYPE_INTERFACE_MainLoop) -+ this->main_loop = support[i].data; -+ } -+ if (this->data_loop == NULL) { -+ spa_log_error(this->log, "a data loop is needed"); -+ return -EINVAL; -+ } -+ if (this->main_loop == NULL) { -+ spa_log_error(this->log, "a main loop is needed"); -+ return -EINVAL; -+ } -+ -+ this->node = impl_node; -+ spa_hook_list_init(&this->hooks); -+ -+ reset_props(&this->props); -+ -+ this->info_all = SPA_NODE_CHANGE_MASK_FLAGS | -+ SPA_NODE_CHANGE_MASK_PARAMS | -+ SPA_NODE_CHANGE_MASK_PROPS; -+ this->info = SPA_NODE_INFO_INIT(); -+ this->info.flags = SPA_NODE_FLAG_RT; -+ this->params[0] = SPA_PARAM_INFO(SPA_PARAM_PropInfo, SPA_PARAM_INFO_READ); -+ this->params[1] = SPA_PARAM_INFO(SPA_PARAM_Props, SPA_PARAM_INFO_READWRITE); -+ this->info.params = this->params; -+ this->info.n_params = 2; -+ -+ port = &this->port; -+ port->info_all = SPA_PORT_CHANGE_MASK_FLAGS | -+ SPA_PORT_CHANGE_MASK_PARAMS; -+ port->info = SPA_PORT_INFO_INIT(); -+ port->info.flags = SPA_PORT_FLAG_CAN_USE_BUFFERS; -+ port->params[0] = SPA_PARAM_INFO(SPA_PARAM_EnumFormat, SPA_PARAM_INFO_READ); -+ port->params[1] = SPA_PARAM_INFO(SPA_PARAM_Meta, SPA_PARAM_INFO_READ); -+ port->params[2] = SPA_PARAM_INFO(SPA_PARAM_IO, SPA_PARAM_INFO_READ); -+ port->params[3] = SPA_PARAM_INFO(SPA_PARAM_Format, SPA_PARAM_INFO_WRITE); -+ port->params[4] = SPA_PARAM_INFO(SPA_PARAM_Buffers, 0); -+ port->info.params = port->params; -+ port->info.n_params = 5; -+ spa_list_init(&port->ready); -+ -+ for (i = 0; info && i < info->n_items; i++) { -+ if (strcmp(info->items[i].key, "bluez5.transport") == 0) -+ sscanf(info->items[i].value, "%p", &this->transport); -+ } -+ if (this->transport == NULL) { -+ spa_log_error(this->log, "a transport is needed"); -+ return -EINVAL; -+ } -+ spa_bt_transport_add_listener(this->transport, -+ &this->transport_listener, &transport_events, this); -+ this->sock_fd = -1; -+ -+ this->timerfd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC | TFD_NONBLOCK); -+ -+ return 0; -+} -+ -+static const struct spa_interface_info impl_interfaces[] = { -+ {SPA_TYPE_INTERFACE_Node,}, -+}; -+ -+static int -+impl_enum_interface_info(const struct spa_handle_factory *factory, -+ const struct spa_interface_info **info, uint32_t *index) -+{ -+ spa_return_val_if_fail(factory != NULL, -EINVAL); -+ spa_return_val_if_fail(info != NULL, -EINVAL); -+ spa_return_val_if_fail(index != NULL, -EINVAL); -+ -+ switch (*index) { -+ case 0: -+ *info = &impl_interfaces[*index]; -+ break; -+ default: -+ return 0; -+ } -+ (*index)++; -+ return 1; -+} -+ -+static const struct spa_dict_item info_items[] = { -+ { "factory.author", "Wim Taymans <wim.taymans@gmail.com>" }, -+ { "factory.description", "Play audio with the sco (hsp/hfp)" }, -+}; -+ -+static const struct spa_dict info = SPA_DICT_INIT_ARRAY(info_items); -+ -+struct spa_handle_factory spa_sco_sink_factory = { -+ SPA_VERSION_HANDLE_FACTORY, -+ NAME, -+ &info, -+ impl_get_size, -+ impl_init, -+ impl_enum_interface_info, -+}; -diff --git a/spa/plugins/bluez5/sco-source.c b/spa/plugins/bluez5/sco-source.c -new file mode 100644 -index 00000000..ce83ee24 ---- /dev/null -+++ b/spa/plugins/bluez5/sco-source.c -@@ -0,0 +1,1136 @@ -+/* Spa SCO Source -+ * -+ * Copyright © 2018 Wim Taymans -+ * Copyright © 2019 Collabora Ltd. -+ * -+ * 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 <unistd.h> -+#include <stddef.h> -+#include <stdio.h> -+#include <time.h> -+#include <fcntl.h> -+#include <sys/types.h> -+#include <sys/socket.h> -+ -+#include <spa/support/loop.h> -+#include <spa/support/log.h> -+#include <spa/utils/list.h> -+ -+#include <spa/node/node.h> -+#include <spa/node/io.h> -+#include <spa/param/param.h> -+#include <spa/param/audio/format.h> -+#include <spa/param/audio/format-utils.h> -+#include <spa/pod/filter.h> -+ -+#include "defs.h" -+ -+struct props { -+ uint32_t min_latency; -+ uint32_t max_latency; -+}; -+ -+#define MAX_BUFFERS 32 -+ -+struct buffer { -+ uint32_t id; -+ unsigned int outstanding:1; -+ struct spa_buffer *buf; -+ struct spa_meta_header *h; -+ struct spa_list link; -+}; -+ -+struct port { -+ struct spa_audio_info current_format; -+ int frame_size; -+ unsigned int have_format:1; -+ -+ uint64_t info_all; -+ struct spa_port_info info; -+ struct spa_io_buffers *io; -+ struct spa_param_info params[8]; -+ -+ struct buffer buffers[MAX_BUFFERS]; -+ uint32_t n_buffers; -+ -+ struct spa_list free; -+ struct spa_list ready; -+ -+ size_t ready_offset; -+}; -+ -+struct impl { -+ struct spa_handle handle; -+ struct spa_node node; -+ -+ struct spa_log *log; -+ struct spa_loop *main_loop; -+ struct spa_loop *data_loop; -+ -+ struct spa_hook_list hooks; -+ struct spa_callbacks callbacks; -+ -+ uint64_t info_all; -+ struct spa_node_info info; -+ struct spa_param_info params[8]; -+ struct props props; -+ -+ struct spa_bt_transport *transport; -+ struct spa_hook transport_listener; -+ int sock_fd; -+ -+ struct port port; -+ -+ unsigned int started:1; -+ unsigned int slaved:1; -+ -+ struct spa_source source; -+ -+ struct spa_io_clock *clock; -+ struct spa_io_position *position; -+ -+ struct timespec now; -+ uint32_t sample_count; -+}; -+ -+#define NAME "sco-source" -+ -+#define CHECK_PORT(this,d,p) ((d) == SPA_DIRECTION_OUTPUT && (p) == 0) -+ -+static const uint32_t default_min_latency = 64; -+static const uint32_t default_max_latency = 256; -+ -+static void reset_props(struct props *props) -+{ -+ props->min_latency = default_min_latency; -+ props->max_latency = default_max_latency; -+} -+ -+static int impl_node_enum_params(struct spa_node *node, int seq, -+ uint32_t id, uint32_t start, uint32_t num, -+ const struct spa_pod *filter) -+{ -+ struct impl *this; -+ struct spa_pod *param; -+ struct spa_pod_builder b = { 0 }; -+ uint8_t buffer[1024]; -+ struct spa_result_node_params result; -+ uint32_t count = 0; -+ -+ spa_return_val_if_fail(node != NULL, -EINVAL); -+ spa_return_val_if_fail(num != 0, -EINVAL); -+ -+ this = SPA_CONTAINER_OF(node, struct impl, node); -+ -+ result.id = id; -+ result.next = start; -+ next: -+ result.index = result.next++; -+ -+ spa_pod_builder_init(&b, buffer, sizeof(buffer)); -+ -+ switch (id) { -+ case SPA_PARAM_PropInfo: -+ { -+ struct props *p = &this->props; -+ -+ switch (result.index) { -+ case 0: -+ param = spa_pod_builder_add_object(&b, -+ SPA_TYPE_OBJECT_PropInfo, id, -+ SPA_PROP_INFO_id, SPA_POD_Id(SPA_PROP_minLatency), -+ SPA_PROP_INFO_name, SPA_POD_String("The minimum latency"), -+ SPA_PROP_INFO_type, SPA_POD_CHOICE_RANGE_Int(p->min_latency, 1, INT32_MAX)); -+ break; -+ case 1: -+ param = spa_pod_builder_add_object(&b, -+ SPA_TYPE_OBJECT_PropInfo, id, -+ SPA_PROP_INFO_id, SPA_POD_Id(SPA_PROP_maxLatency), -+ SPA_PROP_INFO_name, SPA_POD_String("The maximum latency"), -+ SPA_PROP_INFO_type, SPA_POD_CHOICE_RANGE_Int(p->max_latency, 1, INT32_MAX)); -+ break; -+ default: -+ return 0; -+ } -+ break; -+ } -+ case SPA_PARAM_Props: -+ { -+ struct props *p = &this->props; -+ -+ switch (result.index) { -+ case 0: -+ param = spa_pod_builder_add_object(&b, -+ SPA_TYPE_OBJECT_Props, id, -+ SPA_PROP_minLatency, SPA_POD_Int(p->min_latency), -+ SPA_PROP_maxLatency, SPA_POD_Int(p->max_latency)); -+ break; -+ default: -+ return 0; -+ } -+ break; -+ } -+ default: -+ return -ENOENT; -+ } -+ -+ if (spa_pod_filter(&b, &result.param, param, filter) < 0) -+ goto next; -+ -+ spa_node_emit_result(&this->hooks, seq, 0, &result); -+ -+ if (++count != num) -+ goto next; -+ -+ return 0; -+} -+ -+static int do_reslave(struct spa_loop *loop, -+ bool async, -+ uint32_t seq, -+ const void *data, -+ size_t size, -+ void *user_data) -+{ -+ return 0; -+} -+ -+static inline bool is_slaved(struct impl *this) -+{ -+ return this->position && this->clock && this->position->clock.id != this->clock->id; -+} -+ -+static int impl_node_set_io(struct spa_node *node, uint32_t id, void *data, size_t size) -+{ -+ struct impl *this; -+ bool slaved; -+ -+ spa_return_val_if_fail(node != NULL, -EINVAL); -+ -+ this = SPA_CONTAINER_OF(node, struct impl, node); -+ -+ switch (id) { -+ case SPA_IO_Clock: -+ this->clock = data; -+ break; -+ case SPA_IO_Position: -+ this->position = data; -+ break; -+ default: -+ return -ENOENT; -+ } -+ -+ slaved = is_slaved(this); -+ if (this->started && slaved != this->slaved) { -+ spa_log_debug(this->log, "sco-source %p: reslave %d->%d", this, this->slaved, slaved); -+ this->slaved = slaved; -+ spa_loop_invoke(this->data_loop, do_reslave, 0, NULL, 0, true, this); -+ } -+ return 0; -+} -+ -+static int impl_node_set_param(struct spa_node *node, uint32_t id, uint32_t flags, -+ const struct spa_pod *param) -+{ -+ struct impl *this; -+ -+ spa_return_val_if_fail(node != NULL, -EINVAL); -+ -+ this = SPA_CONTAINER_OF(node, struct impl, node); -+ -+ switch (id) { -+ case SPA_PARAM_Props: -+ { -+ struct props *p = &this->props; -+ -+ if (param == NULL) { -+ reset_props(p); -+ return 0; -+ } -+ spa_pod_parse_object(param, -+ SPA_TYPE_OBJECT_Props, NULL, -+ SPA_PROP_minLatency, SPA_POD_OPT_Int(&p->min_latency), -+ SPA_PROP_maxLatency, SPA_POD_OPT_Int(&p->max_latency)); -+ break; -+ } -+ default: -+ return -ENOENT; -+ } -+ -+ return 0; -+} -+ -+static void reset_buffers(struct port *port) -+{ -+ uint32_t i; -+ -+ spa_list_init(&port->free); -+ spa_list_init(&port->ready); -+ -+ for (i = 0; i < port->n_buffers; i++) { -+ struct buffer *b = &port->buffers[i]; -+ spa_list_append(&port->free, &b->link); -+ b->outstanding = false; -+ } -+} -+ -+static bool read_data(struct impl *this, uint8_t *data, uint32_t size, uint32_t *total_read) { -+ const uint32_t mtu_size = this->transport->read_mtu; -+ uint32_t local_total_read = 0; -+ -+ /* TODO: For now we assume the size is always a mutliple of mtu_size */ -+ while (local_total_read < (size - mtu_size)) { -+ const int bytes_read = read(this->sock_fd, data, mtu_size); -+ if (bytes_read == 0) { -+ /* Stop */ -+ return false; -+ } else if (bytes_read < 0) { -+ /* Retry */ -+ if (errno == EINTR) -+ continue; -+ -+ /* Socked has no data so return total data read */ -+ if (errno == EAGAIN || errno == EWOULDBLOCK) -+ goto done; -+ -+ /* Print error and stop */ -+ spa_log_error(this->log, "read error: %s", strerror(errno)); -+ return false; -+ } -+ -+ data += bytes_read; -+ local_total_read += bytes_read; -+ } -+ -+done: -+ if (total_read) -+ *total_read = local_total_read; -+ return true; -+} -+ -+static void sco_on_ready_read(struct spa_source *source) -+{ -+ struct impl *this = source->data; -+ struct port *port = &this->port; -+ struct buffer *buffer; -+ struct spa_data *buffer_data; -+ uint32_t total_read; -+ -+ /* update the current pts */ -+ clock_gettime(CLOCK_MONOTONIC, &this->now); -+ -+ /* check if we have a new buffer */ -+ if (spa_list_is_empty(&port->free)) { -+ spa_log_warn(this->log, "waiting for buffer"); -+ return; -+ } -+ -+ /* get the buffer data */ -+ buffer = spa_list_first(&port->free, struct buffer, link); -+ buffer_data = &buffer->buf->datas[0]; -+ spa_assert(buffer_data->data); -+ -+ /* read data */ -+ if (!read_data(this, buffer_data->data, buffer_data->maxsize, &total_read)) -+ goto stop; -+ if (total_read == 0) -+ return; -+ -+ /* update the buffer offset, size and stride */ -+ buffer_data->chunk->offset = 0; -+ buffer_data->chunk->size = total_read; -+ buffer_data->chunk->stride = port->frame_size; -+ -+ /* update the sample count */ -+ this->sample_count += buffer_data->chunk->size / port->frame_size; -+ -+ /* remove the buffer from the free list and add it to the ready list */ -+ spa_list_remove(&buffer->link); -+ buffer->outstanding = true; -+ spa_list_append(&port->ready, &buffer->link); -+ -+ /* Notify we are ready for the next buffer */ -+ spa_node_call_ready(&this->callbacks, SPA_STATUS_HAVE_BUFFER); -+ -+ return; -+ -+stop: -+ if (this->source.loop) -+ spa_loop_remove_source(this->data_loop, &this->source); -+} -+ -+static int do_start(struct impl *this) -+{ -+ int val; -+ bool do_accept; -+ -+ /* Dont do anything if the node has already started */ -+ if (this->started) -+ return 0; -+ -+ /* Make sure the transport is valid */ -+ spa_return_val_if_fail (this->transport != NULL, -EIO); -+ -+ /* Do accept if Gateway; otherwise do connect for Head Unit */ -+ do_accept = this->transport->profile & SPA_BT_PROFILE_HEADSET_AUDIO_GATEWAY; -+ -+ /* acquire the socked fd (false -> connect | true -> accept) */ -+ this->sock_fd = spa_bt_transport_acquire(this->transport, do_accept); -+ if (this->sock_fd < 0) -+ return -1; -+ -+ /* Set the write MTU */ -+ val = this->transport->write_mtu; -+ if (setsockopt(this->sock_fd, SOL_SOCKET, SO_SNDBUF, &val, sizeof(val)) < 0) -+ spa_log_warn(this->log, "sco-source %p: SO_SNDBUF %m", this); -+ -+ /* Set the read MTU */ -+ val = this->transport->read_mtu; -+ if (setsockopt(this->sock_fd, SOL_SOCKET, SO_RCVBUF, &val, sizeof(val)) < 0) -+ spa_log_warn(this->log, "sco-source %p: SO_RCVBUF %m", this); -+ -+ /* Set the priority */ -+ val = 6; -+ if (setsockopt(this->sock_fd, SOL_SOCKET, SO_PRIORITY, &val, sizeof(val)) < 0) -+ spa_log_warn(this->log, "SO_PRIORITY failed: %m"); -+ -+ /* Reset the buffers and sample count */ -+ reset_buffers(&this->port); -+ this->sample_count = 0; -+ -+ /* Add the ready read callback */ -+ this->source.data = this; -+ this->source.fd = this->sock_fd; -+ this->source.func = sco_on_ready_read; -+ this->source.mask = SPA_IO_IN; -+ this->source.rmask = 0; -+ spa_loop_add_source(this->data_loop, &this->source); -+ -+ /* Set the started flag */ -+ this->started = true; -+ -+ return 0; -+} -+ -+static int do_remove_source(struct spa_loop *loop, -+ bool async, -+ uint32_t seq, -+ const void *data, -+ size_t size, -+ void *user_data) -+{ -+ struct impl *this = user_data; -+ -+ if (this->source.loop) -+ spa_loop_remove_source(this->data_loop, &this->source); -+ -+ return 0; -+} -+ -+static int do_stop(struct impl *this) -+{ -+ int res = 0; -+ -+ if (!this->started) -+ return 0; -+ -+ spa_log_debug(this->log, "sco-source %p: stop", this); -+ -+ spa_loop_invoke(this->data_loop, do_remove_source, 0, NULL, 0, true, this); -+ -+ this->started = false; -+ -+ if (this->transport) { -+ /* Release the transport */ -+ res = spa_bt_transport_release(this->transport); -+ -+ /* Shutdown and close the socket */ -+ shutdown(this->sock_fd, SHUT_RDWR); -+ close(this->sock_fd); -+ this->sock_fd = -1; -+ } -+ -+ return res; -+} -+ -+static int impl_node_send_command(struct spa_node *node, const struct spa_command *command) -+{ -+ struct impl *this; -+ struct port *port; -+ int res; -+ -+ spa_return_val_if_fail(node != NULL, -EINVAL); -+ spa_return_val_if_fail(command != NULL, -EINVAL); -+ -+ this = SPA_CONTAINER_OF(node, struct impl, node); -+ port = &this->port; -+ -+ switch (SPA_NODE_COMMAND_ID(command)) { -+ case SPA_NODE_COMMAND_Start: -+ if (!port->have_format) -+ return -EIO; -+ if (port->n_buffers == 0) -+ return -EIO; -+ if ((res = do_start(this)) < 0) -+ return res; -+ break; -+ case SPA_NODE_COMMAND_Pause: -+ if ((res = do_stop(this)) < 0) -+ return res; -+ break; -+ default: -+ return -ENOTSUP; -+ } -+ return 0; -+} -+ -+static const struct spa_dict_item node_info_items[] = { -+ { "media.class", "Audio/Source" }, -+ { "node.driver", "true" }, -+}; -+ -+static void emit_node_info(struct impl *this, bool full) -+{ -+ if (full) -+ this->info.change_mask = this->info_all; -+ if (this->info.change_mask) { -+ this->info.props = &SPA_DICT_INIT_ARRAY(node_info_items); -+ spa_node_emit_info(&this->hooks, &this->info); -+ this->info.change_mask = 0; -+ } -+} -+ -+static void emit_port_info(struct impl *this, struct port *port, bool full) -+{ -+ if (full) -+ port->info.change_mask = port->info_all; -+ if (port->info.change_mask) { -+ spa_node_emit_port_info(&this->hooks, -+ SPA_DIRECTION_OUTPUT, 0, &port->info); -+ port->info.change_mask = 0; -+ } -+} -+ -+static int -+impl_node_add_listener(struct spa_node *node, -+ struct spa_hook *listener, -+ const struct spa_node_events *events, -+ void *data) -+{ -+ struct impl *this; -+ struct spa_hook_list save; -+ -+ spa_return_val_if_fail(node != NULL, -EINVAL); -+ -+ this = SPA_CONTAINER_OF(node, struct impl, node); -+ spa_hook_list_isolate(&this->hooks, &save, listener, events, data); -+ -+ emit_node_info(this, true); -+ emit_port_info(this, &this->port, true); -+ -+ spa_hook_list_join(&this->hooks, &save); -+ -+ return 0; -+} -+ -+static int -+impl_node_set_callbacks(struct spa_node *node, -+ const struct spa_node_callbacks *callbacks, -+ void *data) -+{ -+ struct impl *this; -+ -+ spa_return_val_if_fail(node != NULL, -EINVAL); -+ -+ this = SPA_CONTAINER_OF(node, struct impl, node); -+ -+ this->callbacks = SPA_CALLBACKS_INIT(callbacks, data); -+ -+ return 0; -+} -+ -+static int impl_node_add_port(struct spa_node *node, enum spa_direction direction, uint32_t port_id, -+ const struct spa_dict *props) -+{ -+ return -ENOTSUP; -+} -+ -+static int impl_node_remove_port(struct spa_node *node, enum spa_direction direction, uint32_t port_id) -+{ -+ return -ENOTSUP; -+} -+ -+static int -+impl_node_port_enum_params(struct spa_node *node, int seq, -+ enum spa_direction direction, uint32_t port_id, -+ uint32_t id, uint32_t start, uint32_t num, -+ const struct spa_pod *filter) -+{ -+ -+ struct impl *this; -+ struct port *port; -+ struct spa_pod *param; -+ struct spa_pod_builder b = { 0 }; -+ uint8_t buffer[1024]; -+ struct spa_result_node_params result; -+ uint32_t count = 0; -+ -+ spa_return_val_if_fail(node != NULL, -EINVAL); -+ spa_return_val_if_fail(num != 0, -EINVAL); -+ -+ this = SPA_CONTAINER_OF(node, struct impl, node); -+ -+ spa_return_val_if_fail(CHECK_PORT(this, direction, port_id), -EINVAL); -+ port = &this->port; -+ -+ result.id = id; -+ result.next = start; -+ next: -+ result.index = result.next++; -+ -+ spa_pod_builder_init(&b, buffer, sizeof(buffer)); -+ -+ switch (id) { -+ case SPA_PARAM_EnumFormat: -+ if (result.index > 0) -+ return 0; -+ -+ if (this->transport == NULL) -+ return -EIO; -+ -+ /* set the info structure */ -+ struct spa_audio_info_raw info = { 0, }; -+ info.format = SPA_AUDIO_FORMAT_S16; -+ info.channels = 1; -+ info.position[0] = SPA_AUDIO_CHANNEL_MONO; -+ -+ /* TODO: For now we only handle HSP profiles which has always CVSD format, -+ * but we eventually need to support HFP that can have both CVSD and MSBC formats */ -+ -+ /* CVSD format has a rate of 8kHz -+ * MSBC format has a rate of 16kHz */ -+ info.rate = 8000; -+ -+ /* build the param */ -+ param = spa_format_audio_raw_build(&b, id, &info); -+ break; -+ -+ case SPA_PARAM_Format: -+ if (!port->have_format) -+ return -EIO; -+ if (result.index > 0) -+ return 0; -+ -+ param = spa_format_audio_raw_build(&b, id, &port->current_format.info.raw); -+ break; -+ -+ case SPA_PARAM_Buffers: -+ if (!port->have_format) -+ return -EIO; -+ if (result.index > 0) -+ return 0; -+ -+ param = spa_pod_builder_add_object(&b, -+ SPA_TYPE_OBJECT_ParamBuffers, id, -+ /* 8 buffers are enough to make sure we always have one available when decoding */ -+ SPA_PARAM_BUFFERS_buffers, SPA_POD_CHOICE_RANGE_Int(8, 8, MAX_BUFFERS), -+ SPA_PARAM_BUFFERS_blocks, SPA_POD_Int(1), -+ SPA_PARAM_BUFFERS_size, SPA_POD_Int(this->props.max_latency * port->frame_size), -+ SPA_PARAM_BUFFERS_stride, SPA_POD_Int(port->frame_size), -+ SPA_PARAM_BUFFERS_align, SPA_POD_Int(16)); -+ break; -+ -+ case SPA_PARAM_Meta: -+ switch (result.index) { -+ case 0: -+ param = spa_pod_builder_add_object(&b, -+ SPA_TYPE_OBJECT_ParamMeta, id, -+ SPA_PARAM_META_type, SPA_POD_Id(SPA_META_Header), -+ SPA_PARAM_META_size, SPA_POD_Int(sizeof(struct spa_meta_header))); -+ break; -+ default: -+ return 0; -+ } -+ break; -+ -+ default: -+ return -ENOENT; -+ } -+ -+ /* TODO: why filer is != NULL when linking it with sco-sink? */ -+ /* if filter is null sco-source cannot be linked with sco-sink, -+ * so for now we always pass NULL */ -+ if (spa_pod_filter(&b, &result.param, param, NULL) < 0) -+ goto next; -+ -+ spa_node_emit_result(&this->hooks, seq, 0, &result); -+ -+ if (++count != num) -+ goto next; -+ -+ return 0; -+} -+ -+static int clear_buffers(struct impl *this, struct port *port) -+{ -+ do_stop(this); -+ if (port->n_buffers > 0) { -+ spa_list_init(&port->free); -+ spa_list_init(&port->ready); -+ port->n_buffers = 0; -+ } -+ return 0; -+} -+ -+static int port_set_format(struct impl *this, struct port *port, -+ uint32_t flags, -+ const struct spa_pod *format) -+{ -+ int err; -+ -+ if (format == NULL) { -+ spa_log_info(this->log, "clear format"); -+ clear_buffers(this, port); -+ port->have_format = false; -+ } else { -+ struct spa_audio_info info = { 0 }; -+ -+ if ((err = spa_format_parse(format, &info.media_type, &info.media_subtype)) < 0) -+ return err; -+ -+ if (info.media_type != SPA_MEDIA_TYPE_audio || -+ info.media_subtype != SPA_MEDIA_SUBTYPE_raw) -+ return -EINVAL; -+ -+ if (spa_format_audio_raw_parse(format, &info.info.raw) < 0) -+ return -EINVAL; -+ -+ port->frame_size = info.info.raw.channels * 2; -+ port->current_format = info; -+ port->have_format = true; -+ } -+ -+ port->info.change_mask |= SPA_PORT_CHANGE_MASK_PARAMS; -+ if (port->have_format) { -+ port->info.change_mask |= SPA_PORT_CHANGE_MASK_FLAGS; -+ port->info.flags = SPA_PORT_FLAG_CAN_USE_BUFFERS | SPA_PORT_FLAG_LIVE; -+ port->info.change_mask |= SPA_PORT_CHANGE_MASK_RATE; -+ port->info.rate = SPA_FRACTION(1, port->current_format.info.raw.rate); -+ port->params[3] = SPA_PARAM_INFO(SPA_PARAM_Format, SPA_PARAM_INFO_READWRITE); -+ port->params[4] = SPA_PARAM_INFO(SPA_PARAM_Buffers, SPA_PARAM_INFO_READ); -+ } else { -+ port->params[3] = SPA_PARAM_INFO(SPA_PARAM_Format, SPA_PARAM_INFO_WRITE); -+ port->params[4] = SPA_PARAM_INFO(SPA_PARAM_Buffers, 0); -+ } -+ emit_port_info(this, port, false); -+ -+ return 0; -+} -+ -+static int -+impl_node_port_set_param(struct spa_node *node, -+ enum spa_direction direction, uint32_t port_id, -+ uint32_t id, uint32_t flags, -+ const struct spa_pod *param) -+{ -+ struct impl *this; -+ struct port *port; -+ int res; -+ -+ spa_return_val_if_fail(node != NULL, -EINVAL); -+ this = SPA_CONTAINER_OF(node, struct impl, node); -+ -+ spa_return_val_if_fail(CHECK_PORT(node, direction, port_id), -EINVAL); -+ port = &this->port; -+ -+ switch (id) { -+ case SPA_PARAM_Format: -+ res = port_set_format(this, port, flags, param); -+ break; -+ default: -+ res = -ENOENT; -+ break; -+ } -+ return res; -+} -+ -+static int -+impl_node_port_use_buffers(struct spa_node *node, -+ enum spa_direction direction, -+ uint32_t port_id, struct spa_buffer **buffers, uint32_t n_buffers) -+{ -+ struct impl *this; -+ struct port *port; -+ uint32_t i; -+ -+ spa_return_val_if_fail(node != NULL, -EINVAL); -+ -+ this = SPA_CONTAINER_OF(node, struct impl, node); -+ -+ spa_return_val_if_fail(CHECK_PORT(this, direction, port_id), -EINVAL); -+ port = &this->port; -+ -+ spa_log_info(this->log, "use buffers %d", n_buffers); -+ -+ if (!port->have_format) -+ return -EIO; -+ -+ clear_buffers(this, port); -+ -+ for (i = 0; i < n_buffers; i++) { -+ struct buffer *b = &port->buffers[i]; -+ struct spa_data *d = buffers[i]->datas; -+ -+ b->buf = buffers[i]; -+ b->id = i; -+ -+ b->h = spa_buffer_find_meta_data(buffers[i], SPA_META_Header, sizeof(*b->h)); -+ -+ if (!((d[0].type == SPA_DATA_MemFd || -+ d[0].type == SPA_DATA_DmaBuf || -+ d[0].type == SPA_DATA_MemPtr) && d[0].data != NULL)) { -+ spa_log_error(this->log, NAME " %p: need mapped memory", this); -+ return -EINVAL; -+ } -+ spa_list_append(&port->free, &b->link); -+ b->outstanding = false; -+ } -+ port->n_buffers = n_buffers; -+ -+ return 0; -+} -+ -+static int -+impl_node_port_alloc_buffers(struct spa_node *node, -+ enum spa_direction direction, -+ uint32_t port_id, -+ struct spa_pod **params, -+ uint32_t n_params, -+ struct spa_buffer **buffers, -+ uint32_t *n_buffers) -+{ -+ struct impl *this; -+ struct port *port; -+ -+ spa_return_val_if_fail(node != NULL, -EINVAL); -+ spa_return_val_if_fail(buffers != NULL, -EINVAL); -+ -+ this = SPA_CONTAINER_OF(node, struct impl, node); -+ -+ spa_return_val_if_fail(CHECK_PORT(this, direction, port_id), -EINVAL); -+ port = &this->port; -+ -+ if (!port->have_format) -+ return -EIO; -+ -+ return -ENOTSUP; -+} -+ -+static int -+impl_node_port_set_io(struct spa_node *node, -+ enum spa_direction direction, -+ uint32_t port_id, -+ uint32_t id, -+ void *data, size_t size) -+{ -+ struct impl *this; -+ struct port *port; -+ -+ spa_return_val_if_fail(node != NULL, -EINVAL); -+ -+ this = SPA_CONTAINER_OF(node, struct impl, node); -+ -+ spa_return_val_if_fail(CHECK_PORT(this, direction, port_id), -EINVAL); -+ port = &this->port; -+ -+ switch (id) { -+ case SPA_IO_Buffers: -+ port->io = data; -+ break; -+ default: -+ return -ENOENT; -+ } -+ return 0; -+} -+ -+static void recycle_buffer(struct impl *this, struct port *port, uint32_t buffer_id) -+{ -+ struct buffer *b = &port->buffers[buffer_id]; -+ -+ if (b->outstanding) { -+ spa_log_trace(this->log, NAME " %p: recycle buffer %u", this, buffer_id); -+ spa_list_append(&port->free, &b->link); -+ b->outstanding = false; -+ } -+} -+ -+static int impl_node_port_reuse_buffer(struct spa_node *node, uint32_t port_id, uint32_t buffer_id) -+{ -+ struct impl *this; -+ struct port *port; -+ -+ spa_return_val_if_fail(node != NULL, -EINVAL); -+ -+ this = SPA_CONTAINER_OF(node, struct impl, node); -+ -+ spa_return_val_if_fail(port_id == 0, -EINVAL); -+ port = &this->port; -+ -+ if (port->n_buffers == 0) -+ return -EIO; -+ -+ if (buffer_id >= port->n_buffers) -+ return -EINVAL; -+ -+ recycle_buffer(this, port, buffer_id); -+ -+ return 0; -+} -+ -+static int impl_node_process(struct spa_node *node) -+{ -+ struct impl *this; -+ struct port *port; -+ struct spa_io_buffers *io; -+ struct buffer *b; -+ -+ /* get IO */ -+ spa_return_val_if_fail(node != NULL, -EINVAL); -+ -+ this = SPA_CONTAINER_OF(node, struct impl, node); -+ port = &this->port; -+ io = port->io; -+ spa_return_val_if_fail(io != NULL, -EIO); -+ -+ /* don't do anything if IO does not need a buffer */ -+ if (io->status != SPA_STATUS_NEED_BUFFER) -+ return io->status; -+ -+ /* Recycle previously played buffer */ -+ if (io->buffer_id != SPA_ID_INVALID && -+ io->buffer_id < port->n_buffers) { -+ spa_log_debug(this->log, "recycling buffer_id=%d", io->buffer_id); -+ recycle_buffer(this, port, io->buffer_id); -+ io->buffer_id = SPA_ID_INVALID; -+ } -+ -+ /* Check if we have new buffers in the queue */ -+ if (spa_list_is_empty(&port->ready)) -+ return SPA_STATUS_HAVE_BUFFER; -+ -+ /* Pop the new buffer from the queue */ -+ b = spa_list_first(&port->ready, struct buffer, link); -+ spa_list_remove(&b->link); -+ -+ /* Set the new buffer in IO to be played */ -+ io->buffer_id = b->id; -+ io->status = SPA_STATUS_HAVE_BUFFER; -+ -+ return SPA_STATUS_HAVE_BUFFER; -+} -+ -+static const struct spa_node impl_node = { -+ SPA_VERSION_NODE, -+ .add_listener = impl_node_add_listener, -+ .set_callbacks = impl_node_set_callbacks, -+ .enum_params = impl_node_enum_params, -+ .set_param = impl_node_set_param, -+ .set_io = impl_node_set_io, -+ .send_command = impl_node_send_command, -+ .add_port = impl_node_add_port, -+ .remove_port = impl_node_remove_port, -+ .port_enum_params = impl_node_port_enum_params, -+ .port_set_param = impl_node_port_set_param, -+ .port_use_buffers = impl_node_port_use_buffers, -+ .port_alloc_buffers = impl_node_port_alloc_buffers, -+ .port_set_io = impl_node_port_set_io, -+ .port_reuse_buffer = impl_node_port_reuse_buffer, -+ .process = impl_node_process, -+}; -+ -+static void transport_destroy(void *data) -+{ -+ struct impl *this = data; -+ spa_log_debug(this->log, "transport %p destroy", this->transport); -+ this->transport = NULL; -+} -+ -+static const struct spa_bt_transport_events transport_events = { -+ SPA_VERSION_BT_TRANSPORT_EVENTS, -+ .destroy = transport_destroy, -+}; -+ -+static int impl_get_interface(struct spa_handle *handle, uint32_t type, void **interface) -+{ -+ struct impl *this; -+ -+ spa_return_val_if_fail(handle != NULL, -EINVAL); -+ spa_return_val_if_fail(interface != NULL, -EINVAL); -+ -+ this = (struct impl *) handle; -+ -+ if (type == SPA_TYPE_INTERFACE_Node) -+ *interface = &this->node; -+ else -+ return -ENOENT; -+ -+ return 0; -+} -+ -+static int impl_clear(struct spa_handle *handle) -+{ -+ return 0; -+} -+ -+static size_t -+impl_get_size(const struct spa_handle_factory *factory, -+ const struct spa_dict *params) -+{ -+ return sizeof(struct impl); -+} -+ -+static int -+impl_init(const struct spa_handle_factory *factory, -+ struct spa_handle *handle, -+ const struct spa_dict *info, -+ const struct spa_support *support, -+ uint32_t n_support) -+{ -+ struct impl *this; -+ struct port *port; -+ uint32_t i; -+ -+ spa_return_val_if_fail(factory != NULL, -EINVAL); -+ spa_return_val_if_fail(handle != NULL, -EINVAL); -+ -+ handle->get_interface = impl_get_interface; -+ handle->clear = impl_clear; -+ -+ this = (struct impl *) handle; -+ -+ for (i = 0; i < n_support; i++) { -+ if (support[i].type == SPA_TYPE_INTERFACE_Log) -+ this->log = support[i].data; -+ else if (support[i].type == SPA_TYPE_INTERFACE_DataLoop) -+ this->data_loop = support[i].data; -+ else if (support[i].type == SPA_TYPE_INTERFACE_MainLoop) -+ this->main_loop = support[i].data; -+ } -+ if (this->data_loop == NULL) { -+ spa_log_error(this->log, "a data loop is needed"); -+ return -EINVAL; -+ } -+ if (this->main_loop == NULL) { -+ spa_log_error(this->log, "a main loop is needed"); -+ return -EINVAL; -+ } -+ -+ this->node = impl_node; -+ spa_hook_list_init(&this->hooks); -+ -+ reset_props(&this->props); -+ -+ /* set the node info */ -+ this->info_all = SPA_NODE_CHANGE_MASK_FLAGS | -+ SPA_NODE_CHANGE_MASK_PROPS | -+ SPA_NODE_CHANGE_MASK_PARAMS; -+ this->info = SPA_NODE_INFO_INIT(); -+ this->info.flags = SPA_NODE_FLAG_RT; -+ this->params[0] = SPA_PARAM_INFO(SPA_PARAM_PropInfo, SPA_PARAM_INFO_READ); -+ this->params[1] = SPA_PARAM_INFO(SPA_PARAM_Props, SPA_PARAM_INFO_READWRITE); -+ this->info.params = this->params; -+ this->info.n_params = 2; -+ -+ /* set the port info */ -+ port = &this->port; -+ port->info_all = SPA_PORT_CHANGE_MASK_FLAGS | -+ SPA_PORT_CHANGE_MASK_PARAMS; -+ port->info = SPA_PORT_INFO_INIT(); -+ port->info.change_mask = SPA_PORT_CHANGE_MASK_FLAGS; -+ port->info.flags = SPA_PORT_FLAG_CAN_USE_BUFFERS | -+ SPA_PORT_FLAG_LIVE | -+ SPA_PORT_FLAG_TERMINAL; -+ port->params[0] = SPA_PARAM_INFO(SPA_PARAM_EnumFormat, SPA_PARAM_INFO_READ); -+ port->params[1] = SPA_PARAM_INFO(SPA_PARAM_Meta, SPA_PARAM_INFO_READ); -+ port->params[2] = SPA_PARAM_INFO(SPA_PARAM_IO, SPA_PARAM_INFO_READ); -+ port->params[3] = SPA_PARAM_INFO(SPA_PARAM_Format, SPA_PARAM_INFO_WRITE); -+ port->params[4] = SPA_PARAM_INFO(SPA_PARAM_Buffers, 0); -+ port->info.params = port->params; -+ port->info.n_params = 5; -+ -+ /* Init the buffer lists */ -+ spa_list_init(&port->ready); -+ spa_list_init(&port->free); -+ -+ for (i = 0; info && i < info->n_items; i++) { -+ if (strcmp(info->items[i].key, "bluez5.transport") == 0) -+ sscanf(info->items[i].value, "%p", &this->transport); -+ } -+ if (this->transport == NULL) { -+ spa_log_error(this->log, "a transport is needed"); -+ return -EINVAL; -+ } -+ spa_bt_transport_add_listener(this->transport, -+ &this->transport_listener, &transport_events, this); -+ this->sock_fd = -1; -+ -+ return 0; -+} -+ -+static const struct spa_interface_info impl_interfaces[] = { -+ {SPA_TYPE_INTERFACE_Node,}, -+}; -+ -+static int -+impl_enum_interface_info(const struct spa_handle_factory *factory, -+ const struct spa_interface_info **info, uint32_t *index) -+{ -+ spa_return_val_if_fail(factory != NULL, -EINVAL); -+ spa_return_val_if_fail(info != NULL, -EINVAL); -+ spa_return_val_if_fail(index != NULL, -EINVAL); -+ -+ switch (*index) { -+ case 0: -+ *info = &impl_interfaces[*index]; -+ break; -+ default: -+ return 0; -+ } -+ (*index)++; -+ return 1; -+} -+ -+static const struct spa_dict_item info_items[] = { -+ { "factory.author", "Collabora Ltd. <contact@collabora.com>" }, -+ { "factory.description", "Capture bluetooth audio with sco (hsp/hfp)" }, -+}; -+ -+static const struct spa_dict info = SPA_DICT_INIT_ARRAY(info_items); -+ -+struct spa_handle_factory spa_sco_source_factory = { -+ SPA_VERSION_HANDLE_FACTORY, -+ NAME, -+ &info, -+ impl_get_size, -+ impl_init, -+ impl_enum_interface_info, -+}; --- -2.23.0.rc1 - diff --git a/meta-pipewire/recipes-multimedia/pipewire/pipewire/0018-device-add-name-field-in-spa_device_object_info.patch b/meta-pipewire/recipes-multimedia/pipewire/pipewire/0018-device-add-name-field-in-spa_device_object_info.patch deleted file mode 100644 index ef1bd61e..00000000 --- a/meta-pipewire/recipes-multimedia/pipewire/pipewire/0018-device-add-name-field-in-spa_device_object_info.patch +++ /dev/null @@ -1,39 +0,0 @@ -From 0e3df7c3612fadf5319efb231fcd16ef16cd6e1a Mon Sep 17 00:00:00 2001 -From: Julian Bouzas <julian.bouzas@collabora.com> -Date: Thu, 29 Aug 2019 13:58:13 -0400 -Subject: [PATCH] device: add name field in spa_device_object_info - -Upstream-Status: Pending ---- - spa/include/spa/monitor/device.h | 1 + - src/pipewire/device.c | 2 +- - 2 files changed, 2 insertions(+), 1 deletion(-) - -diff --git a/spa/include/spa/monitor/device.h b/spa/include/spa/monitor/device.h -index 765e96f8..51a467b1 100644 ---- a/spa/include/spa/monitor/device.h -+++ b/spa/include/spa/monitor/device.h -@@ -59,6 +59,7 @@ struct spa_device_object_info { - - uint32_t type; - const struct spa_handle_factory *factory; -+ const char *name; - - #define SPA_DEVICE_OBJECT_CHANGE_MASK_FLAGS (1u<<0) - #define SPA_DEVICE_OBJECT_CHANGE_MASK_PROPS (1u<<1) -diff --git a/src/pipewire/device.c b/src/pipewire/device.c -index 11907d13..4cfac06e 100644 ---- a/src/pipewire/device.c -+++ b/src/pipewire/device.c -@@ -389,7 +389,7 @@ static void device_add(struct pw_device *device, uint32_t id, - pw_properties_update(props, info->props); - - node = pw_node_new(device->core, -- device->info.name, -+ info->name ? info->name : device->info.name, - props, - sizeof(struct node_data) + - spa_handle_factory_get_size(info->factory, info->props)); --- -2.23.0.rc1 - diff --git a/meta-pipewire/recipes-multimedia/pipewire/pipewire/0019-bluez-add-transport-name-and-use-it-when-emitting-no.patch b/meta-pipewire/recipes-multimedia/pipewire/pipewire/0019-bluez-add-transport-name-and-use-it-when-emitting-no.patch deleted file mode 100644 index 746707e0..00000000 --- a/meta-pipewire/recipes-multimedia/pipewire/pipewire/0019-bluez-add-transport-name-and-use-it-when-emitting-no.patch +++ /dev/null @@ -1,90 +0,0 @@ -From df485216dde74507e5ecb27b9663ab5107c6c5be Mon Sep 17 00:00:00 2001 -From: Julian Bouzas <julian.bouzas@collabora.com> -Date: Thu, 29 Aug 2019 13:59:10 -0400 -Subject: [PATCH] bluez: add transport name and use it when emitting nodes - -Upstream-Status: Pending ---- - spa/plugins/bluez5/bluez5-device.c | 1 + - spa/plugins/bluez5/bluez5-monitor.c | 23 +++++++++++++++++++++++ - spa/plugins/bluez5/defs.h | 1 + - 3 files changed, 25 insertions(+) - -diff --git a/spa/plugins/bluez5/bluez5-device.c b/spa/plugins/bluez5/bluez5-device.c -index 40a340c9..c4380e7a 100644 ---- a/spa/plugins/bluez5/bluez5-device.c -+++ b/spa/plugins/bluez5/bluez5-device.c -@@ -84,6 +84,7 @@ static void emit_node (struct impl *this, struct spa_bt_transport *t, const stru - info = SPA_DEVICE_OBJECT_INFO_INIT(); - info.type = SPA_TYPE_INTERFACE_Node; - info.factory = factory; -+ info.name = t->name; - info.change_mask = SPA_DEVICE_OBJECT_CHANGE_MASK_PROPS; - - /* Pass the transport pointer as a property */ -diff --git a/spa/plugins/bluez5/bluez5-monitor.c b/spa/plugins/bluez5/bluez5-monitor.c -index 2a243715..2914323b 100644 ---- a/spa/plugins/bluez5/bluez5-monitor.c -+++ b/spa/plugins/bluez5/bluez5-monitor.c -@@ -864,6 +864,26 @@ static void transport_free(struct spa_bt_transport *transport) - free(transport); - } - -+static void transport_update_name(struct spa_bt_transport *t) { -+ switch (t->profile) { -+ case SPA_BT_PROFILE_A2DP_SOURCE: -+ case SPA_BT_PROFILE_A2DP_SINK: -+ snprintf (t->name, 256, "bluez5.a2dp %s", t->device->name); -+ break; -+ case SPA_BT_PROFILE_HSP_HS: -+ case SPA_BT_PROFILE_HFP_HF: -+ snprintf (t->name, 256, "bluez5.headunit %s", t->device->name); -+ break; -+ case SPA_BT_PROFILE_HSP_AG: -+ case SPA_BT_PROFILE_HFP_AG: -+ snprintf (t->name, 256, "bluez5.gateway %s", t->device->name); -+ break; -+ default: -+ snprintf (t->name, 256, "bluez5.unknown %s", t->device->name); -+ break; -+ } -+} -+ - static int transport_update_props(struct spa_bt_transport *transport, - DBusMessageIter *props_iter, - DBusMessageIter *invalidated_iter) -@@ -893,9 +913,11 @@ static int transport_update_props(struct spa_bt_transport *transport, - switch (spa_bt_profile_from_uuid(value)) { - case SPA_BT_PROFILE_A2DP_SOURCE: - transport->profile = SPA_BT_PROFILE_A2DP_SINK; -+ transport_update_name(transport); - break; - case SPA_BT_PROFILE_A2DP_SINK: - transport->profile = SPA_BT_PROFILE_A2DP_SOURCE; -+ transport_update_name(transport); - break; - default: - spa_log_warn(monitor->log, "unknown profile %s", value); -@@ -1743,6 +1765,7 @@ static DBusHandlerResult profile_new_connection(DBusConnection *conn, DBusMessag - t->device = d; - spa_list_append(&t->device->transport_list, &t->device_link); - t->profile = profile; -+ transport_update_name(t); - - td = t->user_data; - td->rfcomm.func = rfcomm_event; -diff --git a/spa/plugins/bluez5/defs.h b/spa/plugins/bluez5/defs.h -index 7402cdf4..933a6413 100644 ---- a/spa/plugins/bluez5/defs.h -+++ b/spa/plugins/bluez5/defs.h -@@ -207,6 +207,7 @@ struct spa_bt_transport { - struct spa_bt_device *device; - struct spa_list device_link; - enum spa_bt_profile profile; -+ char name[256]; - enum spa_bt_transport_state state; - int codec; - void *configuration; --- -2.23.0.rc1 - diff --git a/meta-pipewire/recipes-multimedia/pipewire/pipewire/0020-a2dp-sink-check-if-transport-is-valid-before-releasi.patch b/meta-pipewire/recipes-multimedia/pipewire/pipewire/0020-a2dp-sink-check-if-transport-is-valid-before-releasi.patch deleted file mode 100644 index 746d2451..00000000 --- a/meta-pipewire/recipes-multimedia/pipewire/pipewire/0020-a2dp-sink-check-if-transport-is-valid-before-releasi.patch +++ /dev/null @@ -1,36 +0,0 @@ -From 411fd3d4e7b3f076a14c8eae7be976ce17b686ca Mon Sep 17 00:00:00 2001 -From: Julian Bouzas <julian.bouzas@collabora.com> -Date: Fri, 30 Aug 2019 08:45:11 -0400 -Subject: [PATCH] a2dp-sink: check if transport is valid before releasing it - -Upstream-Status: Pending ---- - spa/plugins/bluez5/a2dp-sink.c | 5 +++-- - 1 file changed, 3 insertions(+), 2 deletions(-) - -diff --git a/spa/plugins/bluez5/a2dp-sink.c b/spa/plugins/bluez5/a2dp-sink.c -index d6d9e7d6..041d75bb 100644 ---- a/spa/plugins/bluez5/a2dp-sink.c -+++ b/spa/plugins/bluez5/a2dp-sink.c -@@ -866,7 +866,7 @@ static int do_remove_source(struct spa_loop *loop, - - static int do_stop(struct impl *this) - { -- int res; -+ int res = 0; - - if (!this->started) - return 0; -@@ -877,7 +877,8 @@ static int do_stop(struct impl *this) - - this->started = false; - -- res = spa_bt_transport_release(this->transport); -+ if (this->transport) -+ res = spa_bt_transport_release(this->transport); - - return res; - } --- -2.23.0.rc1 - diff --git a/meta-pipewire/recipes-multimedia/pipewire/pipewire_git.bb b/meta-pipewire/recipes-multimedia/pipewire/pipewire_git.bb index 011354d8..54ec92bf 100644 --- a/meta-pipewire/recipes-multimedia/pipewire/pipewire_git.bb +++ b/meta-pipewire/recipes-multimedia/pipewire/pipewire_git.bb @@ -1,30 +1,23 @@ require pipewire.inc SRC_URI = "gitsm://github.com/PipeWire/pipewire;protocol=https;branch=work \ - file://0001-spa-include-install-missing-headers.patch \ - file://0002-extensions-implement-Endpoint-ClientEndpoint-interfa.patch \ - file://0003-pipewire-cli-add-support-for-printing-endpoint-info-.patch \ - file://0004-pipewire-cli-add-command-to-modify-endpoint-control-.patch \ - file://0005-arm-build-with-mno-unaligned-access.patch \ - file://0006-logger-print-timestamps-on-logged-messages.patch \ - file://0007-alsa-make-corrections-on-the-timeout-based-on-how-fa.patch \ - file://0008-audio-dsp-allow-mode-to-be-set-with-a-property.patch \ - file://0009-audioconvert-do-setup-internal-links-and-buffers-als.patch \ - file://0010-gst-Implement-new-pwaudio-src-sink-elements-based-on.patch \ - file://0011-gst-pwaudioringbuffer-make-the-buffer-size-sensitive.patch \ - file://0012-gst-pwaudioringbuffer-request-pause-play-on-the-appr.patch \ - file://0013-gst-pwaudioringbuffer-wait-only-for-STREAM_STATE_CON.patch \ - file://0014-gst-pwaudiosink-set-the-default-latency-time-buffer-.patch \ - file://0015-audioconvert-fmtconvert-assume-F32-on-the-other-port.patch \ - file://0016-a2dpsink-fix-infinite-loop-when-buffer-could-not-be-.patch \ - file://0017-bluez5-add-sco-sink-and-sco-src-nodes.patch \ - file://0018-device-add-name-field-in-spa_device_object_info.patch \ - file://0019-bluez-add-transport-name-and-use-it-when-emitting-no.patch \ - file://0020-a2dp-sink-check-if-transport-is-valid-before-releasi.patch \ - file://0021-gst-pwaudioringbuffer-set-node.latency-to-get-schedu.patch \ + file://0001-arm-build-with-mno-unaligned-access.patch \ + file://0002-logger-print-timestamps-on-logged-messages.patch \ + file://0003-alsa-make-corrections-on-the-timeout-based-on-how-fa.patch \ + file://0004-gst-Implement-new-pwaudio-src-sink-elements-based-on.patch \ + file://0005-gst-pwaudioringbuffer-request-pause-play-on-the-appr.patch \ + file://0006-gst-pwaudioringbuffer-wait-only-for-STREAM_STATE_CON.patch \ + file://0007-gst-pwaudiosink-set-the-default-latency-time-buffer-.patch \ + file://0008-gst-pwaudioringbuffer-set-node.latency-to-get-schedu.patch \ + file://0009-alsa-do-not-expose-non-interleaved-formats-since-the.patch \ + file://0010-bluez-monitor-fix-usage-of-pw_properties_setf-withou.patch \ + file://0011-meson-revert-version-check-to-require-meson-0.47-not.patch \ + file://0012-extensions-implement-new-session-manager-extension.patch \ + file://0013-pipewire-cli-add-support-for-printing-endpoint-info-.patch \ + file://0014-daemon-config-remote-load-module-session-manager-by-.patch \ " -SRCREV = "4be788962e60891237f1f018627bf709ae3981e6" +SRCREV = "d3c7acb137134bddff3bc8a8964600252d3fb674" PV = "0.2.90+git${SRCPV}+2" S = "${WORKDIR}/git" diff --git a/meta-pipewire/recipes-multimedia/wireplumber/wireplumber-board-config-agl/wireplumber.conf.in b/meta-pipewire/recipes-multimedia/wireplumber/wireplumber-board-config-agl/wireplumber.conf.in index fcd1b877..09bbfc3f 100644 --- a/meta-pipewire/recipes-multimedia/wireplumber/wireplumber-board-config-agl/wireplumber.conf.in +++ b/meta-pipewire/recipes-multimedia/wireplumber/wireplumber-board-config-agl/wireplumber.conf.in @@ -1,3 +1,13 @@ +# Register well-known SPA factories +# These do not need to exist on the system to be registered +add-spa-lib audio.convert* audioconvert/libspa-audioconvert +add-spa-lib api.alsa.* alsa/libspa-alsa +add-spa-lib api.v4l2.* v4l2/libspa-v4l2 +add-spa-lib api.bluez5.* bluez5/libspa-bluez5 + +# the client-device pipewire module is needed for libwireplumber-module-monitor +load-pipewire-module libpipewire-module-client-device + # Basic pipewire integration - do not remove load-module C libwireplumber-module-pipewire @@ -18,6 +28,16 @@ load-module C libwireplumber-module-mixer { "Communication", "Emergency"]> } +load-module C libwireplumber-module-monitor { + "factory": <"api.alsa.monitor">, + "flags": <["use-adapter"]> +} + +load-module C libwireplumber-module-monitor { + "factory": <"api.bluez5.monitor">, + "flags": <["local-nodes", "use-adapter"]> +} + # Monitors the ALSA devices that are discovered via udev # and creates softdsp-endopints for each one of them # The streams specified here are the ones that will be available for linking diff --git a/meta-pipewire/recipes-multimedia/wireplumber/wireplumber_git.bb b/meta-pipewire/recipes-multimedia/wireplumber/wireplumber_git.bb index 618ee142..604a1080 100644 --- a/meta-pipewire/recipes-multimedia/wireplumber/wireplumber_git.bb +++ b/meta-pipewire/recipes-multimedia/wireplumber/wireplumber_git.bb @@ -11,8 +11,8 @@ inherit meson pkgconfig gobject-introspection DEPENDS = "glib-2.0 glib-2.0-native pipewire" -SRC_URI = "git://gitlab.freedesktop.org/gkiagia/wireplumber;protocol=https;branch=0.1" -SRCREV = "68a44d5db3d5bbdd77f97a694178c2eef4d08705" +SRC_URI = "git://gitlab.freedesktop.org/gkiagia/wireplumber;protocol=https;branch=master" +SRCREV = "897d94c7ddad79774b32a2acda1c08fbd20c0697" PV = "0.1.1+git${SRCPV}" S = "${WORKDIR}/git" |