diff options
Diffstat (limited to 'meta-pipewire/recipes-multimedia/pipewire')
26 files changed, 256 insertions, 2801 deletions
diff --git a/meta-pipewire/recipes-multimedia/pipewire/pipewire-conf-agl/pipewire.conf b/meta-pipewire/recipes-multimedia/pipewire/pipewire-conf-agl/pipewire.conf deleted file mode 100644 index d09ee8ed..00000000 --- a/meta-pipewire/recipes-multimedia/pipewire/pipewire-conf-agl/pipewire.conf +++ /dev/null @@ -1,10 +0,0 @@ -# daemon config file for PipeWire version "0.2.9" -# distributed by Automotive Grade Linux -load-module libpipewire-module-protocol-native -load-module libpipewire-module-spa-monitor alsa/libspa-alsa alsa-monitor alsa -load-module libpipewire-module-client-node -load-module libpipewire-module-access -load-module libpipewire-module-audio-dsp -load-module libpipewire-module-link-factory -load-module libpipewire-module-endpoint -exec /usr/bin/wireplumber diff --git a/meta-pipewire/recipes-multimedia/pipewire/pipewire-conf-agl/pipewire.conf.in b/meta-pipewire/recipes-multimedia/pipewire/pipewire-conf-agl/pipewire.conf.in new file mode 100644 index 00000000..6c055bcf --- /dev/null +++ b/meta-pipewire/recipes-multimedia/pipewire/pipewire-conf-agl/pipewire.conf.in @@ -0,0 +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-node-factory +load-module libpipewire-module-client-node +load-module libpipewire-module-client-device +load-module libpipewire-module-access same-sec-label-mode=1 +load-module libpipewire-module-adapter +load-module libpipewire-module-link-factory +load-module libpipewire-module-session-manager +exec /usr/bin/wireplumber diff --git a/meta-pipewire/recipes-multimedia/pipewire/pipewire-conf-agl_git.bb b/meta-pipewire/recipes-multimedia/pipewire/pipewire-conf-agl_git.bb index 2bb76f9d..a28c6534 100644 --- a/meta-pipewire/recipes-multimedia/pipewire/pipewire-conf-agl_git.bb +++ b/meta-pipewire/recipes-multimedia/pipewire/pipewire-conf-agl_git.bb @@ -8,7 +8,7 @@ LICENSE = "MIT" LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/MIT;md5=0835ade698e0bcf8506ecda2f7b4f302" SRC_URI = " \ - file://pipewire.conf \ + file://pipewire.conf.in \ file://client.env \ file://server.env \ " @@ -17,8 +17,11 @@ do_configure[noexec] = "1" do_compile[noexec] = "1" do_install_append() { - # if we are distributing our own configuration file, - # replace the one installed by pipewire + # enable optional features in the config + BLUEZ5=${@bb.utils.contains('DISTRO_FEATURES', 'bluez5', '', '#', d)} + sed -e "s/#IF_BLUEZ5 /${BLUEZ5}/" ${WORKDIR}/pipewire.conf.in > ${WORKDIR}/pipewire.conf + + # install our custom config install -d ${D}/${sysconfdir}/pipewire/ install -m 0644 ${WORKDIR}/pipewire.conf ${D}${sysconfdir}/pipewire/pipewire.conf diff --git a/meta-pipewire/recipes-multimedia/pipewire/pipewire.inc b/meta-pipewire/recipes-multimedia/pipewire/pipewire.inc index e9046e8e..b3081ca4 100644 --- a/meta-pipewire/recipes-multimedia/pipewire/pipewire.inc +++ b/meta-pipewire/recipes-multimedia/pipewire/pipewire.inc @@ -19,7 +19,7 @@ DEPENDS = "dbus" PACKAGECONFIG ??= "\ ${@bb.utils.filter('DISTRO_FEATURES', 'systemd', d)} \ ${@bb.utils.filter('DISTRO_FEATURES', 'bluez5', d)} \ - alsa audioconvert \ + alsa audioconvert audiomixer \ pipewire-alsa \ gstreamer \ " @@ -31,11 +31,14 @@ 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[audiomixer] = "-Daudiomixer=true,-Daudiomixer=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/0001-meson-revert-version-check-to-require-meson-0.47-not.patch b/meta-pipewire/recipes-multimedia/pipewire/pipewire/0001-meson-revert-version-check-to-require-meson-0.47-not.patch new file mode 100644 index 00000000..4e7bb0d4 --- /dev/null +++ b/meta-pipewire/recipes-multimedia/pipewire/pipewire/0001-meson-revert-version-check-to-require-meson-0.47-not.patch @@ -0,0 +1,30 @@ +From 5a249321aa84cd74e3d83bcd555c85fba3cd682d 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 2734b0d2..c9da6b4d 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.24.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/0005-arm-build-with-mno-unaligned-access.patch b/meta-pipewire/recipes-multimedia/pipewire/pipewire/0002-arm-build-with-mno-unaligned-access.patch index a670e7ff..2077af63 100644 --- a/meta-pipewire/recipes-multimedia/pipewire/pipewire/0005-arm-build-with-mno-unaligned-access.patch +++ b/meta-pipewire/recipes-multimedia/pipewire/pipewire/0002-arm-build-with-mno-unaligned-access.patch @@ -1,19 +1,19 @@ -From 2016605938f02835c75928648e99b25f7248aa5b Mon Sep 17 00:00:00 2001 +From e4b81946baf2d8c08de87088c01a1d87ae4f03d9 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 Upstream-Status: Inappropriate [workaround] -See also https://github.com/PipeWire/pipewire/issues/161 +See also https://gitlab.freedesktop.org/pipewire/pipewire/issues/161 --- meson.build | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/meson.build b/meson.build -index 81303d27..f3cc6030 100644 +index c9da6b4d..5c121339 100644 --- a/meson.build +++ b/meson.build -@@ -50,6 +50,11 @@ if cc.get_id() == 'gcc' +@@ -52,6 +52,11 @@ if cc.get_id() == 'gcc' language : 'c') endif @@ -26,5 +26,5 @@ index 81303d27..f3cc6030 100644 sse2_args = '-msse2' ssse3_args = '-mssse3' -- -2.20.1 +2.24.0 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/0010-gst-Implement-new-pwaudio-src-sink-elements-based-on.patch b/meta-pipewire/recipes-multimedia/pipewire/pipewire/0003-gst-Implement-new-pwaudio-src-sink-elements-based-on.patch index 6b1a6441..b3eba21f 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/0003-gst-Implement-new-pwaudio-src-sink-elements-based-on.patch @@ -1,4 +1,4 @@ -From bbc875ec4268a88bf2465244e089b119011e7479 Mon Sep 17 00:00:00 2001 +From 1b1f884a165ed7b2147affbdddf85a641d4cf180 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 @@ -14,17 +14,18 @@ These are much more reliable elements to use for audio data. Both elements share a common ringbuffer that actually implements the pipewire integration. -Upstream-Status: Submitted [https://github.com/PipeWire/pipewire/pull/140] +Upstream-Status: Denied +See https://gitlab.freedesktop.org/pipewire/pipewire/merge_requests/140 --- src/gst/gstpipewire.c | 8 +- - src/gst/gstpwaudioringbuffer.c | 542 +++++++++++++++++++++++++++++++++ + src/gst/gstpwaudioringbuffer.c | 565 +++++++++++++++++++++++++++++++++ src/gst/gstpwaudioringbuffer.h | 83 +++++ - src/gst/gstpwaudiosink.c | 200 ++++++++++++ + src/gst/gstpwaudiosink.c | 207 ++++++++++++ src/gst/gstpwaudiosink.h | 48 +++ src/gst/gstpwaudiosrc.c | 200 ++++++++++++ src/gst/gstpwaudiosrc.h | 48 +++ src/gst/meson.build | 6 + - 8 files changed, 1134 insertions(+), 1 deletion(-) + 8 files changed, 1164 insertions(+), 1 deletion(-) create mode 100644 src/gst/gstpwaudioringbuffer.c create mode 100644 src/gst/gstpwaudioringbuffer.h create mode 100644 src/gst/gstpwaudiosink.c @@ -65,10 +66,10 @@ index 4040264b..68fd446f 100644 } diff --git a/src/gst/gstpwaudioringbuffer.c b/src/gst/gstpwaudioringbuffer.c new file mode 100644 -index 00000000..989b2cd7 +index 00000000..babf2d83 --- /dev/null +++ b/src/gst/gstpwaudioringbuffer.c -@@ -0,0 +1,542 @@ +@@ -0,0 +1,565 @@ +/* PipeWire + * + * Copyright © 2018 Wim Taymans @@ -273,24 +274,41 @@ index 00000000..989b2cd7 + enum pw_stream_state state, const char *error) +{ + GstPwAudioRingBuffer *self = GST_PW_AUDIO_RING_BUFFER (data); ++ GstMessage *msg; + + GST_DEBUG_OBJECT (self->elem, "got stream state: %s", + pw_stream_state_as_string (state)); + + switch (state) { ++ case PW_STREAM_STATE_ERROR: ++ GST_ELEMENT_ERROR (self->elem, RESOURCE, FAILED, ++ ("stream error: %s", error), (NULL)); ++ break; + case PW_STREAM_STATE_UNCONNECTED: + GST_ELEMENT_ERROR (self->elem, RESOURCE, FAILED, + ("stream disconnected unexpectedly"), (NULL)); + break; + case PW_STREAM_STATE_CONNECTING: -+ case PW_STREAM_STATE_CONFIGURE: -+ case PW_STREAM_STATE_READY: ++ break; + case PW_STREAM_STATE_PAUSED: -+ case PW_STREAM_STATE_STREAMING: ++ if (old == PW_STREAM_STATE_STREAMING) { ++ if (GST_STATE (self->elem) != GST_STATE_PAUSED && ++ GST_STATE_TARGET (self->elem) != GST_STATE_PAUSED) { ++ GST_DEBUG_OBJECT (self->elem, "requesting GST_STATE_PAUSED"); ++ msg = gst_message_new_request_state (GST_OBJECT (self->elem), ++ GST_STATE_PAUSED); ++ gst_element_post_message (self->elem, msg); ++ } ++ } + break; -+ case PW_STREAM_STATE_ERROR: -+ GST_ELEMENT_ERROR (self->elem, RESOURCE, FAILED, -+ ("stream error: %s", error), (NULL)); ++ case PW_STREAM_STATE_STREAMING: ++ if (GST_STATE (self->elem) != GST_STATE_PLAYING && ++ GST_STATE_TARGET (self->elem) != GST_STATE_PLAYING) { ++ GST_DEBUG_OBJECT (self->elem, "requesting GST_STATE_PLAYING"); ++ msg = gst_message_new_request_state (GST_OBJECT (self->elem), ++ GST_STATE_PLAYING); ++ gst_element_post_message (self->elem, msg); ++ } + break; + } + pw_thread_loop_signal (self->main_loop, FALSE); @@ -311,13 +329,16 @@ index 00000000..989b2cd7 +} + +static void -+on_stream_format_changed (void *data, const struct spa_pod *format) ++on_stream_param_changed (void *data, uint32_t id, const struct spa_pod *format) +{ + GstPwAudioRingBuffer *self = GST_PW_AUDIO_RING_BUFFER (data); + const struct spa_pod *params[1]; + struct spa_pod_builder b = { NULL }; + uint8_t buffer[512]; + ++ if (format == NULL || id != SPA_PARAM_Format) ++ return; ++ + spa_pod_builder_init (&b, buffer, sizeof (buffer)); + params[0] = spa_pod_builder_add_object (&b, + SPA_TYPE_OBJECT_ParamBuffers, SPA_PARAM_Buffers, @@ -328,7 +349,7 @@ index 00000000..989b2cd7 + SPA_PARAM_BUFFERS_align, SPA_POD_Int(16)); + + GST_DEBUG_OBJECT (self->elem, "doing finish format, buffer size:%d", self->segsize); -+ pw_stream_finish_format (self->stream, 0, params, 1); ++ pw_stream_update_params (self->stream, params, 1); +} + +static void @@ -353,7 +374,7 @@ index 00000000..989b2cd7 + + b = pw_stream_dequeue_buffer (self->stream); + if (!b) { -+ GST_WARNING_OBJECT (self->elem, "no pipewire buffer available"); ++ GST_INFO_OBJECT (self->elem, "no pipewire buffer available"); + return; + } + @@ -417,7 +438,7 @@ index 00000000..989b2cd7 +static const struct pw_stream_events stream_events = { + PW_VERSION_STREAM_EVENTS, + .state_changed = on_stream_state_changed, -+ .format_changed = on_stream_format_changed, ++ .param_changed = on_stream_param_changed, + .process = on_stream_process, +}; + @@ -454,11 +475,9 @@ index 00000000..989b2cd7 + + /* construct param & props objects */ + ++ props = pw_properties_new (NULL, NULL); + if (self->props->properties) { -+ props = pw_properties_new (NULL, NULL); + gst_structure_foreach (self->props->properties, copy_properties, props); -+ } else { -+ props = NULL; + } + + spa_pod_builder_init (&b, buffer, sizeof (buffer)); @@ -475,6 +494,11 @@ index 00000000..989b2cd7 + self->rate = GST_AUDIO_INFO_RATE (&spec->info); + self->segoffset = 0; + ++ pw_properties_setf(props, PW_KEY_NODE_LATENCY, "%u/%u", ++ self->segsize / self->bpf, self->rate); ++ GST_DEBUG_OBJECT (self->elem, "segsize:%u, bpf:%u, node.latency = %s", ++ self->segsize, self->bpf, pw_properties_get (props, PW_KEY_NODE_LATENCY)); ++ + /* connect stream */ + + pw_thread_loop_lock (self->main_loop); @@ -494,9 +518,9 @@ index 00000000..989b2cd7 + params, 1) < 0) + goto start_error; + -+ GST_DEBUG_OBJECT (self->elem, "waiting for stream READY"); ++ GST_DEBUG_OBJECT (self->elem, "waiting for stream CONFIGURE"); + -+ if (!wait_for_stream_state (self, PW_STREAM_STATE_READY)) ++ if (!wait_for_stream_state (self, PW_STREAM_STATE_PAUSED)) + goto start_error; + + pw_thread_loop_unlock (self->main_loop); @@ -702,10 +726,10 @@ index 00000000..f47f668a +#endif diff --git a/src/gst/gstpwaudiosink.c b/src/gst/gstpwaudiosink.c new file mode 100644 -index 00000000..6cb71385 +index 00000000..069996c3 --- /dev/null +++ b/src/gst/gstpwaudiosink.c -@@ -0,0 +1,200 @@ +@@ -0,0 +1,207 @@ +/* PipeWire + * + * Copyright © 2018 Wim Taymans @@ -765,6 +789,13 @@ index 00000000..6cb71385 +gst_pw_audio_sink_init (GstPwAudioSink * self) +{ + self->props.fd = -1; ++ ++ /* Bump the default buffer size up to 21.3 ms, which is the default on most ++ * sound cards, in hope to match the alsa buffer size on the pipewire server. ++ * This may not always happen, but it still sounds better than the 10ms ++ * default latency. This is temporary until we have a better mechanism to ++ * select the appropriate latency */ ++ GST_AUDIO_BASE_SINK (self)->latency_time = 21333; +} + +static void @@ -1245,5 +1276,5 @@ index ad0e0801..0e922347 100644 pipewire_gst_c_args = [ -- -2.20.1 +2.24.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/0003-pipewire-cli-add-support-for-printing-endpoint-info-.patch deleted file mode 100644 index a709abdf..00000000 --- a/meta-pipewire/recipes-multimedia/pipewire/pipewire/0003-pipewire-cli-add-support-for-printing-endpoint-info-.patch +++ /dev/null @@ -1,149 +0,0 @@ -From 0e9fe3cfb19c751655f16ce4b8c6100269f23ff2 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(-) - -diff --git a/src/tools/pipewire-cli.c b/src/tools/pipewire-cli.c -index 521739f6..9511db82 100644 ---- a/src/tools/pipewire-cli.c -+++ b/src/tools/pipewire-cli.c -@@ -38,6 +38,8 @@ - #include <pipewire/type.h> - #include <pipewire/permission.h> - -+#include <extensions/endpoint.h> -+ - static const char WHITESPACE[] = " \t"; - - struct remote_data; -@@ -176,8 +178,12 @@ static void print_params(struct spa_param_info *params, uint32_t n_params, char - return; - } - for (i = 0; i < n_params; i++) { -+ const struct spa_type_info *type_info = spa_type_param; -+ if (params[i].id >= PW_ENDPOINT_PARAM_EnumControl) -+ type_info = endpoint_param_type_info; -+ - fprintf(stdout, "%c\t %d (%s) %c%c\n", mark, params[i].id, -- spa_debug_type_find_name(spa_type_param, params[i].id), -+ spa_debug_type_find_name(type_info, params[i].id), - params[i].flags & SPA_PARAM_INFO_READ ? 'r' : '-', - params[i].flags & SPA_PARAM_INFO_WRITE ? 'w' : '-'); - } -@@ -641,6 +647,16 @@ static void info_device(struct proxy_data *pd) - info->change_mask = 0; - } - -+static void info_endpoint(struct proxy_data *pd) -+{ -+ struct pw_endpoint_info *info = pd->info; -+ -+ info_global(pd); -+ print_properties(info->props, MARK_CHANGE(1), true); -+ print_params(info->params, info->n_params, MARK_CHANGE(0), true); -+ info->change_mask = 0; -+} -+ - static void core_event_info(void *object, const struct pw_core_info *info) - { - struct proxy_data *pd = object; -@@ -708,6 +724,9 @@ static void event_param(void *object, int seq, uint32_t id, - - if (spa_pod_is_object_type(param, SPA_TYPE_OBJECT_Format)) - spa_debug_format(2, NULL, param); -+ else if (spa_pod_is_object_type(param, PW_ENDPOINT_OBJECT_ParamControl) || -+ spa_pod_is_object_type(param, PW_ENDPOINT_OBJECT_ParamStream)) -+ spa_debug_pod(2, endpoint_param_object_type_info, param); - else - spa_debug_pod(2, NULL, param); - } -@@ -842,6 +861,53 @@ static const struct pw_device_proxy_events device_events = { - .param = event_param - }; - -+static void endpoint_info_free(struct pw_endpoint_info *info) -+{ -+ free(info->params); -+ if (info->props) -+ pw_properties_free ((struct pw_properties *)info->props); -+ free(info); -+} -+ -+static void endpoint_event_info(void *object, -+ const struct pw_endpoint_info *update) -+{ -+ struct proxy_data *pd = object; -+ struct remote_data *rd = pd->rd; -+ struct pw_endpoint_info *info = pd->info; -+ -+ if (!info) { -+ info = pd->info = calloc(1, sizeof(*info)); -+ info->id = update->id; -+ } -+ if (update->change_mask & PW_ENDPOINT_CHANGE_MASK_PARAMS) { -+ info->n_params = update->n_params; -+ free(info->params); -+ info->params = malloc(info->n_params * sizeof(struct spa_param_info)); -+ memcpy(info->params, update->params, -+ info->n_params * sizeof(struct spa_param_info)); -+ } -+ if (update->change_mask & PW_ENDPOINT_CHANGE_MASK_PROPS) { -+ if (info->props) -+ pw_properties_free ((struct pw_properties *)info->props); -+ info->props = -+ (struct spa_dict *) pw_properties_new_dict (update->props); -+ } -+ -+ if (pd->global == NULL) -+ pd->global = pw_map_lookup(&rd->globals, info->id); -+ if (pd->global && pd->global->info_pending) { -+ info_endpoint(pd); -+ pd->global->info_pending = false; -+ } -+} -+ -+static const struct pw_endpoint_proxy_events endpoint_events = { -+ PW_VERSION_ENDPOINT_PROXY_EVENTS, -+ .info = endpoint_event_info, -+ .param = event_param -+}; -+ - static void - destroy_proxy (void *data) - { -@@ -928,6 +994,12 @@ static bool bind_global(struct remote_data *rd, struct global *global, char **er - destroy = (pw_destroy_t) pw_link_info_free; - info_func = info_link; - break; -+ case PW_TYPE_INTERFACE_Endpoint: -+ events = &endpoint_events; -+ client_version = PW_VERSION_ENDPOINT; -+ destroy = (pw_destroy_t) endpoint_info_free; -+ info_func = info_endpoint; -+ break; - default: - asprintf(error, "unsupported type %s", spa_debug_type_find_name(pw_type_info(), global->type)); - return false; -@@ -1201,6 +1273,10 @@ static bool do_enum_params(struct data *data, const char *cmd, char *args, char - pw_device_proxy_enum_params((struct pw_device_proxy*)global->proxy, 0, - param_id, 0, 0, NULL); - break; -+ case PW_TYPE_INTERFACE_Endpoint: -+ pw_endpoint_proxy_enum_params((struct pw_endpoint_proxy*)global->proxy, 0, -+ param_id, 0, 0, NULL); -+ break; - default: - asprintf(error, "enum-params not implemented on object %d", atoi(a[0])); - return false; --- -2.20.1 - diff --git a/meta-pipewire/recipes-multimedia/pipewire/pipewire/0004-audioconvert-always-assume-that-output-ports-are-NOT.patch b/meta-pipewire/recipes-multimedia/pipewire/pipewire/0004-audioconvert-always-assume-that-output-ports-are-NOT.patch new file mode 100644 index 00000000..beb87839 --- /dev/null +++ b/meta-pipewire/recipes-multimedia/pipewire/pipewire/0004-audioconvert-always-assume-that-output-ports-are-NOT.patch @@ -0,0 +1,35 @@ +From ce155eb0073fba84556782633f79bb7d03492c07 Mon Sep 17 00:00:00 2001 +From: George Kiagiadakis <george.kiagiadakis@collabora.com> +Date: Wed, 2 Oct 2019 21:40:34 +0300 +Subject: [PATCH] audioconvert: always assume that output ports are NOT monitor + ports + +Otherwise, when we setup audioconvert in merge+split mode, +it assumes that the splitter's ports are monitor ports and +belong to the merger. + +Upstream-Status: Inappropriate [workaround] +--- + spa/plugins/audioconvert/audioconvert.c | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/spa/plugins/audioconvert/audioconvert.c b/spa/plugins/audioconvert/audioconvert.c +index 74a62a35..72da37d1 100644 +--- a/spa/plugins/audioconvert/audioconvert.c ++++ b/spa/plugins/audioconvert/audioconvert.c +@@ -113,8 +113,12 @@ struct impl { + unsigned int add_listener:1; + }; + ++#if 0 + #define IS_MONITOR_PORT(this,dir,port_id) (dir == SPA_DIRECTION_OUTPUT && port_id > 0 && \ + this->mode[SPA_DIRECTION_INPUT] == SPA_PARAM_PORT_CONFIG_MODE_dsp) ++#else ++#define IS_MONITOR_PORT(this,dir,port_id) (false) ++#endif + + static void emit_node_info(struct impl *this, bool full) + { +-- +2.24.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/0005-module-access-add-same-sec-label-mode.patch b/meta-pipewire/recipes-multimedia/pipewire/pipewire/0005-module-access-add-same-sec-label-mode.patch new file mode 100644 index 00000000..07a1ec11 --- /dev/null +++ b/meta-pipewire/recipes-multimedia/pipewire/pipewire/0005-module-access-add-same-sec-label-mode.patch @@ -0,0 +1,94 @@ +From 19fad1a4fa8bdc4f02aac4e169e7ff9cab18bdcd Mon Sep 17 00:00:00 2001 +From: George Kiagiadakis <george.kiagiadakis@collabora.com> +Date: Tue, 19 Nov 2019 17:09:07 +0200 +Subject: [PATCH] module-access: add same-sec-label-mode + +This is a mode where the access module allows all clients that have +the same security label as the pipewire daemon, and every other +client is put on the restricted state. + +In systems that use SMACK security labels, such as AGL, this allows +the session manager (which is spawned by pipewire, inheriting the +same smack label) to have full access to all objects, while every +other client is restricted and the session manager must decide +what to do with it + +Note that while this option is configurable, there is no loss of +security if this option is not set in the configuration. Clients +that don't have the same security context will be considered to +be flatpak clients because pipewire will not be able to open +/proc/pid/cmdline. This however results in some unwanted error +messages that may be confusing. + +Upstream-Status: Inappropriate [agl/smack specific] +--- + src/modules/module-access.c | 45 ++++++++++++++++++++++++++++++++++++- + 1 file changed, 44 insertions(+), 1 deletion(-) + +diff --git a/src/modules/module-access.c b/src/modules/module-access.c +index 09dafa43..f75306d9 100644 +--- a/src/modules/module-access.c ++++ b/src/modules/module-access.c +@@ -50,6 +50,30 @@ struct impl { + struct spa_hook module_listener; + }; + ++static int check_seclabel(const char *str) ++{ ++ char attr[1024]; ++ int fd, len; ++ ++ fd = open("/proc/self/attr/current", O_RDONLY); ++ if (fd < 0) ++ return -errno; ++ ++ if ((len = read(fd, attr, 1024)) <= 0) { ++ close(fd); ++ return -EIO; ++ } ++ attr[len] = '\0'; ++ ++ if (strcmp(attr, str) == 0) { ++ close(fd); ++ return 1; ++ } ++ ++ close(fd); ++ return 0; ++} ++ + static int check_cmdline(struct pw_client *client, int pid, const char *str) + { + char path[2048]; +@@ -121,8 +145,27 @@ core_check_access(void *data, struct pw_client *client) + const char *str; + int pid, res; + ++ props = pw_client_get_properties(client); ++ ++ if (impl->properties && ++ (str = pw_properties_get(impl->properties, "same-sec-label-mode")) != NULL && ++ strcmp(str, "1") == 0) { ++ if (props && (str = pw_properties_get(props, PW_KEY_SEC_LABEL)) != NULL) { ++ res = check_seclabel(str); ++ if (res == 1) ++ goto granted; ++ else if (res < 0) ++ pw_log_warn("module %p: client %p seclabel check failed: %s", ++ impl, client, spa_strerror(res)); ++ } ++ pw_log_debug("module %p: seclabel restricted client %p added", ++ impl, client); ++ items[0] = SPA_DICT_ITEM_INIT(PW_KEY_ACCESS, "restricted"); ++ goto wait_permissions; ++ } ++ + pid = -EINVAL; +- if ((props = pw_client_get_properties(client)) != NULL) { ++ if (props != NULL) { + if ((str = pw_properties_get(props, PW_KEY_SEC_PID)) != NULL) + pid = atoi(str); + } +-- +2.24.0 + diff --git a/meta-pipewire/recipes-multimedia/pipewire/pipewire/0006-logger-print-timestamps-on-logged-messages.patch b/meta-pipewire/recipes-multimedia/pipewire/pipewire/0006-logger-print-timestamps-on-logged-messages.patch deleted file mode 100644 index 4d9117f0..00000000 --- a/meta-pipewire/recipes-multimedia/pipewire/pipewire/0006-logger-print-timestamps-on-logged-messages.patch +++ /dev/null @@ -1,52 +0,0 @@ -From 352c58357e5922b21d664c1f5a0b89a74f864f41 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 - -Timestamps have usec precision and the seconds are limited -to 9 digits. Usually what matters in these messages is to spot -delays between printouts and not really what is the absolute -time of the system. - -Upstream-Status: Submitted [https://github.com/PipeWire/pipewire/pull/164] ---- - spa/plugins/support/logger.c | 9 +++++++-- - 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 ---- a/spa/plugins/support/logger.c -+++ b/spa/plugins/support/logger.c -@@ -27,6 +27,7 @@ - #include <string.h> - #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, - const char *prefix = "", *suffix = ""; - int size; - bool do_trace; -+ struct timespec now; -+ -+ clock_gettime(CLOCK_MONOTONIC_RAW, &now); - - if ((do_trace = (level == SPA_LOG_LEVEL_TRACE && impl->have_source))) - level++; -@@ -86,8 +90,9 @@ impl_log_logv(struct spa_log *log, - } - - vsnprintf(text, sizeof(text), fmt, args); -- size = snprintf(location, sizeof(location), "%s[%s][%s:%i %s()] %s%s\n", -- prefix, levels[level], strrchr(file, '/') + 1, line, func, text, suffix); -+ size = snprintf(location, sizeof(location), "%s[%s][%09lu.%06lu][%s:%i %s()] %s%s\n", -+ prefix, levels[level], now.tv_sec & 0x1FFFFFFF, now.tv_nsec / 1000, -+ strrchr(file, '/') + 1, line, func, text, suffix); - - if (SPA_UNLIKELY(do_trace)) { - uint32_t index; --- -2.20.1 - 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/0007-alsa-make-corrections-on-the-timeout-based-on-how-fa.patch deleted file mode 100644 index 86495014..00000000 --- a/meta-pipewire/recipes-multimedia/pipewire/pipewire/0007-alsa-make-corrections-on-the-timeout-based-on-how-fa.patch +++ /dev/null @@ -1,130 +0,0 @@ -From 05a3a926df4906cdf60f7978843c637bbf37714a 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 - consumes samples - -This feels a bit hacky, but it actually makes huge difference when pipewire is -running in qemu. - -The idea is that it keeps track of how much samples are in the device -(fill level) and calculates how many are consumed when a timeout occurs. -Then it converts that into a time based on the sample rate and compares it to -the system clock time that elapsed since the last write to the device. -The division between the two gives a rate (drift) that can be used to shorten -the timeout window. - -So for instance, if the timeout window was 21.3 ms, but the device actually -consumed an equivalent of 28 ms in samples, the drift will be 21.3/28 = 0.76 -and the next timeout window will be approximately 21.3 * 0.76 = 16.1 ms - -To avoid making things worse, the drift is clamped between 0.6 and 1.0. -Min 0.6 was arbitrarily chosen, but sometimes alsa does report strange numbers, -causing the drift to be very low, which in turn causes an early wakeup. -Max 1.0 basically means that we don't care if the device is consuming samples -slower. In that case, the early wakeup mechanism will throttle pipewire. - -Fixes #163 - -Upstream-Status: Submitted [https://github.com/PipeWire/pipewire/pull/166] ---- - spa/plugins/alsa/alsa-utils.c | 31 ++++++++++++++++++++++++++----- - spa/plugins/alsa/alsa-utils.h | 2 ++ - 2 files changed, 28 insertions(+), 5 deletions(-) - -diff --git a/spa/plugins/alsa/alsa-utils.c b/spa/plugins/alsa/alsa-utils.c -index 87582c1c..872969bf 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) - { -- double err, corr; -+ double err, corr, drift; -+ snd_pcm_sframes_t consumed; -+ -+ consumed = state->fill_level - delay; -+ if (state->alsa_started && consumed > 0) { -+ double sysclk_diff = nsec - state->last_time; -+ double devclk_diff = ((double) consumed) * 1e9 / state->rate; -+ drift = sysclk_diff / devclk_diff; -+ drift = SPA_CLAMP(drift, 0.6, 1.0); -+ -+ spa_log_trace_fp(state->log, "cons:%ld sclk:%f dclk:%f drift:%f", -+ consumed, sysclk_diff, devclk_diff, drift); -+ } else { -+ drift = 1.0; -+ } - - 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; - } - -- spa_log_trace_fp(state->log, "slave:%d %"PRIu64" %f %ld %f %f %d", slave, nsec, -- corr, delay, err, state->threshold * corr, -+ spa_log_trace_fp(state->log, "slave:%d %"PRIu64" %f %ld %f %f %f %d", slave, nsec, -+ corr, delay, err, state->threshold * corr, drift, - state->threshold); - -- state->next_time += state->threshold / corr * 1e9 / state->rate; -+ state->next_time += state->threshold / corr * drift * 1e9 / state->rate; - state->last_threshold = state->threshold; - - return 0; -@@ -783,6 +797,10 @@ again: - goto again; - - state->sample_count += total_written; -+ state->fill_level += total_written; -+ -+ clock_gettime(CLOCK_MONOTONIC, &state->now); -+ state->last_time = SPA_TIMESPEC_TO_NSEC (&state->now); - - 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) - return res; - -+ state->fill_level = delay; -+ - 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) - - state->slaved = is_slaved(state); - 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 ---- a/spa/plugins/alsa/alsa-utils.h -+++ b/spa/plugins/alsa/alsa-utils.h -@@ -134,7 +134,9 @@ struct state { - int64_t sample_time; - uint64_t next_time; - uint64_t base_time; -+ uint64_t last_time; - -+ snd_pcm_uframes_t fill_level; - uint64_t underrun; - double safety; - --- -2.20.1 - 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/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/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/0012-gst-pwaudioringbuffer-request-pause-play-on-the-appr.patch b/meta-pipewire/recipes-multimedia/pipewire/pipewire/0012-gst-pwaudioringbuffer-request-pause-play-on-the-appr.patch deleted file mode 100644 index 3680cc35..00000000 --- a/meta-pipewire/recipes-multimedia/pipewire/pipewire/0012-gst-pwaudioringbuffer-request-pause-play-on-the-appr.patch +++ /dev/null @@ -1,76 +0,0 @@ -From 1eb1e3a839f97ad4aa43c289f702c587a068a333 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 - stream state changes - -This allows the client to properly go to PAUSED when the session manager -unlinks the stream and go again to PLAYING when the sm re-links it. -This allows the session manager to implement policies without letting -the client pipeline freeze (in the absence of a running audio clock) -when it is unlinked. Note that in case the client doesn't handle the -request, there is still no issue. Like in pulseaudio, the clock just -freezes, so the pipeline stops progressing. - -This is similar to the pulseaudio cork/uncork mechanism. - -Upstream-Status: Submitted [https://github.com/PipeWire/pipewire/pull/140] ---- - src/gst/gstpwaudioringbuffer.c | 27 +++++++++++++++++++++++---- - 1 file changed, 23 insertions(+), 4 deletions(-) - -diff --git a/src/gst/gstpwaudioringbuffer.c b/src/gst/gstpwaudioringbuffer.c -index 181304e8..04259927 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, - enum pw_stream_state state, const char *error) - { - GstPwAudioRingBuffer *self = GST_PW_AUDIO_RING_BUFFER (data); -+ GstMessage *msg; - - GST_DEBUG_OBJECT (self->elem, "got stream state: %s", - pw_stream_state_as_string (state)); - - switch (state) { -+ case PW_STREAM_STATE_ERROR: -+ GST_ELEMENT_ERROR (self->elem, RESOURCE, FAILED, -+ ("stream error: %s", error), (NULL)); -+ break; - case PW_STREAM_STATE_UNCONNECTED: - GST_ELEMENT_ERROR (self->elem, RESOURCE, FAILED, - ("stream disconnected unexpectedly"), (NULL)); -@@ -214,12 +219,26 @@ on_stream_state_changed (void *data, enum pw_stream_state old, - case PW_STREAM_STATE_CONNECTING: - case PW_STREAM_STATE_CONFIGURE: - case PW_STREAM_STATE_READY: -+ break; - case PW_STREAM_STATE_PAUSED: -- case PW_STREAM_STATE_STREAMING: -+ if (old == PW_STREAM_STATE_STREAMING) { -+ if (GST_STATE (self->elem) != GST_STATE_PAUSED && -+ GST_STATE_TARGET (self->elem) != GST_STATE_PAUSED) { -+ GST_DEBUG_OBJECT (self->elem, "requesting GST_STATE_PAUSED"); -+ msg = gst_message_new_request_state (GST_OBJECT (self->elem), -+ GST_STATE_PAUSED); -+ gst_element_post_message (self->elem, msg); -+ } -+ } - break; -- case PW_STREAM_STATE_ERROR: -- GST_ELEMENT_ERROR (self->elem, RESOURCE, FAILED, -- ("stream error: %s", error), (NULL)); -+ case PW_STREAM_STATE_STREAMING: -+ if (GST_STATE (self->elem) != GST_STATE_PLAYING && -+ GST_STATE_TARGET (self->elem) != GST_STATE_PLAYING) { -+ GST_DEBUG_OBJECT (self->elem, "requesting GST_STATE_PLAYING"); -+ msg = gst_message_new_request_state (GST_OBJECT (self->elem), -+ GST_STATE_PLAYING); -+ gst_element_post_message (self->elem, msg); -+ } - break; - } - pw_thread_loop_signal (self->main_loop, FALSE); --- -2.20.1 - 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/0013-gst-pwaudioringbuffer-wait-only-for-STREAM_STATE_CON.patch deleted file mode 100644 index 539e3a5e..00000000 --- a/meta-pipewire/recipes-multimedia/pipewire/pipewire/0013-gst-pwaudioringbuffer-wait-only-for-STREAM_STATE_CON.patch +++ /dev/null @@ -1,35 +0,0 @@ -From 1b2bf0f435f2912c32fbd7a6118ed9bfb41f031c 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 - when starting - -The CONFIGURE state is reached when the pw_client_node is exported, -while the READY state requires the session manager to try and link -the stream. If the SM does not want to link the stream due to policy, -the client should not hang there forever. - -Upstream-Status: Submitted [https://github.com/PipeWire/pipewire/pull/140] ---- - src/gst/gstpwaudioringbuffer.c | 4 ++-- - 1 file changed, 2 insertions(+), 2 deletions(-) - -diff --git a/src/gst/gstpwaudioringbuffer.c b/src/gst/gstpwaudioringbuffer.c -index 04259927..b92b5feb 100644 ---- a/src/gst/gstpwaudioringbuffer.c -+++ b/src/gst/gstpwaudioringbuffer.c -@@ -444,9 +444,9 @@ gst_pw_audio_ring_buffer_acquire (GstAudioRingBuffer *buf, - params, 1) < 0) - goto start_error; - -- GST_DEBUG_OBJECT (self->elem, "waiting for stream READY"); -+ GST_DEBUG_OBJECT (self->elem, "waiting for stream CONFIGURE"); - -- if (!wait_for_stream_state (self, PW_STREAM_STATE_READY)) -+ if (!wait_for_stream_state (self, PW_STREAM_STATE_CONFIGURE)) - goto start_error; - - pw_thread_loop_unlock (self->main_loop); --- -2.20.1 - 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/0014-gst-pwaudiosink-set-the-default-latency-time-buffer-.patch deleted file mode 100644 index 6f15b7f7..00000000 --- a/meta-pipewire/recipes-multimedia/pipewire/pipewire/0014-gst-pwaudiosink-set-the-default-latency-time-buffer-.patch +++ /dev/null @@ -1,37 +0,0 @@ -From 460ce06c9cc6fd7b0106e0ce8a265bbeff4ae406 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) - to be 21.3ms - -This is to solve underrun issues that seem to appear with the default -10ms latency that GstBaseAudioSink has. -Hopefully in the future we will have a better mechanism to pick -the appropriate latency instead of hardcoding it here. - -Upstream-Status: Submitted [https://github.com/PipeWire/pipewire/pull/140] ---- - src/gst/gstpwaudiosink.c | 7 +++++++ - 1 file changed, 7 insertions(+) - -diff --git a/src/gst/gstpwaudiosink.c b/src/gst/gstpwaudiosink.c -index 6cb71385..069996c3 100644 ---- a/src/gst/gstpwaudiosink.c -+++ b/src/gst/gstpwaudiosink.c -@@ -57,6 +57,13 @@ static void - gst_pw_audio_sink_init (GstPwAudioSink * self) - { - self->props.fd = -1; -+ -+ /* Bump the default buffer size up to 21.3 ms, which is the default on most -+ * sound cards, in hope to match the alsa buffer size on the pipewire server. -+ * This may not always happen, but it still sounds better than the 10ms -+ * default latency. This is temporary until we have a better mechanism to -+ * select the appropriate latency */ -+ GST_AUDIO_BASE_SINK (self)->latency_time = 21333; - } - - static void --- -2.20.1 - 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-gst-pwaudioringbuffer-set-node.latency-to-get-schedu.patch b/meta-pipewire/recipes-multimedia/pipewire/pipewire/0016-gst-pwaudioringbuffer-set-node.latency-to-get-schedu.patch deleted file mode 100644 index b97e21ff..00000000 --- a/meta-pipewire/recipes-multimedia/pipewire/pipewire/0016-gst-pwaudioringbuffer-set-node.latency-to-get-schedu.patch +++ /dev/null @@ -1,41 +0,0 @@ -From 3af64cf4e1d33c33a9757c0f30c7de1068202540 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 - correctly in capture mode - -Upstream-Status: Pending ---- - src/gst/gstpwaudioringbuffer.c | 7 ++++--- - 1 file changed, 4 insertions(+), 3 deletions(-) - -diff --git a/src/gst/gstpwaudioringbuffer.c b/src/gst/gstpwaudioringbuffer.c -index b92b5feb..2314dd77 100644 ---- a/src/gst/gstpwaudioringbuffer.c -+++ b/src/gst/gstpwaudioringbuffer.c -@@ -403,11 +403,9 @@ gst_pw_audio_ring_buffer_acquire (GstAudioRingBuffer *buf, - - /* construct param & props objects */ - -+ props = pw_properties_new (NULL, NULL); - if (self->props->properties) { -- props = pw_properties_new (NULL, NULL); - gst_structure_foreach (self->props->properties, copy_properties, props); -- } else { -- props = NULL; - } - - 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); - self->segoffset = 0; - -+ pw_properties_setf(props, "node.latency", "%u/%u", -+ self->segsize / self->bpf, self->rate); -+ - /* connect stream */ - - pw_thread_loop_lock (self->main_loop); --- -2.23.0 - diff --git a/meta-pipewire/recipes-multimedia/pipewire/pipewire/0017-connection-move-remaining-data-and-fds.patch b/meta-pipewire/recipes-multimedia/pipewire/pipewire/0017-connection-move-remaining-data-and-fds.patch deleted file mode 100644 index be5ac5ea..00000000 --- a/meta-pipewire/recipes-multimedia/pipewire/pipewire/0017-connection-move-remaining-data-and-fds.patch +++ /dev/null @@ -1,61 +0,0 @@ -From 75247f77eae2c473b18cf8d7e117216f73d2e127 Mon Sep 17 00:00:00 2001 -From: Wim Taymans <wtaymans@redhat.com> -Date: Tue, 1 Oct 2019 10:43:48 +0200 -Subject: [PATCH] connection: move remaining data and fds - -If we can't send all of the data, move the remaining data to the -start of the buffer so that we can send it again later. - -See #111 - -Upstream-Status: Backport [3d48ba8394396fc8d8cadb1bff3514217ddd70e6] ---- - .../module-protocol-native/connection.c | 23 +++++++++++-------- - 1 file changed, 14 insertions(+), 9 deletions(-) - -diff --git a/src/modules/module-protocol-native/connection.c b/src/modules/module-protocol-native/connection.c -index dbb6a3cf..cb592e41 100644 ---- a/src/modules/module-protocol-native/connection.c -+++ b/src/modules/module-protocol-native/connection.c -@@ -491,8 +491,12 @@ int pw_protocol_native_connection_flush(struct pw_protocol_native_connection *co - if (sent < 0) { - if (errno == EINTR) - continue; -- else -- goto send_error; -+ else { -+ res = -errno; -+ pw_log_error("could not sendmsg on fd:%d n_fds:%d: %s", -+ conn->fd, n_fds, spa_strerror(res)); -+ goto exit; -+ } - } - break; - } -@@ -504,15 +508,16 @@ int pw_protocol_native_connection_flush(struct pw_protocol_native_connection *co - n_fds -= outfds; - fds += outfds; - } -- buf->buffer_size = size; -- buf->n_fds = n_fds; - -- return 0; -+ res = 0; - -- /* ERRORS */ -- send_error: -- res = -errno; -- pw_log_error("could not sendmsg: %s", strerror(errno)); -+exit: -+ if (size > 0) -+ memmove(buf->buffer_data, data, size); -+ buf->buffer_size = size; -+ if (n_fds > 0) -+ memmove(buf->fds, fds, n_fds * sizeof(int)); -+ buf->n_fds = n_fds; - return res; - } - --- -2.23.0 - diff --git a/meta-pipewire/recipes-multimedia/pipewire/pipewire/0018-protocol-improve-flushing.patch b/meta-pipewire/recipes-multimedia/pipewire/pipewire/0018-protocol-improve-flushing.patch deleted file mode 100644 index e027765e..00000000 --- a/meta-pipewire/recipes-multimedia/pipewire/pipewire/0018-protocol-improve-flushing.patch +++ /dev/null @@ -1,222 +0,0 @@ -From 7e885c029a6cc66ce1881f6f2c50d5f76a7d3372 Mon Sep 17 00:00:00 2001 -From: Wim Taymans <wtaymans@redhat.com> -Date: Tue, 1 Oct 2019 12:53:56 +0200 -Subject: [PATCH] protocol: improve flushing - -Use the IO_OUT flag to schedule flushing instead of a flush_event. - -Handle EGAIN and wait for IO_OUT to try again. - -Fixes #111 - -Upstream-Status: Backport [cc8e992cd155b4f19312a5036c7b744fc547410f] ---- - src/modules/module-protocol-native.c | 89 +++++++++++++------ - .../module-protocol-native/connection.c | 2 - - 2 files changed, 62 insertions(+), 29 deletions(-) - -diff --git a/src/modules/module-protocol-native.c b/src/modules/module-protocol-native.c -index 411bad6c..b7cd3140 100644 ---- a/src/modules/module-protocol-native.c -+++ b/src/modules/module-protocol-native.c -@@ -83,8 +83,7 @@ struct client { - struct spa_hook conn_listener; - - bool disconnecting; -- bool flush_signaled; -- struct spa_source *flush_event; -+ bool flushing; - }; - - struct server { -@@ -106,6 +105,7 @@ struct client_data { - struct spa_source *source; - struct pw_protocol_native_connection *connection; - bool busy; -+ bool need_flush; - }; - - static void -@@ -194,12 +194,14 @@ client_busy_changed(void *data, bool busy) - { - struct client_data *c = data; - struct pw_client *client = c->client; -- enum spa_io mask = SPA_IO_ERR | SPA_IO_HUP; -+ uint32_t mask = c->source->mask; - - c->busy = busy; - -- if (!busy) -- mask |= SPA_IO_IN; -+ if (busy) -+ SPA_FLAG_UNSET(mask, SPA_IO_IN); -+ else -+ SPA_FLAG_SET(mask, SPA_IO_IN); - - pw_log_debug("protocol-native %p: busy changed %d", client->protocol, busy); - pw_loop_update_io(client->core->main_loop, c->source, mask); -@@ -214,13 +216,32 @@ connection_data(void *data, int fd, enum spa_io mask) - { - struct client_data *this = data; - struct pw_client *client = this->client; -+ int res; - -- if (mask & (SPA_IO_ERR | SPA_IO_HUP)) { -+ if (mask & SPA_IO_HUP) { - pw_log_info("protocol-native %p: client %p disconnected", client->protocol, client); - pw_client_destroy(client); - return; - } -- -+ if (mask & SPA_IO_ERR) { -+ pw_log_error("protocol-native %p: client %p error", client->protocol, client); -+ pw_client_destroy(client); -+ return; -+ } -+ if (mask & SPA_IO_OUT) { -+ res = pw_protocol_native_connection_flush(this->connection); -+ if (res >= 0) { -+ int mask = this->source->mask; -+ SPA_FLAG_UNSET(mask, SPA_IO_OUT); -+ pw_loop_update_io(client->protocol->core->main_loop, -+ this->source, mask); -+ } else if (res != EAGAIN) { -+ pw_log_error("client %p: could not flush: %s", -+ client, spa_strerror(res)); -+ pw_client_destroy(client); -+ return; -+ } -+ } - if (mask & SPA_IO_IN) - process_messages(this); - } -@@ -288,7 +309,8 @@ static struct pw_client *client_new(struct server *s, int fd) - - this->client = client; - this->source = pw_loop_add_io(pw_core_get_main_loop(core), -- fd, SPA_IO_ERR | SPA_IO_HUP, true, connection_data, this); -+ fd, SPA_IO_ERR | SPA_IO_HUP, true, -+ connection_data, this); - if (this->source == NULL) - goto cleanup_client; - -@@ -396,7 +418,7 @@ socket_data(void *data, int fd, enum spa_io mask) - - if (!client->busy) - pw_loop_update_io(client->protocol->core->main_loop, -- c->source, SPA_IO_IN | SPA_IO_ERR | SPA_IO_HUP); -+ c->source, c->source->mask | SPA_IO_IN); - } - - static bool add_socket(struct pw_protocol *protocol, struct server *s) -@@ -479,6 +501,17 @@ on_remote_data(void *data, int fd, enum spa_io mask) - res = -EPIPE; - goto error; - } -+ if (mask & SPA_IO_OUT) { -+ res = pw_protocol_native_connection_flush(conn); -+ if (res >= 0) { -+ int mask = impl->source->mask; -+ SPA_FLAG_UNSET(mask, SPA_IO_OUT); -+ pw_loop_update_io(core->main_loop, -+ impl->source, mask); -+ impl->flushing = false; -+ } else if (res != EAGAIN) -+ goto error; -+ } - - if (mask & SPA_IO_IN) { - const struct pw_protocol_native_message *msg; -@@ -545,23 +578,17 @@ error: - } - - --static void do_flush_event(void *data, uint64_t count) --{ -- struct client *impl = data; -- impl->flush_signaled = false; -- if (impl->connection) -- if (pw_protocol_native_connection_flush(impl->connection) < 0) -- impl->this.disconnect(&impl->this); --} -- - static void on_need_flush(void *data) - { - struct client *impl = data; - struct pw_remote *remote = impl->this.remote; - -- if (!impl->flush_signaled) { -- impl->flush_signaled = true; -- pw_loop_signal_event(remote->core->main_loop, impl->flush_event); -+ if (!impl->flushing) { -+ int mask = impl->source->mask; -+ impl->flushing = true; -+ SPA_FLAG_SET(mask, SPA_IO_OUT); -+ pw_loop_update_io(remote->core->main_loop, -+ impl->source, mask); - } - } - -@@ -619,12 +646,9 @@ static void impl_disconnect(struct pw_protocol_client *client) - static void impl_destroy(struct pw_protocol_client *client) - { - struct client *impl = SPA_CONTAINER_OF(client, struct client, this); -- struct pw_remote *remote = client->remote; - - impl_disconnect(client); - -- pw_loop_destroy_source(remote->core->main_loop, impl->flush_event); -- - if (impl->properties) - pw_properties_free(impl->properties); - -@@ -665,8 +689,6 @@ impl_new_client(struct pw_protocol *protocol, - this->disconnect = impl_disconnect; - this->destroy = impl_destroy; - -- impl->flush_event = pw_loop_add_event(remote->core->main_loop, do_flush_event, impl); -- - spa_list_append(&protocol->client_list, &this->link); - - return this; -@@ -701,10 +723,23 @@ static void on_before_hook(void *_data) - struct pw_protocol_server *this = &server->this; - struct pw_client *client, *tmp; - struct client_data *data; -+ int res; - - spa_list_for_each_safe(client, tmp, &this->client_list, protocol_link) { - data = client->user_data; -- pw_protocol_native_connection_flush(data->connection); -+ -+ res = pw_protocol_native_connection_flush(data->connection); -+ if (res == -EAGAIN) { -+ int mask = data->source->mask; -+ SPA_FLAG_SET(mask, SPA_IO_OUT); -+ pw_loop_update_io(client->protocol->core->main_loop, -+ data->source, mask); -+ } else if (res < 0) { -+ pw_log_warn("client %p: could not flush: %s", -+ data->client, spa_strerror(res)); -+ pw_client_destroy(client); -+ } -+ - } - } - -diff --git a/src/modules/module-protocol-native/connection.c b/src/modules/module-protocol-native/connection.c -index cb592e41..8b9e919d 100644 ---- a/src/modules/module-protocol-native/connection.c -+++ b/src/modules/module-protocol-native/connection.c -@@ -493,8 +493,6 @@ int pw_protocol_native_connection_flush(struct pw_protocol_native_connection *co - continue; - else { - res = -errno; -- pw_log_error("could not sendmsg on fd:%d n_fds:%d: %s", -- conn->fd, n_fds, spa_strerror(res)); - goto exit; - } - } --- -2.23.0 - diff --git a/meta-pipewire/recipes-multimedia/pipewire/pipewire_git.bb b/meta-pipewire/recipes-multimedia/pipewire/pipewire_git.bb index 823da421..68eb3d1e 100644 --- a/meta-pipewire/recipes-multimedia/pipewire/pipewire_git.bb +++ b/meta-pipewire/recipes-multimedia/pipewire/pipewire_git.bb @@ -1,29 +1,16 @@ 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-gst-pwaudioringbuffer-set-node.latency-to-get-schedu.patch \ - file://0017-connection-move-remaining-data-and-fds.patch \ - file://0018-protocol-improve-flushing.patch \ +SRC_URI = "git://gitlab.freedesktop.org/pipewire/pipewire.git;protocol=https;branch=master \ + file://0001-meson-revert-version-check-to-require-meson-0.47-not.patch \ + file://0002-arm-build-with-mno-unaligned-access.patch \ + file://0003-gst-Implement-new-pwaudio-src-sink-elements-based-on.patch \ + file://0004-audioconvert-always-assume-that-output-ports-are-NOT.patch \ + file://0005-module-access-add-same-sec-label-mode.patch \ " -SRCREV = "4be788962e60891237f1f018627bf709ae3981e6" +SRCREV = "b0932e687fc47e0872ca291531f2291d99042d70" -PV = "0.2.90+git${SRCPV}+3" +PV = "0.2.91+git${SRCPV}+1" S = "${WORKDIR}/git" RDEPENDS_${PN} += "virtual/pipewire-sessionmanager virtual/pipewire-config" |