diff --git a/NEWS b/NEWS index b730ae372b1f955d4bf3fec9dc552cf16b491f1a..e6ad9e4083876695720fb77a1ff417cf7f98f893 100644 --- a/NEWS +++ b/NEWS @@ -1,3 +1,72 @@ +# PipeWire 1.0.3 (2024-02-02) + +This is a quick bugfix release that is API and ABI compatible with previous +1.0.x releases. + +## Highlights + - Fix ALSA version check. This should allow the alsa plugin to work again. + - Some small fixes and improvements. + +## PipeWire + - Escape @DEFAULT_SINK@ in the conf files. + +## Modules + - Improve logging in module-pipe-tunnel. + +## SPA + - Always recheck rate matching in ALSA when moving drivers. This fixes a + potential issue where the adaptive resampler would not be activated in + some cases. + +## ALSA + - Fix version check. This should allow the alsa plugin to work again + with version 1.0.2. + +Older versions: + + +# PipeWire 1.0.2 (2024-01-31) + +This is a bugfix release that is API and ABI compatible with previous +1.0.x releases. + +## Highlights + - Fix v4l2 enumeration with filter. This should fix negotiation in some + GStreamer pipelines with capsfilter. Also probe for EXPBUF support + before using it. + - Fix max-latency property and Buffer param when dealing with small + ALSA device buffers. This should fix stuttering with some AMD + based soundcards. + - More small cleanups an improvements. + +## Modules + - Improve netjack2 channel positions. + - Improve RAOP module state after suspend/resume. (#3778) + - Avoid crash in some LV2 plugins by configuring the Atom ports. (#3815) + +## SPA + - Bump libcamera requirements to 0.2.0. + - Try to avoid unaligned load exceptions. (#3790) + - Fix v4l2 enumeration with filter. (#1793) + - Fix max-latency property and Buffer param when dealing with small + ALSA device buffers. This should fix stuttering with some AMD + based soundcards. (#3744,#3622) + - Add a resync.ms option to node.driver to make it possible to resync + fast to clock jumps. + - Probe for EXPBUF support in v4l2 before using it. (#3821) + +## pulse-server + - Also emit change events when the port list change. + +## Bluetooth + - Log a more verbose explanation when other soundservers seem to be + interfering with bluetooth. + - Add quirks for Rockbox Brick. (#3786) + - Add quirks for SoundCore mini2. (#2927) + +## JACK + - Improve check for the running state of clients. (#3794) + # PipeWire 1.0.1 (2024-01-11) This is a bugfix release that is API and ABI compatible with previous @@ -49,9 +118,6 @@ This is a bugfix release that is API and ABI compatible with previous - Improve error handling while connecting. - Fix dts_offset. -Older versions: - - # PipeWire 1.0.0 (2023-11-26) The PipeWire project is immensely proud to announce the 1.0 release diff --git a/debian/changelog b/debian/changelog index 00652c5cca2bebe7f6632a35559f762d4afb86ab..91b38d24708bed40055835d247399ee65d07d650 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,44 @@ +pipewire (1.0.3-1~bpo12+1) bookworm-backports; urgency=medium + + * Rebuild for bookworm-backports. + * Disable ROC, not available in Bookworm + * Install udev rules in /lib/udev/rules.d + * Add patches to keep compatibility with libcamera < 0.2.0 + + -- Dylan Aïssi <daissi@debian.org> Thu, 15 Feb 2024 11:51:26 +0100 + +pipewire (1.0.3-1) unstable; urgency=medium + + * New upstream release + + -- Dylan Aïssi <daissi@debian.org> Fri, 02 Feb 2024 16:06:47 +0100 + +pipewire (1.0.2-1) unstable; urgency=medium + + [ Dylan Aïssi ] + * New upstream release + * Drop patches included in upstream release: + - libcamera0.2 compatibility + + [ Jeremy BÃcha ] + * Cherry-pick patches from Sergio Costas for snap permissions support. + CVE-2022-4964 (LP: #1995707) + * Add apparmor and snapd-glib to Build-Depends for snap feature + * Cherry-pick a patch to fix LP: #2051504 + + -- Dylan Aïssi <daissi@debian.org> Thu, 01 Feb 2024 13:01:35 +0100 + +pipewire (1.0.1-2) unstable; urgency=medium + + * Switch Build-Deps from systemd to systemd-dev (Closes: #1060598) + * Use pkgconf instead of the transitional pkg-config + * Bump minimum meson to 0.61.1 + * Drop 'dh_missing --fail-missing', default since debhelper compat 13 + * Bump minimum libcamera to 0.2.0 + * Cherry-pick upstream patches for libcamera0.2 compatibility + + -- Dylan Aïssi <daissi@debian.org> Mon, 22 Jan 2024 17:24:44 +0100 + pipewire (1.0.1-1~bpo12+1) bookworm-backports; urgency=medium * Rebuild for bookworm-backports. diff --git a/debian/control b/debian/control index 54564a09e006e3668535dbebad0bad35f9e41898..a95b2cae74e7fcf3dc0451bc905690e391f6d567 100644 --- a/debian/control +++ b/debian/control @@ -7,6 +7,7 @@ Uploaders: Jeremy Bicha <jbicha@debian.org>, Build-Depends: debhelper-compat (= 13), doxygen <!nodoc>, graphviz <!nodoc>, + libapparmor-dev [linux-any], libasound2-dev, libavahi-client-dev, libbluetooth-dev [linux-any], @@ -31,6 +32,7 @@ Build-Depends: debhelper-compat (= 13), # libroc-dev (>= 0.3.0+dfsg-3), libsbc-dev, libsdl2-dev <!noinsttest>, + libsnapd-glib-dev [linux-any], libsndfile1-dev, libssl-dev, libsystemd-dev [linux-any], @@ -39,11 +41,11 @@ Build-Depends: debhelper-compat (= 13), libv4l-dev, libwebrtc-audio-processing-dev, libxfixes-dev (>= 1:6.0.0), - meson (>= 0.59.0), + meson (>= 0.61.1), modemmanager-dev, - pkg-config, + pkgconf, python3-docutils, - systemd [linux-any] + systemd-dev Build-Conflicts: libfdk-aac-dev Standards-Version: 4.6.2 Vcs-Browser: https://salsa.debian.org/utopia-team/pipewire diff --git a/debian/gbp.conf b/debian/gbp.conf index d8690834d056cfc29241b181c58761de74fb7a91..24390e79c5c51dc4a6c48a3517e44e92cb601f9c 100644 --- a/debian/gbp.conf +++ b/debian/gbp.conf @@ -1,7 +1,7 @@ [DEFAULT] pristine-tar = True debian-branch = debian/bookworm-backports -upstream-branch = upstream/1.0 +upstream-branch = upstream/1.0.x upstream-vcs-tag = %(version)s [buildpackage] diff --git a/debian/patches/Revert-libcamera0.2-01cb3fa8.patch b/debian/patches/Revert-libcamera0.2-01cb3fa8.patch new file mode 100644 index 0000000000000000000000000000000000000000..75649a7fc297787c0346ec3d5cd8609414aa067e --- /dev/null +++ b/debian/patches/Revert-libcamera0.2-01cb3fa8.patch @@ -0,0 +1,48 @@ +From: Dylan Aïssi <dylan.aissi@collabora.com> +Date: Fri, 2 Feb 2024 16:32:57 +0100 +Subject: [PATCH] Revert "spa: libcamera: bump minimum supported version to + 0.2.0" + +This reverts commit 01cb3fa862d36adfded8f02540660b43b9c1584c. +libcamera 0.2.0 is not available in Bookworm, so we need to revert this patch +to keep compatibility with the version in Bookworm. +--- + spa/meson.build | 3 ++- + spa/plugins/libcamera/libcamera-device.cpp | 2 ++ + 2 files changed, 4 insertions(+), 1 deletion(-) + +diff --git a/spa/meson.build b/spa/meson.build +index db0a84425..0ee750d6e 100644 +--- a/spa/meson.build ++++ b/spa/meson.build +@@ -96,8 +96,9 @@ if get_option('spa-plugins').allowed() + endif + summary({'Vulkan': have_vulkan}, bool_yn: true, section: 'Misc dependencies') + +- libcamera_dep = dependency('libcamera', version: '>= 0.2.0', required: get_option('libcamera')) ++ libcamera_dep = dependency('libcamera', required: get_option('libcamera')) + summary({'libcamera': libcamera_dep.found()}, bool_yn: true, section: 'Backend') ++ cdata.set('HAVE_LIBCAMERA_SYSTEM_DEVICES', libcamera_dep.version().version_compare('>= 0.1.0')) + + compress_offload_option = get_option('compress-offload') + summary({'Compress-Offload': compress_offload_option.allowed()}, bool_yn: true, section: 'Backend') +diff --git a/spa/plugins/libcamera/libcamera-device.cpp b/spa/plugins/libcamera/libcamera-device.cpp +index b25a4eb72..0abf2f619 100644 +--- a/spa/plugins/libcamera/libcamera-device.cpp ++++ b/spa/plugins/libcamera/libcamera-device.cpp +@@ -61,10 +61,12 @@ struct impl { + static const libcamera::Span<const int64_t> cameraDevice( + const Camera *camera) + { ++#ifdef HAVE_LIBCAMERA_SYSTEM_DEVICES + const ControlList &props = camera->properties(); + + if (auto devices = props.get(properties::SystemDevices)) + return devices.value(); ++#endif + + return {}; + } +-- +2.30.2 + diff --git a/debian/patches/Revert-libcamera0.2-fd33d2d3.patch b/debian/patches/Revert-libcamera0.2-fd33d2d3.patch new file mode 100644 index 0000000000000000000000000000000000000000..a9b6c9a22ae73bea5d70d36bf064aa6a5bd46cd9 --- /dev/null +++ b/debian/patches/Revert-libcamera0.2-fd33d2d3.patch @@ -0,0 +1,72 @@ +From: Dylan Aïssi <dylan.aissi@collabora.com> +Date: Fri, 2 Feb 2024 16:32:49 +0100 +Subject: [PATCH] Revert "spa: libcamera: use + `CameraConfiguration::orientation`" + +This reverts commit fd33d2d3bb6333c7d6e74cbaa806bff2d908f589. +libcamera 0.2.0 is not available in Bookworm, so we need to revert this patch +to keep compatibility with the version in Bookworm. +--- + spa/plugins/libcamera/libcamera-utils.cpp | 36 ++++++++++++----------- + 1 file changed, 19 insertions(+), 17 deletions(-) + +diff --git a/spa/plugins/libcamera/libcamera-utils.cpp b/spa/plugins/libcamera/libcamera-utils.cpp +index c197248d3..2b1aea5a7 100644 +--- a/spa/plugins/libcamera/libcamera-utils.cpp ++++ b/spa/plugins/libcamera/libcamera-utils.cpp +@@ -716,23 +716,25 @@ static int spa_libcamera_use_buffers(struct impl *impl, struct port *port, + } + + static const struct { +- Orientation libcamera_orientation; /* clockwise rotation then horizontal mirroring */ +- uint32_t spa_transform_value; /* horizontal mirroring then counter-clockwise rotation */ +-} orientation_map[] = { +- { Orientation::Rotate0, SPA_META_TRANSFORMATION_None }, +- { Orientation::Rotate0Mirror, SPA_META_TRANSFORMATION_Flipped }, +- { Orientation::Rotate90, SPA_META_TRANSFORMATION_270 }, +- { Orientation::Rotate90Mirror, SPA_META_TRANSFORMATION_Flipped90 }, +- { Orientation::Rotate180, SPA_META_TRANSFORMATION_180 }, +- { Orientation::Rotate180Mirror, SPA_META_TRANSFORMATION_Flipped180 }, +- { Orientation::Rotate270, SPA_META_TRANSFORMATION_90 }, +- { Orientation::Rotate270Mirror, SPA_META_TRANSFORMATION_Flipped270 }, ++ Transform libcamera_transform; ++ uint32_t spa_transform_value; ++} transform_map[] = { ++ { Transform::Identity, SPA_META_TRANSFORMATION_None }, ++ { Transform::Rot0, SPA_META_TRANSFORMATION_None }, ++ { Transform::HFlip, SPA_META_TRANSFORMATION_Flipped }, ++ { Transform::VFlip, SPA_META_TRANSFORMATION_Flipped180 }, ++ { Transform::HVFlip, SPA_META_TRANSFORMATION_180 }, ++ { Transform::Rot180, SPA_META_TRANSFORMATION_180 }, ++ { Transform::Transpose, SPA_META_TRANSFORMATION_Flipped90 }, ++ { Transform::Rot90, SPA_META_TRANSFORMATION_90 }, ++ { Transform::Rot270, SPA_META_TRANSFORMATION_270 }, ++ { Transform::Rot180Transpose, SPA_META_TRANSFORMATION_Flipped270 }, + }; + +-static uint32_t libcamera_orientation_to_spa_transform_value(Orientation orientation) ++static uint32_t libcamera_transform_to_spa_transform_value(Transform transform) + { +- for (const auto& t : orientation_map) { +- if (t.libcamera_orientation == orientation) ++ for (const auto& t : transform_map) { ++ if (t.libcamera_transform == transform) + return t.spa_transform_value; + } + return SPA_META_TRANSFORMATION_None; +@@ -786,9 +788,9 @@ mmap_init(struct impl *impl, struct port *port, + buffers[i], SPA_META_VideoTransform, sizeof(*b->videotransform)); + if (b->videotransform) { + b->videotransform->transform = +- libcamera_orientation_to_spa_transform_value(impl->config->orientation); +- spa_log_debug(impl->log, "Setting videotransform for buffer %u to %u", +- i, b->videotransform->transform); ++ libcamera_transform_to_spa_transform_value(impl->config->transform); ++ spa_log_debug(impl->log, "Setting videotransform for buffer %d to %u (from %s)", ++ i, b->videotransform->transform, transformToString(impl->config->transform)); + + } + +-- +2.30.2 + diff --git a/debian/patches/series b/debian/patches/series index b015341dcc6c7b109a535f04835fd39928afaf71..3fe13612576f315a24a49db4d4f1ec62467c69ba 100644 --- a/debian/patches/series +++ b/debian/patches/series @@ -1,3 +1,24 @@ Don-t-automatically-start-pipewire-for-root-logins.patch Fix_services.patch -# Upstream patch for 0.3.8X + +# Upstream patches for snap permissions +snap/pipewire-pulse-add-snap-permissions-support.patch +snap/Add-missing-files.patch +snap/Apply-1-suggestion-s-to-1-file-s.patch +snap/Apply-1-suggestion-s-to-1-file-s-1.patch +snap/Use-assert-to-check-client-is-not-NULL.patch +snap/Apply-1-suggestion-s-to-1-file-s-2.patch +snap/fix-possible-leak.patch +snap/Better-error-logging-if-getting-connections-fails.patch +snap/Move-variable-definition-inside-block.patch +snap/Move-context-variable-definition-inside-block.patch +snap/Move-add_permission-definition-inside-block.patch +snap/Apply-1-suggestion-s-to-1-file-s-3.patch +snap/Fix-spacing-when-calling-functions.patch +snap/Better-meson_options-description.patch +snap/Replace-spaces-with-tabs.patch +snap/snap-policy-Manage-ENOPROTOOPT-error-in-aa_getpeercon.patch + +# Patches to keep compatibility with libcamera < 0.2.0 +Revert-libcamera0.2-01cb3fa8.patch +Revert-libcamera0.2-fd33d2d3.patch diff --git a/debian/patches/snap/Add-missing-files.patch b/debian/patches/snap/Add-missing-files.patch new file mode 100644 index 0000000000000000000000000000000000000000..f0d7ba7cf41120987c00002b4c262a7476b769a4 --- /dev/null +++ b/debian/patches/snap/Add-missing-files.patch @@ -0,0 +1,242 @@ +From: Sergio Costas Rodriguez <sergio.costas@canonical.com> +Date: Wed, 22 Nov 2023 11:48:14 +0100 +Subject: Add missing files + +Accidentally, I forgot to add snap-policy.* files. + +(cherry picked from commit 5e20a2d5704586f89f48d7575e63f9dac621b89f) +Origin: upstream, after 1.0.1 +--- + src/modules/module-protocol-pulse/snap-policy.c | 193 ++++++++++++++++++++++++ + src/modules/module-protocol-pulse/snap-policy.h | 22 +++ + 2 files changed, 215 insertions(+) + create mode 100644 src/modules/module-protocol-pulse/snap-policy.c + create mode 100644 src/modules/module-protocol-pulse/snap-policy.h + +diff --git a/src/modules/module-protocol-pulse/snap-policy.c b/src/modules/module-protocol-pulse/snap-policy.c +new file mode 100644 +index 0000000..70bf984 +--- /dev/null ++++ b/src/modules/module-protocol-pulse/snap-policy.c +@@ -0,0 +1,193 @@ ++/* PipeWire */ ++/* SPDX-FileCopyrightText: Copyright © 2022 Canonical Ltd. */ ++/* SPDX-License-Identifier: MIT */ ++ ++#ifdef HAVE_CONFIG_H ++#include <config.h> ++#endif ++ ++#include <glib.h> ++#include <snapd-glib/snapd-glib.h> ++#include <pipewire/pipewire.h> ++#include <sys/stat.h> ++#include <fcntl.h> ++#include "client.h" ++#include <sys/apparmor.h> ++#include <errno.h> ++#include "snap-policy.h" ++#include <fcntl.h> ++ ++#define SNAP_LABEL_PREFIX "snap." ++ ++static gboolean check_is_same_snap(gchar *snap1, gchar *snap2) { ++ // Checks if two apparmor labels belong to the same snap ++ g_auto(GStrv) strings1 = NULL; ++ g_auto(GStrv) strings2 = NULL; ++ ++ if (!g_str_has_prefix(snap1, SNAP_LABEL_PREFIX)) { ++ return FALSE; ++ } ++ if (!g_str_has_prefix(snap2, SNAP_LABEL_PREFIX)) { ++ return FALSE; ++ } ++ strings1 = g_strsplit(snap1, ".", 3); ++ strings2 = g_strsplit(snap2, ".", 3); ++ ++ if (g_str_equal(strings1[1], strings2[1]) && (strings1[1] != NULL)) { ++ return TRUE; ++ } ++ return FALSE; ++} ++ ++pw_sandbox_access_t pw_snap_get_audio_permissions(struct client *client, int fd, char **app_id) ++{ ++ g_autofree gchar* aa_label = NULL; ++ gchar* snap_id = NULL; ++ gchar* snap_confinement = NULL; ++ gchar *separator = NULL; ++ g_autofree gchar *aacon = NULL; ++ gchar *aamode = NULL; ++ const char *context = NULL; ++ g_autoptr(SnapdClient) snapdclient = NULL; ++ g_autoptr(GPtrArray) plugs = NULL; ++ gboolean retv; ++ pw_sandbox_access_t permissions = PW_SANDBOX_ACCESS_NONE; ++ pw_sandbox_access_t add_permission = PW_SANDBOX_ACCESS_NONE; ++ SnapdPlug **plug = NULL; ++ GPtrArray *slots = NULL; ++ SnapdSlotRef **slot = NULL; ++ GError *error = NULL; ++ int exit_code; ++ ++ *app_id = g_strdup("unknown"); ++ if (client == NULL) { ++ pw_log_warn("Called snap_get_audio_permissions with NULL parameter."); ++ return PW_SANDBOX_ACCESS_NONE; ++ } ++ ++ if (aa_getpeercon(fd, &aa_label, &snap_confinement) == -1) { ++ if (errno == EINVAL) { ++ // if apparmor isn't enabled, we can safely assume that there are no SNAPs in the system ++ return PW_SANDBOX_ACCESS_NOT_A_SANDBOX; ++ } ++ pw_log_warn("snap_get_audio_permissions: failed to get the AppArmor info."); ++ return PW_SANDBOX_ACCESS_NONE; ++ } ++ if (!g_str_has_prefix(aa_label, SNAP_LABEL_PREFIX)) { ++ // not a SNAP. ++ pw_log_info("snap_get_audio_permissions: not an snap."); ++ return PW_SANDBOX_ACCESS_NOT_A_SANDBOX; ++ } ++ ++ snap_id = g_strdup(aa_label + strlen(SNAP_LABEL_PREFIX)); ++ separator = strchr(snap_id, '.'); ++ if (separator == NULL) { ++ pw_log_info("snap_get_audio_permissions: aa_label has only one dot; not a valid ID."); ++ return PW_SANDBOX_ACCESS_NONE; ++ } ++ *separator = 0; ++ g_free(*app_id); ++ *app_id = snap_id; ++ ++ // it's a "classic" or a "devmode" confinement snap, so we give it full access ++ if (g_str_equal (snap_confinement, "complain")) { ++ return PW_SANDBOX_ACCESS_ALL; ++ } ++ ++ snapdclient = snapd_client_new(); ++ if (snapdclient == NULL) { ++ pw_log_warn("snap_get_audio_permissions: error creating SnapdClient object."); ++ return PW_SANDBOX_ACCESS_NONE; ++ } ++ ++ if (aa_getcon(&aacon, &aamode) == -1) { ++ pw_log_warn("snap_get_audio_permissions: error checking if pipewire-pulse is inside a snap."); ++ return PW_SANDBOX_ACCESS_NONE; // failed to get access to apparmor ++ } ++ ++ // If pipewire-pulse is inside a snap, use snapctl API ++ if (g_str_has_prefix(aacon, SNAP_LABEL_PREFIX)) { ++ // If the snap wanting to get access is the same that contains pipewire, ++ // give to it full access. ++ if (check_is_same_snap(aacon, aa_label)) ++ return PW_SANDBOX_ACCESS_ALL; ++ snapd_client_set_socket_path (snapdclient, "/run/snapd-snap.socket"); ++ ++ /* Take context from the environment if available */ ++ context = g_getenv ("SNAP_COOKIE"); ++ if (!context) ++ context = ""; ++ ++ char *snapctl_command[] = { "is-connected", "--apparmor-label", aa_label, "pulseaudio", NULL }; ++ if (!snapd_client_run_snapctl2_sync (snapdclient, context, (char **) snapctl_command, NULL, NULL, &exit_code, NULL, &error)) { ++ pw_log_warn("snap_get_audio_permissions: error summoning snapctl2 for pulseaudio interface: %s", error->message); ++ return PW_SANDBOX_ACCESS_NONE; ++ } ++ if (exit_code != 1) { ++ // 0 = Connected ++ // 10 = Classic environment ++ // 11 = Not a snap ++ return PW_SANDBOX_ACCESS_ALL; ++ } ++ char *snapctl_command2[] = { "is-connected", "--apparmor-label", aa_label, "audio-record", NULL }; ++ if (!snapd_client_run_snapctl2_sync (snapdclient, context, (char **) snapctl_command2, NULL, NULL, &exit_code, NULL, &error)) { ++ pw_log_warn("snap_get_audio_permissions: error summoning snapctl2 for audio-record interface: %s", error->message); ++ return PW_SANDBOX_ACCESS_NONE; ++ } ++ if (exit_code == 1) { ++ return PW_SANDBOX_ACCESS_PLAYBACK; ++ } ++ return PW_SANDBOX_ACCESS_ALL; ++ } ++ ++ retv = snapd_client_get_connections2_sync(snapdclient, ++ SNAPD_GET_CONNECTIONS_FLAGS_NONE, ++ snap_id, ++ NULL, ++ NULL, ++ NULL, ++ &plugs, ++ NULL, ++ NULL, ++ NULL); ++ if (retv == FALSE) { ++ pw_log_warn("Failed to get Snap connections for snap %s\n", snap_id); ++ return PW_SANDBOX_ACCESS_NONE; ++ } ++ if (plugs == NULL) { ++ pw_log_warn("Failed to get Snap connections for snap %s\n", snap_id); ++ return PW_SANDBOX_ACCESS_NONE; ++ } ++ if (plugs->pdata == NULL) { ++ pw_log_warn("Failed to get Snap connections for snap %s\n", snap_id); ++ return PW_SANDBOX_ACCESS_NONE; ++ } ++ ++ plug = (SnapdPlug **)plugs->pdata; ++ for (guint p = 0; p < plugs->len; p++, plug++) { ++ const gchar *plug_name = snapd_plug_get_name(*plug); ++ if (g_str_equal("audio-record", plug_name)) { ++ add_permission = PW_SANDBOX_ACCESS_RECORD; ++ } else if (g_str_equal("audio-playback", plug_name)) { ++ add_permission = PW_SANDBOX_ACCESS_PLAYBACK; ++ } else if (g_str_equal("pulseaudio", plug_name)) { ++ add_permission = PW_SANDBOX_ACCESS_ALL; ++ } else { ++ continue; ++ } ++ slots = snapd_plug_get_connected_slots(*plug); ++ if (slots == NULL) ++ continue; ++ slot = (SnapdSlotRef**) slots->pdata; ++ ++ for (guint q = 0; q < slots->len; q++, slot++) { ++ const gchar *slot_name = snapd_slot_ref_get_slot (*slot); ++ const gchar *snap_name = snapd_slot_ref_get_snap (*slot); ++ if (g_str_equal (snap_name, "snapd") && ++ g_str_equal (slot_name, plug_name)) ++ permissions |= add_permission; ++ } ++ } ++ ++ return permissions; ++} +diff --git a/src/modules/module-protocol-pulse/snap-policy.h b/src/modules/module-protocol-pulse/snap-policy.h +new file mode 100644 +index 0000000..0c152d3 +--- /dev/null ++++ b/src/modules/module-protocol-pulse/snap-policy.h +@@ -0,0 +1,22 @@ ++/* PipeWire */ ++/* SPDX-FileCopyrightText: Copyright © 2022 Canonical Ltd. */ ++/* SPDX-License-Identifier: MIT */ ++ ++#ifndef _SNAP_POLICY_H_ ++#define _SNAP_POLICY_H_ ++ ++typedef enum _pw_sandbox_access { ++ PW_SANDBOX_ACCESS_NONE = 0, ++ PW_SANDBOX_ACCESS_NOT_A_SANDBOX = 1 << 0, ++ PW_SANDBOX_ACCESS_RECORD = 1 << 1, ++ PW_SANDBOX_ACCESS_PLAYBACK = 1 << 2, ++ PW_SANDBOX_ACCESS_ALL = (PW_SANDBOX_ACCESS_PLAYBACK | PW_SANDBOX_ACCESS_RECORD), ++} pw_sandbox_access_t; ++ ++#define PW_KEY_SNAP_ID "pipewire.snap.id" ++#define PW_KEY_SNAP_PLAYBACK_ALLOWED "pipewire.snap.audio.playback" ++#define PW_KEY_SNAP_RECORD_ALLOWED "pipewire.snap.audio.record" ++ ++pw_sandbox_access_t pw_snap_get_audio_permissions(struct client *client, int fd, char **app_id); ++ ++#endif // _SNAP_POLICY_H_ diff --git a/debian/patches/snap/Apply-1-suggestion-s-to-1-file-s-1.patch b/debian/patches/snap/Apply-1-suggestion-s-to-1-file-s-1.patch new file mode 100644 index 0000000000000000000000000000000000000000..e659139adf5a31751483404cc038e968a76f7dc8 --- /dev/null +++ b/debian/patches/snap/Apply-1-suggestion-s-to-1-file-s-1.patch @@ -0,0 +1,24 @@ +From: =?utf-8?q?Barnab=C3=A1s_P=C5=91cze?= <pobrn@protonmail.com> +Date: Wed, 22 Nov 2023 11:47:07 +0000 +Subject: Apply 1 suggestion(s) to 1 file(s) + +(cherry picked from commit c34bd9575f46275b304298c221356bc827f7a1a9) +Origin: upstream, after 1.0.1 +--- + src/modules/module-protocol-pulse/snap-policy.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/src/modules/module-protocol-pulse/snap-policy.c b/src/modules/module-protocol-pulse/snap-policy.c +index 8cf2762..7bec184 100644 +--- a/src/modules/module-protocol-pulse/snap-policy.c ++++ b/src/modules/module-protocol-pulse/snap-policy.c +@@ -19,7 +19,8 @@ + + #define SNAP_LABEL_PREFIX "snap." + +-static gboolean check_is_same_snap(gchar *snap1, gchar *snap2) { ++static gboolean check_is_same_snap(gchar *snap1, gchar *snap2) ++{ + // Checks if two apparmor labels belong to the same snap + g_auto(GStrv) strings1 = NULL; + g_auto(GStrv) strings2 = NULL; diff --git a/debian/patches/snap/Apply-1-suggestion-s-to-1-file-s-2.patch b/debian/patches/snap/Apply-1-suggestion-s-to-1-file-s-2.patch new file mode 100644 index 0000000000000000000000000000000000000000..a4a1640e6ce9435f26842a1de6e9d885eb4ba5eb --- /dev/null +++ b/debian/patches/snap/Apply-1-suggestion-s-to-1-file-s-2.patch @@ -0,0 +1,23 @@ +From: =?utf-8?q?Barnab=C3=A1s_P=C5=91cze?= <pobrn@protonmail.com> +Date: Wed, 22 Nov 2023 12:05:47 +0000 +Subject: Apply 1 suggestion(s) to 1 file(s) + +(cherry picked from commit abc4bd111be6273298aaeb290d8fb613ac7316d9) +Origin: upstream, after 1.0.1 +--- + src/modules/module-protocol-pulse/snap-policy.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/modules/module-protocol-pulse/snap-policy.c b/src/modules/module-protocol-pulse/snap-policy.c +index d838d33..a6f2e24 100644 +--- a/src/modules/module-protocol-pulse/snap-policy.c ++++ b/src/modules/module-protocol-pulse/snap-policy.c +@@ -58,7 +58,7 @@ pw_sandbox_access_t pw_snap_get_audio_permissions(struct client *client, int fd, + SnapdPlug **plug = NULL; + GPtrArray *slots = NULL; + SnapdSlotRef **slot = NULL; +- GError *error = NULL; ++ g_autoptr(GError) error = NULL; + int exit_code; + + *app_id = g_strdup("unknown"); diff --git a/debian/patches/snap/Apply-1-suggestion-s-to-1-file-s-3.patch b/debian/patches/snap/Apply-1-suggestion-s-to-1-file-s-3.patch new file mode 100644 index 0000000000000000000000000000000000000000..c27d11fce7bc93f82d861f38df5f24b0796cf266 --- /dev/null +++ b/debian/patches/snap/Apply-1-suggestion-s-to-1-file-s-3.patch @@ -0,0 +1,23 @@ +From: =?utf-8?q?Barnab=C3=A1s_P=C5=91cze?= <pobrn@protonmail.com> +Date: Wed, 22 Nov 2023 12:30:32 +0000 +Subject: Apply 1 suggestion(s) to 1 file(s) + +(cherry picked from commit b9b5a261998c2634477a71392a0c0c512c6e5974) +Origin: upstream, after 1.0.1 +--- + meson.build | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/meson.build b/meson.build +index ccdd998..a564b16 100644 +--- a/meson.build ++++ b/meson.build +@@ -439,7 +439,7 @@ else + snap_dep = dependency('snapd-glib', required : get_option('snap')) + endif + if snap_dep.found() and glib2_snap_dep.found() and gio2_snap_dep.found() and apparmor_snap_dep.found() +- cdata.set('HAVE_SNAP', 1) ++ cdata.set('HAVE_SNAP', true) + snap_deps = [glib2_snap_dep, gio2_snap_dep, snap_dep, apparmor_snap_dep] + endif + summary({'GLib-2.0 (Snap support)': glib2_snap_dep.found()}, bool_yn: true, section: 'Misc dependencies') diff --git a/debian/patches/snap/Apply-1-suggestion-s-to-1-file-s.patch b/debian/patches/snap/Apply-1-suggestion-s-to-1-file-s.patch new file mode 100644 index 0000000000000000000000000000000000000000..da7896325a8e56c9d477c500fea34cdda4d7516c --- /dev/null +++ b/debian/patches/snap/Apply-1-suggestion-s-to-1-file-s.patch @@ -0,0 +1,23 @@ +From: =?utf-8?q?Barnab=C3=A1s_P=C5=91cze?= <pobrn@protonmail.com> +Date: Wed, 22 Nov 2023 11:46:57 +0000 +Subject: Apply 1 suggestion(s) to 1 file(s) + +(cherry picked from commit 69b093ebf14b04c9af4fbd899fdfbffff5fc4d8c) +Origin: upstream, after 1.0.1 +--- + src/modules/module-protocol-pulse/snap-policy.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/modules/module-protocol-pulse/snap-policy.c b/src/modules/module-protocol-pulse/snap-policy.c +index 70bf984..8cf2762 100644 +--- a/src/modules/module-protocol-pulse/snap-policy.c ++++ b/src/modules/module-protocol-pulse/snap-policy.c +@@ -90,7 +90,7 @@ pw_sandbox_access_t pw_snap_get_audio_permissions(struct client *client, int fd, + *app_id = snap_id; + + // it's a "classic" or a "devmode" confinement snap, so we give it full access +- if (g_str_equal (snap_confinement, "complain")) { ++ if (g_str_equal(snap_confinement, "complain")) { + return PW_SANDBOX_ACCESS_ALL; + } + diff --git a/debian/patches/snap/Better-error-logging-if-getting-connections-fails.patch b/debian/patches/snap/Better-error-logging-if-getting-connections-fails.patch new file mode 100644 index 0000000000000000000000000000000000000000..f306e91e58e08b1e879fdce01e703d158c3c10d7 --- /dev/null +++ b/debian/patches/snap/Better-error-logging-if-getting-connections-fails.patch @@ -0,0 +1,36 @@ +From: Sergio Costas Rodriguez <sergio.costas@canonical.com> +Date: Wed, 22 Nov 2023 13:18:53 +0100 +Subject: Better error logging if getting connections fails + +(cherry picked from commit 1728b7de598a14b8b2819c6d269f586a4a843501) +Origin: upstream, after 1.0.1 +--- + src/modules/module-protocol-pulse/snap-policy.c | 8 ++++---- + 1 file changed, 4 insertions(+), 4 deletions(-) + +diff --git a/src/modules/module-protocol-pulse/snap-policy.c b/src/modules/module-protocol-pulse/snap-policy.c +index a6f2e24..14e9827 100644 +--- a/src/modules/module-protocol-pulse/snap-policy.c ++++ b/src/modules/module-protocol-pulse/snap-policy.c +@@ -148,17 +148,17 @@ pw_sandbox_access_t pw_snap_get_audio_permissions(struct client *client, int fd, + &plugs, + NULL, + NULL, +- NULL); ++ &error); + if (retv == FALSE) { +- pw_log_warn("Failed to get Snap connections for snap %s\n", snap_id); ++ pw_log_warn("Failed to get Snap connections for snap %s: %s\n", snap_id, error->message); + return PW_SANDBOX_ACCESS_NONE; + } + if (plugs == NULL) { +- pw_log_warn("Failed to get Snap connections for snap %s\n", snap_id); ++ pw_log_warn("Failed to get Snap connections for snap %s: %s\n", snap_id, error->message); + return PW_SANDBOX_ACCESS_NONE; + } + if (plugs->pdata == NULL) { +- pw_log_warn("Failed to get Snap connections for snap %s\n", snap_id); ++ pw_log_warn("Failed to get Snap connections for snap %s: %s\n", snap_id, error->message); + return PW_SANDBOX_ACCESS_NONE; + } + diff --git a/debian/patches/snap/Better-meson_options-description.patch b/debian/patches/snap/Better-meson_options-description.patch new file mode 100644 index 0000000000000000000000000000000000000000..fe9bbbdcd26dbbb41841209af73af7fcf54f4da9 --- /dev/null +++ b/debian/patches/snap/Better-meson_options-description.patch @@ -0,0 +1,22 @@ +From: Sergio Costas Rodriguez <sergio.costas@canonical.com> +Date: Wed, 22 Nov 2023 16:47:26 +0100 +Subject: Better meson_options description + +(cherry picked from commit 5125d69a6905f3ad86c1b1424d814f5442d9b677) +Origin: upstream, after 1.0.1 +--- + meson_options.txt | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/meson_options.txt b/meson_options.txt +index e0937b4..6b274dc 100644 +--- a/meson_options.txt ++++ b/meson_options.txt +@@ -331,6 +331,6 @@ option('libffado', + type: 'feature', + value: 'auto') + option('snap', +- description : 'Snap support is available.', ++ description : 'Enable Snap permissions support.', + type : 'feature', + value : 'auto') diff --git a/debian/patches/snap/Fix-spacing-when-calling-functions.patch b/debian/patches/snap/Fix-spacing-when-calling-functions.patch new file mode 100644 index 0000000000000000000000000000000000000000..f3d6561a5574cfd410ea7c63d1aa8f6c38a63e63 --- /dev/null +++ b/debian/patches/snap/Fix-spacing-when-calling-functions.patch @@ -0,0 +1,57 @@ +From: Sergio Costas Rodriguez <sergio.costas@canonical.com> +Date: Wed, 22 Nov 2023 13:43:54 +0100 +Subject: Fix spacing when calling functions + +(cherry picked from commit fda4addf1ec3d97c13ed8998a92b7ae5d12f6c68) +Origin: upstream, after 1.0.1 +--- + src/modules/module-protocol-pulse/snap-policy.c | 16 ++++++++-------- + 1 file changed, 8 insertions(+), 8 deletions(-) + +diff --git a/src/modules/module-protocol-pulse/snap-policy.c b/src/modules/module-protocol-pulse/snap-policy.c +index b96c599..e1dd0d5 100644 +--- a/src/modules/module-protocol-pulse/snap-policy.c ++++ b/src/modules/module-protocol-pulse/snap-policy.c +@@ -106,15 +106,15 @@ pw_sandbox_access_t pw_snap_get_audio_permissions(struct client *client, int fd, + // give to it full access. + if (check_is_same_snap(aacon, aa_label)) + return PW_SANDBOX_ACCESS_ALL; +- snapd_client_set_socket_path (snapdclient, "/run/snapd-snap.socket"); ++ snapd_client_set_socket_path(snapdclient, "/run/snapd-snap.socket"); + + /* Take context from the environment if available */ +- const char *context = g_getenv ("SNAP_COOKIE"); ++ const char *context = g_getenv("SNAP_COOKIE"); + if (!context) + context = ""; + + char *snapctl_command[] = { "is-connected", "--apparmor-label", aa_label, "pulseaudio", NULL }; +- if (!snapd_client_run_snapctl2_sync (snapdclient, context, (char **) snapctl_command, NULL, NULL, &exit_code, NULL, &error)) { ++ if (!snapd_client_run_snapctl2_sync(snapdclient, context, (char **) snapctl_command, NULL, NULL, &exit_code, NULL, &error)) { + pw_log_warn("snap_get_audio_permissions: error summoning snapctl2 for pulseaudio interface: %s", error->message); + return PW_SANDBOX_ACCESS_NONE; + } +@@ -125,7 +125,7 @@ pw_sandbox_access_t pw_snap_get_audio_permissions(struct client *client, int fd, + return PW_SANDBOX_ACCESS_ALL; + } + char *snapctl_command2[] = { "is-connected", "--apparmor-label", aa_label, "audio-record", NULL }; +- if (!snapd_client_run_snapctl2_sync (snapdclient, context, (char **) snapctl_command2, NULL, NULL, &exit_code, NULL, &error)) { ++ if (!snapd_client_run_snapctl2_sync(snapdclient, context, (char **) snapctl_command2, NULL, NULL, &exit_code, NULL, &error)) { + pw_log_warn("snap_get_audio_permissions: error summoning snapctl2 for audio-record interface: %s", error->message); + return PW_SANDBOX_ACCESS_NONE; + } +@@ -177,10 +177,10 @@ pw_sandbox_access_t pw_snap_get_audio_permissions(struct client *client, int fd, + SnapdSlotRef **slot = (SnapdSlotRef**) slots->pdata; + + for (guint q = 0; q < slots->len; q++, slot++) { +- const gchar *slot_name = snapd_slot_ref_get_slot (*slot); +- const gchar *snap_name = snapd_slot_ref_get_snap (*slot); +- if (g_str_equal (snap_name, "snapd") && +- g_str_equal (slot_name, plug_name)) ++ const gchar *slot_name = snapd_slot_ref_get_slot(*slot); ++ const gchar *snap_name = snapd_slot_ref_get_snap(*slot); ++ if (g_str_equal(snap_name, "snapd") && ++ g_str_equal(slot_name, plug_name)) + permissions |= add_permission; + } + } diff --git a/debian/patches/snap/Move-add_permission-definition-inside-block.patch b/debian/patches/snap/Move-add_permission-definition-inside-block.patch new file mode 100644 index 0000000000000000000000000000000000000000..3dfe90e714cb13a6d0c5ba9ccc76069e0e621ea0 --- /dev/null +++ b/debian/patches/snap/Move-add_permission-definition-inside-block.patch @@ -0,0 +1,30 @@ +From: Sergio Costas Rodriguez <sergio.costas@canonical.com> +Date: Wed, 22 Nov 2023 13:29:39 +0100 +Subject: Move add_permission definition inside block + +(cherry picked from commit 1c9016280c42dc43087700789d30066e9d7a19b9) +Origin: upstream, after 1.0.1 +--- + src/modules/module-protocol-pulse/snap-policy.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/modules/module-protocol-pulse/snap-policy.c b/src/modules/module-protocol-pulse/snap-policy.c +index ebb5f29..b96c599 100644 +--- a/src/modules/module-protocol-pulse/snap-policy.c ++++ b/src/modules/module-protocol-pulse/snap-policy.c +@@ -53,7 +53,6 @@ pw_sandbox_access_t pw_snap_get_audio_permissions(struct client *client, int fd, + g_autoptr(GPtrArray) plugs = NULL; + gboolean retv; + pw_sandbox_access_t permissions = PW_SANDBOX_ACCESS_NONE; +- pw_sandbox_access_t add_permission = PW_SANDBOX_ACCESS_NONE; + SnapdPlug **plug = NULL; + g_autoptr(GError) error = NULL; + int exit_code; +@@ -161,6 +160,7 @@ pw_sandbox_access_t pw_snap_get_audio_permissions(struct client *client, int fd, + + plug = (SnapdPlug **)plugs->pdata; + for (guint p = 0; p < plugs->len; p++, plug++) { ++ pw_sandbox_access_t add_permission; + const gchar *plug_name = snapd_plug_get_name(*plug); + if (g_str_equal("audio-record", plug_name)) { + add_permission = PW_SANDBOX_ACCESS_RECORD; diff --git a/debian/patches/snap/Move-context-variable-definition-inside-block.patch b/debian/patches/snap/Move-context-variable-definition-inside-block.patch new file mode 100644 index 0000000000000000000000000000000000000000..619221a4a934d23064a600d642def7b4e355b864 --- /dev/null +++ b/debian/patches/snap/Move-context-variable-definition-inside-block.patch @@ -0,0 +1,31 @@ +From: Sergio Costas Rodriguez <sergio.costas@canonical.com> +Date: Wed, 22 Nov 2023 13:26:23 +0100 +Subject: Move context variable definition inside block + +(cherry picked from commit 67b9e9c4e8354a5f2075e41fa0d63f0b9b82d96a) +Origin: upstream, after 1.0.1 +--- + src/modules/module-protocol-pulse/snap-policy.c | 3 +-- + 1 file changed, 1 insertion(+), 2 deletions(-) + +diff --git a/src/modules/module-protocol-pulse/snap-policy.c b/src/modules/module-protocol-pulse/snap-policy.c +index fcb1ed6..ebb5f29 100644 +--- a/src/modules/module-protocol-pulse/snap-policy.c ++++ b/src/modules/module-protocol-pulse/snap-policy.c +@@ -49,7 +49,6 @@ pw_sandbox_access_t pw_snap_get_audio_permissions(struct client *client, int fd, + gchar *separator = NULL; + g_autofree gchar *aacon = NULL; + gchar *aamode = NULL; +- const char *context = NULL; + g_autoptr(SnapdClient) snapdclient = NULL; + g_autoptr(GPtrArray) plugs = NULL; + gboolean retv; +@@ -111,7 +110,7 @@ pw_sandbox_access_t pw_snap_get_audio_permissions(struct client *client, int fd, + snapd_client_set_socket_path (snapdclient, "/run/snapd-snap.socket"); + + /* Take context from the environment if available */ +- context = g_getenv ("SNAP_COOKIE"); ++ const char *context = g_getenv ("SNAP_COOKIE"); + if (!context) + context = ""; + diff --git a/debian/patches/snap/Move-variable-definition-inside-block.patch b/debian/patches/snap/Move-variable-definition-inside-block.patch new file mode 100644 index 0000000000000000000000000000000000000000..528a58239dd404ef96eaeb78224e187af2554f65 --- /dev/null +++ b/debian/patches/snap/Move-variable-definition-inside-block.patch @@ -0,0 +1,36 @@ +From: Sergio Costas Rodriguez <sergio.costas@canonical.com> +Date: Wed, 22 Nov 2023 13:24:26 +0100 +Subject: Move variable definition inside block + +(cherry picked from commit 18d0e2e850d74c014c6c27d22c736ec40ac9a873) +Origin: upstream, after 1.0.1 +--- + src/modules/module-protocol-pulse/snap-policy.c | 6 ++---- + 1 file changed, 2 insertions(+), 4 deletions(-) + +diff --git a/src/modules/module-protocol-pulse/snap-policy.c b/src/modules/module-protocol-pulse/snap-policy.c +index 14e9827..fcb1ed6 100644 +--- a/src/modules/module-protocol-pulse/snap-policy.c ++++ b/src/modules/module-protocol-pulse/snap-policy.c +@@ -56,8 +56,6 @@ pw_sandbox_access_t pw_snap_get_audio_permissions(struct client *client, int fd, + pw_sandbox_access_t permissions = PW_SANDBOX_ACCESS_NONE; + pw_sandbox_access_t add_permission = PW_SANDBOX_ACCESS_NONE; + SnapdPlug **plug = NULL; +- GPtrArray *slots = NULL; +- SnapdSlotRef **slot = NULL; + g_autoptr(GError) error = NULL; + int exit_code; + +@@ -174,10 +172,10 @@ pw_sandbox_access_t pw_snap_get_audio_permissions(struct client *client, int fd, + } else { + continue; + } +- slots = snapd_plug_get_connected_slots(*plug); ++ GPtrArray *slots = snapd_plug_get_connected_slots(*plug); + if (slots == NULL) + continue; +- slot = (SnapdSlotRef**) slots->pdata; ++ SnapdSlotRef **slot = (SnapdSlotRef**) slots->pdata; + + for (guint q = 0; q < slots->len; q++, slot++) { + const gchar *slot_name = snapd_slot_ref_get_slot (*slot); diff --git a/debian/patches/snap/Replace-spaces-with-tabs.patch b/debian/patches/snap/Replace-spaces-with-tabs.patch new file mode 100644 index 0000000000000000000000000000000000000000..e64ab148133d5b370d5b19cd2fc622c7a1ce55a6 --- /dev/null +++ b/debian/patches/snap/Replace-spaces-with-tabs.patch @@ -0,0 +1,348 @@ +From: Sergio Costas Rodriguez <sergio.costas@canonical.com> +Date: Wed, 22 Nov 2023 13:49:11 +0100 +Subject: Replace spaces with tabs + +(cherry picked from commit 6506bb2f4444be032985cc2874cc3eb94abf78fa) +Origin: upstream, after 1.0.1 +--- + src/modules/module-protocol-pulse/snap-policy.c | 322 ++++++++++++------------ + 1 file changed, 161 insertions(+), 161 deletions(-) + +diff --git a/src/modules/module-protocol-pulse/snap-policy.c b/src/modules/module-protocol-pulse/snap-policy.c +index e1dd0d5..f027f5c 100644 +--- a/src/modules/module-protocol-pulse/snap-policy.c ++++ b/src/modules/module-protocol-pulse/snap-policy.c +@@ -18,172 +18,172 @@ + #include <fcntl.h> + #include <assert.h> + +-#define SNAP_LABEL_PREFIX "snap." ++#define SNAP_LABEL_PREFIX "snap." + + static gboolean check_is_same_snap(gchar *snap1, gchar *snap2) + { +- // Checks if two apparmor labels belong to the same snap +- g_auto(GStrv) strings1 = NULL; +- g_auto(GStrv) strings2 = NULL; +- +- if (!g_str_has_prefix(snap1, SNAP_LABEL_PREFIX)) { +- return FALSE; +- } +- if (!g_str_has_prefix(snap2, SNAP_LABEL_PREFIX)) { +- return FALSE; +- } +- strings1 = g_strsplit(snap1, ".", 3); +- strings2 = g_strsplit(snap2, ".", 3); +- +- if (g_str_equal(strings1[1], strings2[1]) && (strings1[1] != NULL)) { +- return TRUE; +- } +- return FALSE; ++ // Checks if two apparmor labels belong to the same snap ++ g_auto(GStrv) strings1 = NULL; ++ g_auto(GStrv) strings2 = NULL; ++ ++ if (!g_str_has_prefix(snap1, SNAP_LABEL_PREFIX)) { ++ return FALSE; ++ } ++ if (!g_str_has_prefix(snap2, SNAP_LABEL_PREFIX)) { ++ return FALSE; ++ } ++ strings1 = g_strsplit(snap1, ".", 3); ++ strings2 = g_strsplit(snap2, ".", 3); ++ ++ if (g_str_equal(strings1[1], strings2[1]) && (strings1[1] != NULL)) { ++ return TRUE; ++ } ++ return FALSE; + } + + pw_sandbox_access_t pw_snap_get_audio_permissions(struct client *client, int fd, char **app_id) + { +- g_autofree gchar* aa_label = NULL; +- gchar* snap_id = NULL; +- gchar* snap_confinement = NULL; +- gchar *separator = NULL; +- g_autofree gchar *aacon = NULL; +- gchar *aamode = NULL; +- g_autoptr(SnapdClient) snapdclient = NULL; +- g_autoptr(GPtrArray) plugs = NULL; +- gboolean retv; +- pw_sandbox_access_t permissions = PW_SANDBOX_ACCESS_NONE; +- SnapdPlug **plug = NULL; +- g_autoptr(GError) error = NULL; +- int exit_code; +- +- *app_id = g_strdup("unknown"); +- assert(client != NULL); +- +- if (aa_getpeercon(fd, &aa_label, &snap_confinement) == -1) { +- if (errno == EINVAL) { +- // if apparmor isn't enabled, we can safely assume that there are no SNAPs in the system +- return PW_SANDBOX_ACCESS_NOT_A_SANDBOX; +- } +- pw_log_warn("snap_get_audio_permissions: failed to get the AppArmor info."); +- return PW_SANDBOX_ACCESS_NONE; +- } +- if (!g_str_has_prefix(aa_label, SNAP_LABEL_PREFIX)) { +- // not a SNAP. +- pw_log_info("snap_get_audio_permissions: not an snap."); +- return PW_SANDBOX_ACCESS_NOT_A_SANDBOX; +- } +- +- snap_id = g_strdup(aa_label + strlen(SNAP_LABEL_PREFIX)); +- separator = strchr(snap_id, '.'); +- if (separator == NULL) { +- pw_log_info("snap_get_audio_permissions: aa_label has only one dot; not a valid ID."); +- return PW_SANDBOX_ACCESS_NONE; +- } +- *separator = 0; +- g_free(*app_id); +- *app_id = snap_id; +- +- // it's a "classic" or a "devmode" confinement snap, so we give it full access +- if (g_str_equal(snap_confinement, "complain")) { +- return PW_SANDBOX_ACCESS_ALL; +- } +- +- snapdclient = snapd_client_new(); +- if (snapdclient == NULL) { +- pw_log_warn("snap_get_audio_permissions: error creating SnapdClient object."); +- return PW_SANDBOX_ACCESS_NONE; +- } +- +- if (aa_getcon(&aacon, &aamode) == -1) { +- pw_log_warn("snap_get_audio_permissions: error checking if pipewire-pulse is inside a snap."); +- return PW_SANDBOX_ACCESS_NONE; // failed to get access to apparmor +- } +- +- // If pipewire-pulse is inside a snap, use snapctl API +- if (g_str_has_prefix(aacon, SNAP_LABEL_PREFIX)) { +- // If the snap wanting to get access is the same that contains pipewire, +- // give to it full access. +- if (check_is_same_snap(aacon, aa_label)) +- return PW_SANDBOX_ACCESS_ALL; +- snapd_client_set_socket_path(snapdclient, "/run/snapd-snap.socket"); +- +- /* Take context from the environment if available */ +- const char *context = g_getenv("SNAP_COOKIE"); +- if (!context) +- context = ""; +- +- char *snapctl_command[] = { "is-connected", "--apparmor-label", aa_label, "pulseaudio", NULL }; +- if (!snapd_client_run_snapctl2_sync(snapdclient, context, (char **) snapctl_command, NULL, NULL, &exit_code, NULL, &error)) { +- pw_log_warn("snap_get_audio_permissions: error summoning snapctl2 for pulseaudio interface: %s", error->message); +- return PW_SANDBOX_ACCESS_NONE; +- } +- if (exit_code != 1) { +- // 0 = Connected +- // 10 = Classic environment +- // 11 = Not a snap +- return PW_SANDBOX_ACCESS_ALL; +- } +- char *snapctl_command2[] = { "is-connected", "--apparmor-label", aa_label, "audio-record", NULL }; +- if (!snapd_client_run_snapctl2_sync(snapdclient, context, (char **) snapctl_command2, NULL, NULL, &exit_code, NULL, &error)) { +- pw_log_warn("snap_get_audio_permissions: error summoning snapctl2 for audio-record interface: %s", error->message); +- return PW_SANDBOX_ACCESS_NONE; +- } +- if (exit_code == 1) { +- return PW_SANDBOX_ACCESS_PLAYBACK; +- } +- return PW_SANDBOX_ACCESS_ALL; +- } +- +- retv = snapd_client_get_connections2_sync(snapdclient, +- SNAPD_GET_CONNECTIONS_FLAGS_NONE, +- snap_id, +- NULL, +- NULL, +- NULL, +- &plugs, +- NULL, +- NULL, +- &error); +- if (retv == FALSE) { +- pw_log_warn("Failed to get Snap connections for snap %s: %s\n", snap_id, error->message); +- return PW_SANDBOX_ACCESS_NONE; +- } +- if (plugs == NULL) { +- pw_log_warn("Failed to get Snap connections for snap %s: %s\n", snap_id, error->message); +- return PW_SANDBOX_ACCESS_NONE; +- } +- if (plugs->pdata == NULL) { +- pw_log_warn("Failed to get Snap connections for snap %s: %s\n", snap_id, error->message); +- return PW_SANDBOX_ACCESS_NONE; +- } +- +- plug = (SnapdPlug **)plugs->pdata; +- for (guint p = 0; p < plugs->len; p++, plug++) { +- pw_sandbox_access_t add_permission; +- const gchar *plug_name = snapd_plug_get_name(*plug); +- if (g_str_equal("audio-record", plug_name)) { +- add_permission = PW_SANDBOX_ACCESS_RECORD; +- } else if (g_str_equal("audio-playback", plug_name)) { +- add_permission = PW_SANDBOX_ACCESS_PLAYBACK; +- } else if (g_str_equal("pulseaudio", plug_name)) { +- add_permission = PW_SANDBOX_ACCESS_ALL; +- } else { +- continue; +- } +- GPtrArray *slots = snapd_plug_get_connected_slots(*plug); +- if (slots == NULL) +- continue; +- SnapdSlotRef **slot = (SnapdSlotRef**) slots->pdata; +- +- for (guint q = 0; q < slots->len; q++, slot++) { +- const gchar *slot_name = snapd_slot_ref_get_slot(*slot); +- const gchar *snap_name = snapd_slot_ref_get_snap(*slot); +- if (g_str_equal(snap_name, "snapd") && +- g_str_equal(slot_name, plug_name)) +- permissions |= add_permission; +- } +- } +- +- return permissions; ++ g_autofree gchar* aa_label = NULL; ++ gchar* snap_id = NULL; ++ gchar* snap_confinement = NULL; ++ gchar *separator = NULL; ++ g_autofree gchar *aacon = NULL; ++ gchar *aamode = NULL; ++ g_autoptr(SnapdClient) snapdclient = NULL; ++ g_autoptr(GPtrArray) plugs = NULL; ++ gboolean retv; ++ pw_sandbox_access_t permissions = PW_SANDBOX_ACCESS_NONE; ++ SnapdPlug **plug = NULL; ++ g_autoptr(GError) error = NULL; ++ int exit_code; ++ ++ *app_id = g_strdup("unknown"); ++ assert(client != NULL); ++ ++ if (aa_getpeercon(fd, &aa_label, &snap_confinement) == -1) { ++ if (errno == EINVAL) { ++ // if apparmor isn't enabled, we can safely assume that there are no SNAPs in the system ++ return PW_SANDBOX_ACCESS_NOT_A_SANDBOX; ++ } ++ pw_log_warn("snap_get_audio_permissions: failed to get the AppArmor info."); ++ return PW_SANDBOX_ACCESS_NONE; ++ } ++ if (!g_str_has_prefix(aa_label, SNAP_LABEL_PREFIX)) { ++ // not a SNAP. ++ pw_log_info("snap_get_audio_permissions: not an snap."); ++ return PW_SANDBOX_ACCESS_NOT_A_SANDBOX; ++ } ++ ++ snap_id = g_strdup(aa_label + strlen(SNAP_LABEL_PREFIX)); ++ separator = strchr(snap_id, '.'); ++ if (separator == NULL) { ++ pw_log_info("snap_get_audio_permissions: aa_label has only one dot; not a valid ID."); ++ return PW_SANDBOX_ACCESS_NONE; ++ } ++ *separator = 0; ++ g_free(*app_id); ++ *app_id = snap_id; ++ ++ // it's a "classic" or a "devmode" confinement snap, so we give it full access ++ if (g_str_equal(snap_confinement, "complain")) { ++ return PW_SANDBOX_ACCESS_ALL; ++ } ++ ++ snapdclient = snapd_client_new(); ++ if (snapdclient == NULL) { ++ pw_log_warn("snap_get_audio_permissions: error creating SnapdClient object."); ++ return PW_SANDBOX_ACCESS_NONE; ++ } ++ ++ if (aa_getcon(&aacon, &aamode) == -1) { ++ pw_log_warn("snap_get_audio_permissions: error checking if pipewire-pulse is inside a snap."); ++ return PW_SANDBOX_ACCESS_NONE; // failed to get access to apparmor ++ } ++ ++ // If pipewire-pulse is inside a snap, use snapctl API ++ if (g_str_has_prefix(aacon, SNAP_LABEL_PREFIX)) { ++ // If the snap wanting to get access is the same that contains pipewire, ++ // give to it full access. ++ if (check_is_same_snap(aacon, aa_label)) ++ return PW_SANDBOX_ACCESS_ALL; ++ snapd_client_set_socket_path(snapdclient, "/run/snapd-snap.socket"); ++ ++ /* Take context from the environment if available */ ++ const char *context = g_getenv("SNAP_COOKIE"); ++ if (!context) ++ context = ""; ++ ++ char *snapctl_command[] = { "is-connected", "--apparmor-label", aa_label, "pulseaudio", NULL }; ++ if (!snapd_client_run_snapctl2_sync(snapdclient, context, (char **) snapctl_command, NULL, NULL, &exit_code, NULL, &error)) { ++ pw_log_warn("snap_get_audio_permissions: error summoning snapctl2 for pulseaudio interface: %s", error->message); ++ return PW_SANDBOX_ACCESS_NONE; ++ } ++ if (exit_code != 1) { ++ // 0 = Connected ++ // 10 = Classic environment ++ // 11 = Not a snap ++ return PW_SANDBOX_ACCESS_ALL; ++ } ++ char *snapctl_command2[] = { "is-connected", "--apparmor-label", aa_label, "audio-record", NULL }; ++ if (!snapd_client_run_snapctl2_sync(snapdclient, context, (char **) snapctl_command2, NULL, NULL, &exit_code, NULL, &error)) { ++ pw_log_warn("snap_get_audio_permissions: error summoning snapctl2 for audio-record interface: %s", error->message); ++ return PW_SANDBOX_ACCESS_NONE; ++ } ++ if (exit_code == 1) { ++ return PW_SANDBOX_ACCESS_PLAYBACK; ++ } ++ return PW_SANDBOX_ACCESS_ALL; ++ } ++ ++ retv = snapd_client_get_connections2_sync(snapdclient, ++ SNAPD_GET_CONNECTIONS_FLAGS_NONE, ++ snap_id, ++ NULL, ++ NULL, ++ NULL, ++ &plugs, ++ NULL, ++ NULL, ++ &error); ++ if (retv == FALSE) { ++ pw_log_warn("Failed to get Snap connections for snap %s: %s\n", snap_id, error->message); ++ return PW_SANDBOX_ACCESS_NONE; ++ } ++ if (plugs == NULL) { ++ pw_log_warn("Failed to get Snap connections for snap %s: %s\n", snap_id, error->message); ++ return PW_SANDBOX_ACCESS_NONE; ++ } ++ if (plugs->pdata == NULL) { ++ pw_log_warn("Failed to get Snap connections for snap %s: %s\n", snap_id, error->message); ++ return PW_SANDBOX_ACCESS_NONE; ++ } ++ ++ plug = (SnapdPlug **)plugs->pdata; ++ for (guint p = 0; p < plugs->len; p++, plug++) { ++ pw_sandbox_access_t add_permission; ++ const gchar *plug_name = snapd_plug_get_name(*plug); ++ if (g_str_equal("audio-record", plug_name)) { ++ add_permission = PW_SANDBOX_ACCESS_RECORD; ++ } else if (g_str_equal("audio-playback", plug_name)) { ++ add_permission = PW_SANDBOX_ACCESS_PLAYBACK; ++ } else if (g_str_equal("pulseaudio", plug_name)) { ++ add_permission = PW_SANDBOX_ACCESS_ALL; ++ } else { ++ continue; ++ } ++ GPtrArray *slots = snapd_plug_get_connected_slots(*plug); ++ if (slots == NULL) ++ continue; ++ SnapdSlotRef **slot = (SnapdSlotRef**) slots->pdata; ++ ++ for (guint q = 0; q < slots->len; q++, slot++) { ++ const gchar *slot_name = snapd_slot_ref_get_slot(*slot); ++ const gchar *snap_name = snapd_slot_ref_get_snap(*slot); ++ if (g_str_equal(snap_name, "snapd") && ++ g_str_equal(slot_name, plug_name)) ++ permissions |= add_permission; ++ } ++ } ++ ++ return permissions; + } diff --git a/debian/patches/snap/Use-assert-to-check-client-is-not-NULL.patch b/debian/patches/snap/Use-assert-to-check-client-is-not-NULL.patch new file mode 100644 index 0000000000000000000000000000000000000000..7b8029ad1a535108caf97cee0f05914921d6021e --- /dev/null +++ b/debian/patches/snap/Use-assert-to-check-client-is-not-NULL.patch @@ -0,0 +1,34 @@ +From: Sergio Costas Rodriguez <sergio.costas@canonical.com> +Date: Wed, 22 Nov 2023 13:03:06 +0100 +Subject: Use assert to check client is not NULL + +(cherry picked from commit b054bc25910713df63a1321bc86fe15432821800) +Origin: upstream, after 1.0.1 +--- + src/modules/module-protocol-pulse/snap-policy.c | 6 ++---- + 1 file changed, 2 insertions(+), 4 deletions(-) + +diff --git a/src/modules/module-protocol-pulse/snap-policy.c b/src/modules/module-protocol-pulse/snap-policy.c +index 7bec184..d838d33 100644 +--- a/src/modules/module-protocol-pulse/snap-policy.c ++++ b/src/modules/module-protocol-pulse/snap-policy.c +@@ -16,6 +16,7 @@ + #include <errno.h> + #include "snap-policy.h" + #include <fcntl.h> ++#include <assert.h> + + #define SNAP_LABEL_PREFIX "snap." + +@@ -61,10 +62,7 @@ pw_sandbox_access_t pw_snap_get_audio_permissions(struct client *client, int fd, + int exit_code; + + *app_id = g_strdup("unknown"); +- if (client == NULL) { +- pw_log_warn("Called snap_get_audio_permissions with NULL parameter."); +- return PW_SANDBOX_ACCESS_NONE; +- } ++ assert(client != NULL); + + if (aa_getpeercon(fd, &aa_label, &snap_confinement) == -1) { + if (errno == EINVAL) { diff --git a/debian/patches/snap/fix-possible-leak.patch b/debian/patches/snap/fix-possible-leak.patch new file mode 100644 index 0000000000000000000000000000000000000000..3939cd1ffdda1bac8a8302c11ada4fe215c5ab37 --- /dev/null +++ b/debian/patches/snap/fix-possible-leak.patch @@ -0,0 +1,39 @@ +From: Sergio Costas Rodriguez <sergio.costas@canonical.com> +Date: Wed, 22 Nov 2023 13:15:42 +0100 +Subject: fix possible leak + +If pw_check_flatpak() sets app_id, its value will leak when +calling pw_snap_get_audio_permissions(). This patch fixes +this. + +(cherry picked from commit ae11e61105d53e4dc0860894ccc17b1a8aead7fa) +Origin: upstream, after 1.0.1 +--- + src/modules/module-protocol-pulse/server.c | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git a/src/modules/module-protocol-pulse/server.c b/src/modules/module-protocol-pulse/server.c +index 6ae97b7..bbb790f 100644 +--- a/src/modules/module-protocol-pulse/server.c ++++ b/src/modules/module-protocol-pulse/server.c +@@ -408,7 +408,7 @@ on_connect(void *data, int fd, uint32_t mask) + client_access = server->client_access; + + if (server->addr.ss_family == AF_UNIX) { +- spa_autofree char *app_id = NULL, *devices = NULL; ++ spa_autofree char *app_id = NULL, *snap_app_id = NULL, *devices = NULL; + #ifdef HAVE_SNAP + pw_sandbox_access_t snap_access; + #endif +@@ -452,9 +452,9 @@ on_connect(void *data, int fd, uint32_t mask) + } + // check SNAP permissions + #ifdef HAVE_SNAP +- snap_access = pw_snap_get_audio_permissions(client, client_fd, &app_id); ++ snap_access = pw_snap_get_audio_permissions(client, client_fd, &snap_app_id); + if ((snap_access & PW_SANDBOX_ACCESS_NOT_A_SANDBOX) == 0) { +- pw_properties_set(client->props, PW_KEY_SNAP_ID, app_id); ++ pw_properties_set(client->props, PW_KEY_SNAP_ID, snap_app_id); + + pw_properties_set(client->props, + PW_KEY_SNAP_PLAYBACK_ALLOWED, diff --git a/debian/patches/snap/pipewire-pulse-add-snap-permissions-support.patch b/debian/patches/snap/pipewire-pulse-add-snap-permissions-support.patch new file mode 100644 index 0000000000000000000000000000000000000000..039c02a9a26d891c3ea00c6b44f9651d917bef48 --- /dev/null +++ b/debian/patches/snap/pipewire-pulse-add-snap-permissions-support.patch @@ -0,0 +1,255 @@ +From: Sergio Costas Rodriguez <sergio.costas@canonical.com> +Date: Wed, 22 Nov 2023 11:26:16 +0100 +Subject: pipewire-pulse: add snap permissions support + +SNAP containers have two main "audio" security rules: + + * audio-playback: the applications inside the container can + send audio samples into a sink + + * audio-record: the applications inside the container can + get audio samples from a source + +Also, old SNAP containers had the "pulseaudio" rule, which just +exposed the pulseaudio socket directly, without limits. This +is similar to the current Flatpak audio permissions. + +In the pulseaudio days, a specific pulseaudio module was used +that checked the permissions given to the application and +allowed or forbade access to the pulseaudio operations. +With the change to pipewire, this functionality must be +implemented in pipewire-pulse to guarantee the sandbox +security. + +This patch adds support for sandboxing permissions in the +pulseaudio module, and implements support for the SNAP audio +security model, thus forbiding a SNAP application to record +audio unless it has permissions to do so. + +The current code for pipewire-pulseaudio checks the permissions +of the snap and adds three properties to each new client: + + * pipewire.snap.id: contains the Snap ID of the client. + + * pipewire.snap.audio.playback: its value is 'true' if the client + has permission to play audio, or 'false' if not. + + * pipewire.snap.audio.record: its value is 'true' if the client + has permission to record audio, or 'false' if not. + +These properties must be processed by wireplumber to add or +remove access permissions to the corresponding nodes. That +code is available in a separate patch: https://gitlab.freedesktop.org/pipewire/wireplumber/-/merge_requests/567 + +(cherry picked from commit d568dcd64f64454289e1f35ed07a11749f95b04e) +Origin: upstream, after 1.0.1 +--- + .gitlab-ci.yml | 18 +++++++++++------- + meson.build | 16 ++++++++++++++++ + meson_options.txt | 4 ++++ + src/modules/meson.build | 7 +++++++ + src/modules/module-protocol-pulse/server.c | 21 +++++++++++++++++++++ + 5 files changed, 59 insertions(+), 7 deletions(-) + +diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml +index e0fce1c..87b3852 100644 +--- a/.gitlab-ci.yml ++++ b/.gitlab-ci.yml +@@ -96,6 +96,7 @@ include: + debhelper-compat + findutils + git ++ libapparmor-dev + libasound2-dev + libavcodec-dev + libavfilter-dev +@@ -107,6 +108,7 @@ include: + libgstreamer-plugins-base1.0-dev + libsbc-dev + libsdl2-dev ++ libsnapd-glib-dev + libudev-dev + libva-dev + libv4l-dev +@@ -247,7 +249,7 @@ build_on_ubuntu: + - .build + stage: build + variables: +- MESON_OPTIONS: "-Dsession-managers=[]" ++ MESON_OPTIONS: "-Dsession-managers=[] -Dsnap=enabled" + + .build_on_fedora: + extends: +@@ -274,6 +276,7 @@ build_on_fedora: + -Dsdl2=enabled + -Dsndfile=enabled + -Dsession-managers=[] ++ -Dsnap=disabled + artifacts: + name: pipewire-$CI_COMMIT_SHA + when: always +@@ -289,7 +292,7 @@ build_on_alpine: + - .build + stage: build + variables: +- MESON_OPTIONS: "-Dsession-managers=[]" ++ MESON_OPTIONS: "-Dsession-managers=[] -Dsnap=disabled" + + # build with all auto() options enabled + build_all: +@@ -308,6 +311,7 @@ build_all: + -Dsession-managers=[] + -Dc_args=['-UFASTPATH'] + -Dcpp_args=['-UFASTPATH'] ++ -Dsnap=disabled + parallel: + matrix: + - CC: [gcc, clang] +@@ -317,7 +321,7 @@ build_with_no_commandline_options: + extends: + - .build_on_fedora + variables: +- MESON_OPTIONS: "-Dsession-managers=[]" ++ MESON_OPTIONS: "-Dsession-managers=[] -Dsnap=disabled" + parallel: + matrix: + - CC: [gcc, clang] +@@ -353,7 +357,7 @@ build_release: + extends: + - .build_on_fedora + variables: +- MESON_OPTIONS: "-Dtest=enabled -Dbuildtype=release -Db_ndebug=true -Dsession-managers=[]" ++ MESON_OPTIONS: "-Dtest=enabled -Dbuildtype=release -Db_ndebug=true -Dsession-managers=[] -Dsnap=disabled" + parallel: + matrix: + - CC: [gcc, clang] +@@ -367,7 +371,7 @@ build_session_managers: + - meson compile -C "$BUILD_DIR" $COMPILE_ARGS + - meson install -C "$BUILD_DIR" --no-rebuild + variables: +- MESON_OPTIONS: "-Dsession-managers=$SESSION_MANAGERS" ++ MESON_OPTIONS: "-Dsession-managers=$SESSION_MANAGERS -Dsnap=disabled" + parallel: + matrix: + - SESSION_MANAGERS: ["[]", "wireplumber", "media-session", "media-session,wireplumber", "wireplumber,media-session" ] +@@ -384,7 +388,7 @@ build_meson_prerelease: + - meson compile -C "$BUILD_DIR" $COMPILE_ARGS + - meson install -C "$BUILD_DIR" --no-rebuild + variables: +- MESON_OPTIONS: "-Dsession-managers=wireplumber,media-session" ++ MESON_OPTIONS: "-Dsession-managers=wireplumber,media-session -Dsnap=disabled" + allow_failure: true + + build_meson_exact_release: +@@ -402,7 +406,7 @@ build_meson_exact_release: + - meson compile -C "$BUILD_DIR" $COMPILE_ARGS + - meson install -C "$BUILD_DIR" --no-rebuild + variables: +- MESON_OPTIONS: "-Dsession-managers=[]" ++ MESON_OPTIONS: "-Dsession-managers=[] -Dsnap=disabled" + + valgrind: + extends: +diff --git a/meson.build b/meson.build +index eb76fb9..ccdd998 100644 +--- a/meson.build ++++ b/meson.build +@@ -430,6 +430,22 @@ summary({'lilv (for lv2 plugins)': lilv_lib.found()}, bool_yn: true) + + libffado_dep = dependency('libffado', required: get_option('libffado')) + summary({'ffado': libffado_dep.found()}, bool_yn: true) ++glib2_snap_dep = dependency('glib-2.0', required : get_option('snap')) ++gio2_snap_dep = dependency('gio-2.0', required : get_option('snap')) ++apparmor_snap_dep = dependency('libapparmor', required : get_option('snap')) ++if dependency('snapd-glib-2', required: false).found() ++ snap_dep = dependency('snapd-glib-2', required : get_option('snap')) ++else ++ snap_dep = dependency('snapd-glib', required : get_option('snap')) ++endif ++if snap_dep.found() and glib2_snap_dep.found() and gio2_snap_dep.found() and apparmor_snap_dep.found() ++ cdata.set('HAVE_SNAP', 1) ++ snap_deps = [glib2_snap_dep, gio2_snap_dep, snap_dep, apparmor_snap_dep] ++endif ++summary({'GLib-2.0 (Snap support)': glib2_snap_dep.found()}, bool_yn: true, section: 'Misc dependencies') ++summary({'Gio-2.0 (Snap support)': gio2_snap_dep.found()}, bool_yn: true, section: 'Misc dependencies') ++summary({'Apparmor (Snap support)': apparmor_snap_dep.found()}, bool_yn: true, section: 'Misc dependencies') ++summary({'Snapd-glib (Snap support)': snap_dep.found()}, bool_yn: true, section: 'Misc dependencies') + + check_functions = [ + ['gettid', '#include <unistd.h>', ['-D_GNU_SOURCE'], []], +diff --git a/meson_options.txt b/meson_options.txt +index a4b0ab5..e0937b4 100644 +--- a/meson_options.txt ++++ b/meson_options.txt +@@ -330,3 +330,7 @@ option('libffado', + description: 'Enable code that depends on libffado', + type: 'feature', + value: 'auto') ++option('snap', ++ description : 'Snap support is available.', ++ type : 'feature', ++ value : 'auto') +diff --git a/src/modules/meson.build b/src/modules/meson.build +index 1b434b7..0909f24 100644 +--- a/src/modules/meson.build ++++ b/src/modules/meson.build +@@ -393,6 +393,13 @@ pipewire_module_protocol_pulse_sources = [ + 'module-protocol-pulse/modules/module-zeroconf-discover.c', + ] + ++if snap_dep.found() and glib2_snap_dep.found() and gio2_snap_dep.found() and apparmor_snap_dep.found() ++ pipewire_module_protocol_pulse_sources += [ ++ 'module-protocol-pulse/snap-policy.c', ++ ] ++ pipewire_module_protocol_pulse_deps += snap_deps ++endif ++ + if dbus_dep.found() + pipewire_module_protocol_pulse_sources += [ + 'module-protocol-pulse/dbus-name.c', +diff --git a/src/modules/module-protocol-pulse/server.c b/src/modules/module-protocol-pulse/server.c +index 53b1a7e..6ae97b7 100644 +--- a/src/modules/module-protocol-pulse/server.c ++++ b/src/modules/module-protocol-pulse/server.c +@@ -42,6 +42,9 @@ + #include "stream.h" + #include "utils.h" + #include "flatpak-utils.h" ++#ifdef HAVE_SNAP ++#include "snap-policy.h" ++#endif + + #define LISTEN_BACKLOG 32 + #define MAX_CLIENTS 64 +@@ -406,6 +409,9 @@ on_connect(void *data, int fd, uint32_t mask) + + if (server->addr.ss_family == AF_UNIX) { + spa_autofree char *app_id = NULL, *devices = NULL; ++#ifdef HAVE_SNAP ++ pw_sandbox_access_t snap_access; ++#endif + + #ifdef SO_PRIORITY + val = 6; +@@ -444,6 +450,21 @@ on_connect(void *data, int fd, uint32_t mask) + else + pw_properties_set(client->props, PW_KEY_MEDIA_CATEGORY, NULL); + } ++ // check SNAP permissions ++#ifdef HAVE_SNAP ++ snap_access = pw_snap_get_audio_permissions(client, client_fd, &app_id); ++ if ((snap_access & PW_SANDBOX_ACCESS_NOT_A_SANDBOX) == 0) { ++ pw_properties_set(client->props, PW_KEY_SNAP_ID, app_id); ++ ++ pw_properties_set(client->props, ++ PW_KEY_SNAP_PLAYBACK_ALLOWED, ++ (snap_access & PW_SANDBOX_ACCESS_PLAYBACK) ? "true" : "false"); ++ ++ pw_properties_set(client->props, ++ PW_KEY_SNAP_RECORD_ALLOWED, ++ (snap_access & PW_SANDBOX_ACCESS_RECORD) ? "true" : "false"); ++ } ++#endif + } + else if (server->addr.ss_family == AF_INET || server->addr.ss_family == AF_INET6) { + diff --git a/debian/patches/snap/snap-policy-Manage-ENOPROTOOPT-error-in-aa_getpeercon.patch b/debian/patches/snap/snap-policy-Manage-ENOPROTOOPT-error-in-aa_getpeercon.patch new file mode 100644 index 0000000000000000000000000000000000000000..214179780a0b1d35140b2f814550c4f01211b04c --- /dev/null +++ b/debian/patches/snap/snap-policy-Manage-ENOPROTOOPT-error-in-aa_getpeercon.patch @@ -0,0 +1,31 @@ +From: Sergio Costas <sergio.costas@canonical.com> +Date: Tue, 30 Jan 2024 10:28:27 +0000 +Subject: snap-policy: Manage ENOPROTOOPT error in aa_getpeercon() + +Bug-Ubuntu: https://launchpad.net/bugs/2051504 + +(cherry picked from commit e8fcaa5157506e5def0f45dc135ad97da75237f1) +Origin: upstream, after 1.0.1 +--- + src/modules/module-protocol-pulse/snap-policy.c | 8 +++++++- + 1 file changed, 7 insertions(+), 1 deletion(-) + +diff --git a/src/modules/module-protocol-pulse/snap-policy.c b/src/modules/module-protocol-pulse/snap-policy.c +index f027f5c..48084b8 100644 +--- a/src/modules/module-protocol-pulse/snap-policy.c ++++ b/src/modules/module-protocol-pulse/snap-policy.c +@@ -65,7 +65,13 @@ pw_sandbox_access_t pw_snap_get_audio_permissions(struct client *client, int fd, + // if apparmor isn't enabled, we can safely assume that there are no SNAPs in the system + return PW_SANDBOX_ACCESS_NOT_A_SANDBOX; + } +- pw_log_warn("snap_get_audio_permissions: failed to get the AppArmor info."); ++ if (errno == ENOPROTOOPT) { ++ // if fine grained unix mediation isn't available, we can't know if this is a snap or ++ // not, so we have no choice but give full access ++ pw_log_warn("snap_get_audio_permissions: kernel lacks 'fine grained unix mediation'; snap audio permissions won't be honored."); ++ return PW_SANDBOX_ACCESS_NOT_A_SANDBOX; ++ } ++ pw_log_warn("snap_get_audio_permissions: failed to get the AppArmor info: %s.", strerror(errno)); + return PW_SANDBOX_ACCESS_NONE; + } + if (!g_str_has_prefix(aa_label, SNAP_LABEL_PREFIX)) { diff --git a/debian/rules b/debian/rules index 150cbd4af35d4844a30741ef525daf79be8094a4..b1fa5749c098074abf619825249c3b346f694bcc 100755 --- a/debian/rules +++ b/debian/rules @@ -46,11 +46,13 @@ LIBFFADO=enabled endif ifneq (,$(filter hurd-amd64 hurd-i386,$(DEB_HOST_ARCH))) +SNAP=disabled UDEVRULESDIR= else # export UDEVRULESDIR=/usr/lib/udev/rules.d # For pre-Trixie releases udev rules should go in: export UDEVRULESDIR=/lib/udev/rules.d +SNAP=enabled endif override_dh_auto_configure: @@ -76,6 +78,7 @@ override_dh_auto_configure: -Droc=disabled \ -Dsdl2=$(SDL2) \ -Dsession-managers= \ + -Dsnap=$(SNAP) \ -Dtest=enabled \ -Dudevrulesdir=$(UDEVRULESDIR) \ -Dvideotestsrc=enabled \ @@ -100,9 +103,6 @@ override_dh_auto_test: --timeout-multiplier $(test_timeout_multiplier) \ $(NULL) -override_dh_missing: - dh_missing --fail-missing - override_dh_makeshlibs: dh_makeshlibs \ --exclude=/usr/lib/$(DEB_HOST_MULTIARCH)/gstreamer-1.0 \ diff --git a/meson.build b/meson.build index eb76fb9eed8f3f4d9d77366cf5fef7431c23fa6b..b9019b8400f4f039fdd6ec99a4bcd663078931b9 100644 --- a/meson.build +++ b/meson.build @@ -1,5 +1,5 @@ project('pipewire', ['c' ], - version : '1.0.1', + version : '1.0.3', license : [ 'MIT', 'LGPL-2.1-or-later', 'GPL-2.0-only' ], meson_version : '>= 0.61.1', default_options : [ 'warning_level=3', diff --git a/pipewire-alsa/alsa-plugins/pcm_pipewire.c b/pipewire-alsa/alsa-plugins/pcm_pipewire.c index 3464692dc7566feaa2f6cacc2037b301d6364bca..e5043aa949c2a86e4c83b96243bcee4d11d163f0 100644 --- a/pipewire-alsa/alsa-plugins/pcm_pipewire.c +++ b/pipewire-alsa/alsa-plugins/pcm_pipewire.c @@ -1298,7 +1298,7 @@ SND_PCM_PLUGIN_DEFINE_FUNC(pipewire) int err; pw_init(NULL, NULL); - if (strstr(pw_get_library_version(), "0.2") != NULL) + if (spa_strstartswith(pw_get_library_version(), "0.2")) return -ENOTSUP; props = pw_properties_new(NULL, NULL); diff --git a/pipewire-jack/src/pipewire-jack.c b/pipewire-jack/src/pipewire-jack.c index b891e60a3b36812aa20a3dc8f9c4d023558096cb..27c6d2555435640746d1a0e55d5cf09fd561ece6 100644 --- a/pipewire-jack/src/pipewire-jack.c +++ b/pipewire-jack/src/pipewire-jack.c @@ -3241,18 +3241,28 @@ static const struct pw_proxy_events proxy_events = { .destroy = proxy_destroy, }; +static bool node_is_active(struct client *c, struct object *n) +{ + return !n->node.is_jack || + (c->node_id == n->id ? c->active : n->node.is_running); +} + static void node_info(void *data, const struct pw_node_info *info) { struct object *n = data; struct client *c = n->client; - const char *str; + bool active; if (info->change_mask & PW_NODE_CHANGE_MASK_PROPS) { - str = spa_dict_lookup(info->props, PW_KEY_NODE_ALWAYS_PROCESS); + /* JACK clients always need ALWAYS_PROCESS=true or else they don't + * conform to the JACK API. We would try to hide the ports of + * PAUSED JACK clients, for example, even if they are active. */ + const char *str = spa_dict_lookup(info->props, PW_KEY_NODE_ALWAYS_PROCESS); n->node.is_jack = str ? spa_atob(str) : false; } - n->node.is_running = !n->node.is_jack || (info->state == PW_NODE_STATE_RUNNING); + n->node.is_running = info->state == PW_NODE_STATE_RUNNING; + active = node_is_active(c, n); pw_log_debug("DSP node %d %08"PRIx64" jack:%u state change %s running:%d", info->id, info->change_mask, n->node.is_jack, @@ -3264,7 +3274,7 @@ static void node_info(void *data, const struct pw_node_info *info) if (p->type != INTERFACE_Port || p->removed || p->port.node_id != info->id) continue; - if (n->node.is_running) + if (active) queue_notify(c, NOTIFY_TYPE_PORTREGISTRATION, p, 1, NULL); else { spa_list_for_each(l, &c->context.objects, link) { @@ -3492,7 +3502,7 @@ static void registry_event_global(void *data, uint32_t id, o->port.latency[SPA_DIRECTION_INPUT] = SPA_LATENCY_INFO(SPA_DIRECTION_INPUT); o->port.latency[SPA_DIRECTION_OUTPUT] = SPA_LATENCY_INFO(SPA_DIRECTION_OUTPUT); - do_emit = !ot->node.is_jack || ot->node.is_running; + do_emit = node_is_active(c, ot); o->proxy = pw_registry_bind(c->registry, id, type, PW_VERSION_PORT, 0); @@ -3785,7 +3795,7 @@ jack_client_t * jack_client_open (const char *client_name, if (getenv("PIPEWIRE_NOJACK") != NULL || getenv("PIPEWIRE_INTERNAL") != NULL || - strstr(pw_get_library_version(), "0.2") != NULL) + spa_strstartswith(pw_get_library_version(), "0.2")) goto disabled; return_val_if_fail(client_name != NULL, NULL); diff --git a/spa/include/spa/utils/defs.h b/spa/include/spa/utils/defs.h index 8940b52df7721ca5807880a57d62dfe4874130e6..393b1f33468b95381cab1412d4d188f0301e0d9f 100644 --- a/spa/include/spa/utils/defs.h +++ b/spa/include/spa/utils/defs.h @@ -156,6 +156,10 @@ struct spa_fraction { ({ \ fminf(fmaxf(v, low), high); \ }) +#define SPA_CLAMPD(v,low,high) \ +({ \ + fmin(fmax(v, low), high); \ +}) #define SPA_SWAP(a,b) \ diff --git a/spa/meson.build b/spa/meson.build index 0ee750d6eb052f5bf378a5d12a066f36f2e82637..db0a844258b0e9526be071daf9b0d5c8b1f92329 100644 --- a/spa/meson.build +++ b/spa/meson.build @@ -96,9 +96,8 @@ if get_option('spa-plugins').allowed() endif summary({'Vulkan': have_vulkan}, bool_yn: true, section: 'Misc dependencies') - libcamera_dep = dependency('libcamera', required: get_option('libcamera')) + libcamera_dep = dependency('libcamera', version: '>= 0.2.0', required: get_option('libcamera')) summary({'libcamera': libcamera_dep.found()}, bool_yn: true, section: 'Backend') - cdata.set('HAVE_LIBCAMERA_SYSTEM_DEVICES', libcamera_dep.version().version_compare('>= 0.1.0')) compress_offload_option = get_option('compress-offload') summary({'Compress-Offload': compress_offload_option.allowed()}, bool_yn: true, section: 'Backend') diff --git a/spa/plugins/alsa/alsa-pcm-sink.c b/spa/plugins/alsa/alsa-pcm-sink.c index aa8105ab1a4164a2798ccbcedd20ae534757cf1b..2d595122750dc43c4e359c54b2a18681198b3498 100644 --- a/spa/plugins/alsa/alsa-pcm-sink.c +++ b/spa/plugins/alsa/alsa-pcm-sink.c @@ -458,14 +458,19 @@ impl_node_port_enum_params(void *object, int seq, break; case SPA_PARAM_Buffers: + { + uint32_t min_buffers; + if (!this->have_format) return -EIO; if (result.index > 0) return 0; + min_buffers = (this->quantum_limit * 4 * this->frame_scale) > this->buffer_frames ? 2 : 1; + param = spa_pod_builder_add_object(&b.b, SPA_TYPE_OBJECT_ParamBuffers, id, - SPA_PARAM_BUFFERS_buffers, SPA_POD_CHOICE_RANGE_Int(1, 1, MAX_BUFFERS), + SPA_PARAM_BUFFERS_buffers, SPA_POD_CHOICE_RANGE_Int(min_buffers, min_buffers, MAX_BUFFERS), SPA_PARAM_BUFFERS_blocks, SPA_POD_Int(this->blocks), SPA_PARAM_BUFFERS_size, SPA_POD_CHOICE_RANGE_Int( this->quantum_limit * this->frame_size * this->frame_scale, @@ -473,6 +478,7 @@ impl_node_port_enum_params(void *object, int seq, INT32_MAX), SPA_PARAM_BUFFERS_stride, SPA_POD_Int(this->frame_size)); break; + } case SPA_PARAM_Meta: switch (result.index) { diff --git a/spa/plugins/alsa/alsa-pcm.c b/spa/plugins/alsa/alsa-pcm.c index 7774bfd4258ec0e8bd709365c6b59e9032727c45..fd1057eae9ae72661817f792af7b98ac5b4f7eb0 100644 --- a/spa/plugins/alsa/alsa-pcm.c +++ b/spa/plugins/alsa/alsa-pcm.c @@ -3476,8 +3476,8 @@ int spa_alsa_reassign_follower(struct state *state) if (following != state->following) { spa_log_debug(state->log, "%p: reassign follower %d->%d", state, state->following, following); state->following = following; - setup_matching(state); } + setup_matching(state); if (state->started) spa_loop_invoke(state->data_loop, do_state_sync, 0, NULL, 0, true, state); @@ -3534,7 +3534,8 @@ void spa_alsa_emit_node_info(struct state *state, bool full) state->stream == SND_PCM_STREAM_PLAYBACK ? "Audio/Sink" : "Audio/Source"); items[n_items++] = SPA_DICT_ITEM_INIT(SPA_KEY_NODE_DRIVER, "true"); if (state->have_format) { - snprintf(latency, sizeof(latency), "%lu/%d", state->buffer_frames / 2, state->rate); + snprintf(latency, sizeof(latency), "%lu/%d", + state->buffer_frames / (2 * state->frame_scale), state->rate); items[n_items++] = SPA_DICT_ITEM_INIT(SPA_KEY_NODE_MAX_LATENCY, latency); snprintf(period, sizeof(period), "%lu", state->period_frames); items[n_items++] = SPA_DICT_ITEM_INIT("api.alsa.period-size", period); diff --git a/spa/plugins/audioconvert/resample-native-avx.c b/spa/plugins/audioconvert/resample-native-avx.c index c4b4c605b81b395eee2f7684c0e515626e8bb6c5..a57d668b782aa3e84137bf4503252001140cacc8 100644 --- a/spa/plugins/audioconvert/resample-native-avx.c +++ b/spa/plugins/audioconvert/resample-native-avx.c @@ -16,18 +16,18 @@ static inline void inner_product_avx(float *d, const float * SPA_RESTRICT s, uint32_t n_taps4 = n_taps & ~0xf; for (; i < n_taps4; i += 16) { - ty = (__m256)_mm256_lddqu_si256((__m256i*)(s + i + 0)); + ty = _mm256_loadu_ps(s + i + 0); sy[0] = _mm256_fmadd_ps(ty, _mm256_load_ps(taps + i + 0), sy[0]); - ty = (__m256)_mm256_lddqu_si256((__m256i*)(s + i + 8)); + ty = _mm256_loadu_ps(s + i + 8); sy[1] = _mm256_fmadd_ps(ty, _mm256_load_ps(taps + i + 8), sy[1]); } sy[0] = _mm256_add_ps(sy[1], sy[0]); sx[1] = _mm256_extractf128_ps(sy[0], 1); sx[0] = _mm256_extractf128_ps(sy[0], 0); for (; i < n_taps; i += 8) { - tx = (__m128)_mm_lddqu_si128((__m128i*)(s + i + 0)); + tx = _mm_loadu_ps(s + i + 0); sx[0] = _mm_fmadd_ps(tx, _mm_load_ps(taps + i + 0), sx[0]); - tx = (__m128)_mm_lddqu_si128((__m128i*)(s + i + 4)); + tx = _mm_loadu_ps(s + i + 4); sx[1] = _mm_fmadd_ps(tx, _mm_load_ps(taps + i + 4), sx[1]); } sx[0] = _mm_add_ps(sx[0], sx[1]); @@ -45,10 +45,10 @@ static inline void inner_product_ip_avx(float *d, const float * SPA_RESTRICT s, uint32_t i, n_taps4 = n_taps & ~0xf; for (i = 0; i < n_taps4; i += 16) { - ty = (__m256)_mm256_lddqu_si256((__m256i*)(s + i + 0)); + ty = _mm256_loadu_ps(s + i + 0); sy[0] = _mm256_fmadd_ps(ty, _mm256_load_ps(t0 + i + 0), sy[0]); sy[1] = _mm256_fmadd_ps(ty, _mm256_load_ps(t1 + i + 0), sy[1]); - ty = (__m256)_mm256_lddqu_si256((__m256i*)(s + i + 8)); + ty = _mm256_loadu_ps(s + i + 8); sy[0] = _mm256_fmadd_ps(ty, _mm256_load_ps(t0 + i + 8), sy[0]); sy[1] = _mm256_fmadd_ps(ty, _mm256_load_ps(t1 + i + 8), sy[1]); } @@ -56,10 +56,10 @@ static inline void inner_product_ip_avx(float *d, const float * SPA_RESTRICT s, sx[1] = _mm_add_ps(_mm256_extractf128_ps(sy[1], 0), _mm256_extractf128_ps(sy[1], 1)); for (; i < n_taps; i += 8) { - tx = (__m128)_mm_lddqu_si128((__m128i*)(s + i + 0)); + tx = _mm_loadu_ps(s + i + 0); sx[0] = _mm_fmadd_ps(tx, _mm_load_ps(t0 + i + 0), sx[0]); sx[1] = _mm_fmadd_ps(tx, _mm_load_ps(t1 + i + 0), sx[1]); - tx = (__m128)_mm_lddqu_si128((__m128i*)(s + i + 4)); + tx = _mm_loadu_ps(s + i + 4); sx[0] = _mm_fmadd_ps(tx, _mm_load_ps(t0 + i + 4), sx[0]); sx[1] = _mm_fmadd_ps(tx, _mm_load_ps(t1 + i + 4), sx[1]); } diff --git a/spa/plugins/avb/avb-pcm-sink.c b/spa/plugins/avb/avb-pcm-sink.c index 2c77483e3eb7e39ddf23a0b8799bd4d24d9ec328..6b9e4856cb298d1fd8a8b7464170a3a530f6e1c6 100644 --- a/spa/plugins/avb/avb-pcm-sink.c +++ b/spa/plugins/avb/avb-pcm-sink.c @@ -557,7 +557,7 @@ impl_node_port_set_param(void *object, { struct state *this = object; struct port *port; - int res; + int res = 0; spa_return_val_if_fail(this != NULL, -EINVAL); diff --git a/spa/plugins/avb/avb-pcm-source.c b/spa/plugins/avb/avb-pcm-source.c index 1fa48dfdb106bc3809408cb902da11d2325612c9..9811c24314db8ccb0f4bef71978ef163a9d05d6c 100644 --- a/spa/plugins/avb/avb-pcm-source.c +++ b/spa/plugins/avb/avb-pcm-source.c @@ -557,7 +557,7 @@ impl_node_port_set_param(void *object, { struct state *this = object; struct port *port; - int res; + int res = 0; spa_return_val_if_fail(this != NULL, -EINVAL); diff --git a/spa/plugins/bluez5/bap-codec-lc3.c b/spa/plugins/bluez5/bap-codec-lc3.c index 1cffbeddeeb1faf00eea0d21e53486a9cd7a0317..0226d14e2eba8c1da797cb2923e8fd67ac34a493 100644 --- a/spa/plugins/bluez5/bap-codec-lc3.c +++ b/spa/plugins/bluez5/bap-codec-lc3.c @@ -600,22 +600,22 @@ static int codec_enum_config(const struct media_codec *codec, uint32_t flags, spa_pod_builder_push_choice(b, &f[1], SPA_CHOICE_None, 0); choice = (struct spa_pod_choice*)spa_pod_builder_frame(b, &f[1]); i = 0; - if (conf.rate & LC3_CONFIG_FREQ_48KHZ) { + if (conf.rate == LC3_CONFIG_FREQ_48KHZ) { if (i++ == 0) spa_pod_builder_int(b, 48000); spa_pod_builder_int(b, 48000); } - if (conf.rate & LC3_CONFIG_FREQ_24KHZ) { + if (conf.rate == LC3_CONFIG_FREQ_24KHZ) { if (i++ == 0) spa_pod_builder_int(b, 24000); spa_pod_builder_int(b, 24000); } - if (conf.rate & LC3_CONFIG_FREQ_16KHZ) { + if (conf.rate == LC3_CONFIG_FREQ_16KHZ) { if (i++ == 0) spa_pod_builder_int(b, 16000); spa_pod_builder_int(b, 16000); } - if (conf.rate & LC3_CONFIG_FREQ_8KHZ) { + if (conf.rate == LC3_CONFIG_FREQ_8KHZ) { if (i++ == 0) spa_pod_builder_int(b, 8000); spa_pod_builder_int(b, 8000); diff --git a/spa/plugins/bluez5/bluez-hardware.conf b/spa/plugins/bluez5/bluez-hardware.conf index 28b949570c23c6a080d871ce9de9962317bbf868..34abe6ff82a89d36a81aab8eac7e9fd9b9c4b65a 100644 --- a/spa/plugins/bluez5/bluez-hardware.conf +++ b/spa/plugins/bluez5/bluez-hardware.conf @@ -40,9 +40,11 @@ bluez5.features.device = [ { name = "Motorola DC800", no-features = [ sbc-xq ] }, # #pipewire-1590 { name = "Motorola S305", no-features = [ sbc-xq ] }, # #pipewire-1590 { name = "PMK True Wireless Earbuds" no-features = [ sbc-xq ] }, # Primark earbud + { name = "Rockbox Brick", no-features = [ hw-volume ] }, # #pipewire-3786 { name = "Soundcore Life P2-L", no-features = [ msbc-alt1, msbc-alt1-rtl ] }, { name = "Soundcore Motion B", no-features = [ hw-volume ] }, { name = "SoundCore mini", no-features = [ hw-volume ] }, # #pipewire-1686 + { name = "SoundCore mini2", no-features = [ hw-volume ] }, # #pipewire-2927 { name = "SoundCore 2", no-features = [ sbc-xq ] }, # #pipewire-2291 { name = "Tribit MAXSound Plus", no-features = [ hw-volume ] }, # #pipewire-1592 { name = "Urbanista Stockholm Plus", no-features = [ msbc-alt1, msbc-alt1-rtl ] }, diff --git a/spa/plugins/bluez5/bluez5-dbus.c b/spa/plugins/bluez5/bluez5-dbus.c index bb6e0a8b336d8cc0b03578ff3fb780a91507fc46..82a2c85bface9442756eb1c6b46c681b545f29f2 100644 --- a/spa/plugins/bluez5/bluez5-dbus.c +++ b/spa/plugins/bluez5/bluez5-dbus.c @@ -5545,7 +5545,12 @@ static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *m, void *us transport = spa_bt_transport_find(monitor, path); if (transport == NULL) { spa_log_warn(monitor->log, - "Properties changed in unknown transport %s", path); + "Properties changed in unknown transport '%s'. " + "Multiple sound server instances (PipeWire/Pulseaudio/bluez-alsa) are " + "probably trying to use Bluetooth audio at the same time, which can " + "cause problems. The system configuration likely should be fixed " + "to have only one sound server that manages Bluetooth audio.", + path); goto finish; } diff --git a/spa/plugins/libcamera/libcamera-device.cpp b/spa/plugins/libcamera/libcamera-device.cpp index 0abf2f619520f4eeb44f0aece14ee8e2895d8940..b25a4eb728d8312b789a5d2d72ee462b94f231bc 100644 --- a/spa/plugins/libcamera/libcamera-device.cpp +++ b/spa/plugins/libcamera/libcamera-device.cpp @@ -61,12 +61,10 @@ struct impl { static const libcamera::Span<const int64_t> cameraDevice( const Camera *camera) { -#ifdef HAVE_LIBCAMERA_SYSTEM_DEVICES const ControlList &props = camera->properties(); if (auto devices = props.get(properties::SystemDevices)) return devices.value(); -#endif return {}; } diff --git a/spa/plugins/libcamera/libcamera-utils.cpp b/spa/plugins/libcamera/libcamera-utils.cpp index 2b1aea5a76bf47a7893236e6bbba170f92da6954..c197248d30ef25c322f6279be1d48e0339471560 100644 --- a/spa/plugins/libcamera/libcamera-utils.cpp +++ b/spa/plugins/libcamera/libcamera-utils.cpp @@ -716,25 +716,23 @@ static int spa_libcamera_use_buffers(struct impl *impl, struct port *port, } static const struct { - Transform libcamera_transform; - uint32_t spa_transform_value; -} transform_map[] = { - { Transform::Identity, SPA_META_TRANSFORMATION_None }, - { Transform::Rot0, SPA_META_TRANSFORMATION_None }, - { Transform::HFlip, SPA_META_TRANSFORMATION_Flipped }, - { Transform::VFlip, SPA_META_TRANSFORMATION_Flipped180 }, - { Transform::HVFlip, SPA_META_TRANSFORMATION_180 }, - { Transform::Rot180, SPA_META_TRANSFORMATION_180 }, - { Transform::Transpose, SPA_META_TRANSFORMATION_Flipped90 }, - { Transform::Rot90, SPA_META_TRANSFORMATION_90 }, - { Transform::Rot270, SPA_META_TRANSFORMATION_270 }, - { Transform::Rot180Transpose, SPA_META_TRANSFORMATION_Flipped270 }, + Orientation libcamera_orientation; /* clockwise rotation then horizontal mirroring */ + uint32_t spa_transform_value; /* horizontal mirroring then counter-clockwise rotation */ +} orientation_map[] = { + { Orientation::Rotate0, SPA_META_TRANSFORMATION_None }, + { Orientation::Rotate0Mirror, SPA_META_TRANSFORMATION_Flipped }, + { Orientation::Rotate90, SPA_META_TRANSFORMATION_270 }, + { Orientation::Rotate90Mirror, SPA_META_TRANSFORMATION_Flipped90 }, + { Orientation::Rotate180, SPA_META_TRANSFORMATION_180 }, + { Orientation::Rotate180Mirror, SPA_META_TRANSFORMATION_Flipped180 }, + { Orientation::Rotate270, SPA_META_TRANSFORMATION_90 }, + { Orientation::Rotate270Mirror, SPA_META_TRANSFORMATION_Flipped270 }, }; -static uint32_t libcamera_transform_to_spa_transform_value(Transform transform) +static uint32_t libcamera_orientation_to_spa_transform_value(Orientation orientation) { - for (const auto& t : transform_map) { - if (t.libcamera_transform == transform) + for (const auto& t : orientation_map) { + if (t.libcamera_orientation == orientation) return t.spa_transform_value; } return SPA_META_TRANSFORMATION_None; @@ -788,9 +786,9 @@ mmap_init(struct impl *impl, struct port *port, buffers[i], SPA_META_VideoTransform, sizeof(*b->videotransform)); if (b->videotransform) { b->videotransform->transform = - libcamera_transform_to_spa_transform_value(impl->config->transform); - spa_log_debug(impl->log, "Setting videotransform for buffer %d to %u (from %s)", - i, b->videotransform->transform, transformToString(impl->config->transform)); + libcamera_orientation_to_spa_transform_value(impl->config->orientation); + spa_log_debug(impl->log, "Setting videotransform for buffer %u to %u", + i, b->videotransform->transform); } diff --git a/spa/plugins/support/node-driver.c b/spa/plugins/support/node-driver.c index dc36c821660c104eb6d0f29dfc052d69825a10fb..df9b9cfdb8dfed1f42ce6868fce72b723fc07310 100644 --- a/spa/plugins/support/node-driver.c +++ b/spa/plugins/support/node-driver.c @@ -33,6 +33,7 @@ #define DEFAULT_FREEWHEEL_WAIT 10 #define DEFAULT_CLOCK_PREFIX "clock.system" #define DEFAULT_CLOCK_ID CLOCK_MONOTONIC +#define DEFAULT_RESYNC_MS 10 #define CLOCKFD 3 #define FD_TO_CLOCKID(fd) ((~(clockid_t) (fd) << 3) | CLOCKFD) @@ -46,6 +47,7 @@ struct props { char clock_name[64]; clockid_t clock_id; uint32_t freewheel_wait; + float resync_ms; }; struct impl { @@ -81,6 +83,7 @@ struct impl { uint64_t base_time; struct spa_dll dll; double max_error; + double max_resync; }; static void reset_props(struct props *props) @@ -89,6 +92,7 @@ static void reset_props(struct props *props) spa_zero(props->clock_name); props->clock_id = CLOCK_MONOTONIC; props->freewheel_wait = DEFAULT_FREEWHEEL_WAIT; + props->resync_ms = DEFAULT_RESYNC_MS; } static const struct clock_info { @@ -281,6 +285,7 @@ static void on_timeout(struct spa_source *source) if (this->last_time == 0) { spa_dll_set_bw(&this->dll, SPA_DLL_BW_MIN, duration, rate); this->max_error = rate * MAX_ERROR_MS / 1000; + this->max_resync = rate * this->props.resync_ms / 1000; position = current_position; } else if (SPA_LIKELY(this->clock)) { position = this->clock->position + this->clock->duration; @@ -288,21 +293,27 @@ static void on_timeout(struct spa_source *source) position = current_position; } - /* check the elapsed time of the other clock against - * the graph clock elapsed time, feed this error into the - * dll and adjust the timeout of our MONOTONIC clock. */ - err = (double)position - (double)current_position; - if (err > this->max_error) - err = this->max_error; - else if (err < -this->max_error) - err = -this->max_error; - this->last_time = current_time; if (this->props.freewheel) { corr = 1.0; this->next_time = nsec + this->props.freewheel_wait * SPA_NSEC_PER_SEC; } else if (this->tracking) { + /* check the elapsed time of the other clock against + * the graph clock elapsed time, feed this error into the + * dll and adjust the timeout of our MONOTONIC clock. */ + err = (double)position - (double)current_position; + if (fabs(err) > this->max_error) { + if (fabs(err) > this->max_resync) { + spa_log_warn(this->log, "err %f > max_resync %f, resetting", + err, this->max_resync); + spa_dll_set_bw(&this->dll, SPA_DLL_BW_MIN, duration, rate); + position = current_position; + err = 0.0; + } else { + err = SPA_CLAMPD(err, -this->max_error, this->max_error); + } + } corr = spa_dll_update(&this->dll, err); this->next_time = nsec + duration / corr * 1e9 / rate; } else { @@ -621,6 +632,8 @@ impl_init(const struct spa_handle_factory *factory, } } else if (spa_streq(k, "freewheel.wait")) { this->props.freewheel_wait = atoi(s); + } else if (spa_streq(k, "resync.ms")) { + this->props.resync_ms = atof(s); } } if (this->props.clock_name[0] == '\0') { diff --git a/spa/plugins/v4l2/v4l2-source.c b/spa/plugins/v4l2/v4l2-source.c index 413670eacc002c95fab5c750521aadf8ce618fa9..c532ea18591be0cd4ed8febeab3b08ce96759f4b 100644 --- a/spa/plugins/v4l2/v4l2-source.c +++ b/spa/plugins/v4l2/v4l2-source.c @@ -71,6 +71,7 @@ struct port { struct impl *impl; bool alloc_buffers; + bool probed_expbuf; bool have_expbuf; bool next_fmtdesc; @@ -715,7 +716,7 @@ static int impl_node_port_set_param(void *object, uint32_t id, uint32_t flags, const struct spa_pod *param) { - int res; + int res = 0; struct impl *this = object; struct port *port; @@ -1020,8 +1021,7 @@ impl_init(const struct spa_handle_factory *factory, port->info.params = port->params; port->info.n_params = N_PORT_PARAMS; - port->alloc_buffers = true; - port->have_expbuf = true; + port->probed_expbuf = false; port->have_query_ext_ctrl = true; port->dev.log = this->log; port->dev.fd = -1; diff --git a/spa/plugins/v4l2/v4l2-utils.c b/spa/plugins/v4l2/v4l2-utils.c index 8647ca9e3e897c4f33131bfc62f5c09548b3f29d..1ed46d62ce737a02fee1479a00575f099d7c1287 100644 --- a/spa/plugins/v4l2/v4l2-utils.c +++ b/spa/plugins/v4l2/v4l2-utils.c @@ -367,11 +367,11 @@ static const struct format_info *find_format_info_by_media_type(uint32_t type, return NULL; } -static uint32_t +static int enum_filter_format(uint32_t media_type, int32_t media_subtype, const struct spa_pod *filter, uint32_t index) { - uint32_t video_format = 0; + uint32_t video_format = SPA_VIDEO_FORMAT_UNKNOWN; switch (media_type) { case SPA_MEDIA_TYPE_video: @@ -383,12 +383,12 @@ enum_filter_format(uint32_t media_type, int32_t media_subtype, const uint32_t *values; if (!(p = spa_pod_find_prop(filter, NULL, SPA_FORMAT_VIDEO_format))) - return SPA_VIDEO_FORMAT_UNKNOWN; + return -ENOENT; val = spa_pod_get_values(&p->value, &n_values, &choice); if (val->type != SPA_TYPE_Id) - return SPA_VIDEO_FORMAT_UNKNOWN; + return -EINVAL; values = SPA_POD_BODY(val); @@ -503,7 +503,7 @@ spa_v4l2_enum_format(struct impl *this, int seq, int res, n_fractions; const struct format_info *info; struct spa_pod_choice *choice; - uint32_t filter_media_type, filter_media_subtype, video_format; + uint32_t filter_media_type, filter_media_subtype; struct spa_v4l2_device *dev = &port->dev; uint8_t buffer[1024]; struct spa_pod_builder b = { 0 }; @@ -545,16 +545,19 @@ spa_v4l2_enum_format(struct impl *this, int seq, if (filter) { struct v4l2_format fmt; - video_format = enum_filter_format(filter_media_type, + res = enum_filter_format(filter_media_type, filter_media_subtype, filter, port->fmtdesc.index); - - if (video_format == SPA_VIDEO_FORMAT_UNKNOWN) + if (res == -ENOENT) + goto do_enum_fmt; + if (res < 0) + goto exit; + if (res == SPA_VIDEO_FORMAT_UNKNOWN) goto enum_end; info = find_format_info_by_media_type(filter_media_type, filter_media_subtype, - video_format, 0); + res, 0); if (info == NULL) goto next_fmtdesc; @@ -580,6 +583,7 @@ spa_v4l2_enum_format(struct impl *this, int seq, } } else { +do_enum_fmt: if ((res = xioctl(dev->fd, VIDIOC_ENUM_FMT, &port->fmtdesc)) < 0) { if (errno == EINVAL) goto enum_end; @@ -815,6 +819,7 @@ spa_v4l2_enum_format(struct impl *this, int seq, port->frmival.discrete.denominator, port->frmival.discrete.numerator); port->frmival.index++; + n_fractions++; } else if (port->frmival.type == V4L2_FRMIVAL_TYPE_CONTINUOUS || port->frmival.type == V4L2_FRMIVAL_TYPE_STEPWISE) { if (n_fractions == 0) @@ -828,20 +833,23 @@ spa_v4l2_enum_format(struct impl *this, int seq, if (port->frmival.type == V4L2_FRMIVAL_TYPE_CONTINUOUS) { choice->body.type = SPA_CHOICE_Range; + n_fractions += 2; } else { choice->body.type = SPA_CHOICE_Step; spa_pod_builder_fraction(&b, port->frmival.stepwise.step.denominator, port->frmival.stepwise.step.numerator); + n_fractions += 3; } port->frmsize.index++; port->next_frmsize = true; break; } - n_fractions++; } - if (n_fractions <= 1) + if (n_fractions == 0) + goto next_frmsize; + if (n_fractions == 1) choice->body.type = SPA_CHOICE_None; spa_pod_builder_pop(&b, &f[1]); @@ -859,6 +867,48 @@ spa_v4l2_enum_format(struct impl *this, int seq, return res; } +static int probe_expbuf(struct impl *this) +{ + struct port *port = &this->out_ports[0]; + struct spa_v4l2_device *dev = &port->dev; + struct v4l2_requestbuffers reqbuf; + struct v4l2_exportbuffer expbuf; + + if (port->probed_expbuf) + return 0; + port->probed_expbuf = true; + + spa_zero(reqbuf); + reqbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + reqbuf.memory = V4L2_MEMORY_MMAP; + reqbuf.count = 2; + + if (xioctl(dev->fd, VIDIOC_REQBUFS, &reqbuf) < 0) { + spa_log_error(this->log, "'%s' VIDIOC_REQBUFS: %m", this->props.device); + return -errno; + } + + spa_zero(expbuf); + expbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + expbuf.index = 0; + expbuf.flags = O_CLOEXEC | O_RDONLY; + if (xioctl(dev->fd, VIDIOC_EXPBUF, &expbuf) < 0) { + spa_log_info(this->log, "'%s' EXPBUF not supported: %m", this->props.device); + port->have_expbuf = false; + port->alloc_buffers = false; + } else { + port->have_expbuf = true; + port->alloc_buffers = true; + } + + reqbuf.count = 0; + if (xioctl(dev->fd, VIDIOC_REQBUFS, &reqbuf) < 0) { + spa_log_error(this->log, "'%s' VIDIOC_REQBUFS: %m", this->props.device); + return -errno; + } + return 0; +} + static int spa_v4l2_set_format(struct impl *this, struct spa_video_info *format, uint32_t flags) { struct port *port = &this->out_ports[0]; @@ -965,6 +1015,8 @@ static int spa_v4l2_set_format(struct impl *this, struct spa_video_info *format, port->rate.denom = framerate->num = streamparm.parm.capture.timeperframe.denominator; port->rate.num = framerate->denom = streamparm.parm.capture.timeperframe.numerator; + probe_expbuf(this); + port->fmt = fmt; port->info.change_mask |= SPA_PORT_CHANGE_MASK_FLAGS | SPA_PORT_CHANGE_MASK_RATE; port->info.flags = (port->alloc_buffers ? SPA_PORT_FLAG_CAN_ALLOC_BUFFERS : 0) | @@ -1576,6 +1628,7 @@ mmap_init(struct impl *this, spa_log_debug(this->log, "data types %08x", d[0].type); +again: if (port->have_expbuf && d[0].type != SPA_ID_INVALID && (d[0].type & ((1u << SPA_DATA_DmaBuf)|(1u<<SPA_DATA_MemFd)))) { @@ -1590,7 +1643,7 @@ mmap_init(struct impl *this, spa_log_debug(this->log, "'%s' VIDIOC_EXPBUF not supported: %m", this->props.device); port->have_expbuf = false; - goto fallback; + goto again; } spa_log_error(this->log, "'%s' VIDIOC_EXPBUF: %m", this->props.device); return -errno; @@ -1606,7 +1659,6 @@ mmap_init(struct impl *this, spa_log_debug(this->log, "EXPBUF fd:%d", expbuf.fd); use_expbuf = true; } else if (d[0].type & (1u << SPA_DATA_MemPtr)) { -fallback: d[0].type = SPA_DATA_MemPtr; d[0].flags = SPA_DATA_FLAG_READABLE; d[0].fd = -1; @@ -1626,6 +1678,7 @@ fallback: use_expbuf = false; } else { spa_log_error(this->log, "unsupported data type:%08x", d[0].type); + port->alloc_buffers = false; return -ENOTSUP; } spa_v4l2_buffer_recycle(this, i); diff --git a/src/daemon/pipewire-aes67.conf.in b/src/daemon/pipewire-aes67.conf.in index 3b7e711e0fbe72b2c8d5ded865dbe76d7c604c77..fa2cd67f3329308e1e201b6a92f05a9e5153469b 100644 --- a/src/daemon/pipewire-aes67.conf.in +++ b/src/daemon/pipewire-aes67.conf.in @@ -39,6 +39,7 @@ context.objects = [ #clock.id = tai clock.device = "/dev/ptp0" #clock.interface = "eth0" + resync.ms = 1.5 object.export = true } } diff --git a/src/daemon/pipewire.conf.in b/src/daemon/pipewire.conf.in index a9fb1c35dd9e9f3430efd509131b2b0a3d078913..b47bbec44db45bc718d4a7d48a77cb407c9a986b 100644 --- a/src/daemon/pipewire.conf.in +++ b/src/daemon/pipewire.conf.in @@ -172,7 +172,7 @@ context.modules = [ # Use libcanberra to play X11 Bell { name = libpipewire-module-x11-bell args = { - #sink.name = "@DEFAULT_SINK@" + #sink.name = "\@DEFAULT_SINK\@" #sample.name = "bell-window-system" #x11.display = null #x11.xauthority = null diff --git a/src/modules/module-filter-chain/lv2_plugin.c b/src/modules/module-filter-chain/lv2_plugin.c index 3f036ceaff7bbd07574ae7ff5ad80b2e6fdf62a5..06b6ada5c61c820e9634099edd9ecd8081e81dd8 100644 --- a/src/modules/module-filter-chain/lv2_plugin.c +++ b/src/modules/module-filter-chain/lv2_plugin.c @@ -237,6 +237,7 @@ struct instance { const LV2_Worker_Interface *work_iface; int32_t block_length; + LV2_Atom empty_atom; }; static int @@ -284,7 +285,7 @@ static void *lv2_instantiate(const struct fc_descriptor *desc, struct plugin *p = d->p; struct context *c = p->c; struct instance *i; - uint32_t n_features = 0; + uint32_t n, n_features = 0; static const int32_t min_block_length = 1; static const int32_t max_block_length = 8192; static const int32_t seq_size = 32768; @@ -339,6 +340,12 @@ static void *lv2_instantiate(const struct fc_descriptor *desc, i->work_iface = (const LV2_Worker_Interface*) lilv_instance_get_extension_data(i->instance, LV2_WORKER__interface); } + for (n = 0; n < desc->n_ports; n++) { + const LilvPort *port = lilv_plugin_get_port_by_index(p->p, n); + if (lilv_port_is_a(p->p, port, c->atom_AtomPort)) { + lilv_instance_connect_port(i->instance, n, &i->empty_atom); + } + } return i; } diff --git a/src/modules/module-netjack2-driver.c b/src/modules/module-netjack2-driver.c index 88e6f84c2af3a7e1319edab2b1bf35fb787f6098..4753cc091ce573d0faa8001f5f2fdb5650281104 100644 --- a/src/modules/module-netjack2-driver.c +++ b/src/modules/module-netjack2-driver.c @@ -416,7 +416,7 @@ static void make_stream_ports(struct stream *s) if (i < s->info.channels) { str = spa_debug_type_find_short_name(spa_type_audio_channel, - s->info.position[i]); + s->info.position[i % SPA_AUDIO_MAX_CHANNELS]); if (str) snprintf(name, sizeof(name), "%s_%s", prefix, str); else @@ -819,6 +819,7 @@ static int handle_follower_setup(struct impl *impl, struct nj2_session_params *p { int res; struct netjack2_peer *peer = &impl->peer; + uint32_t i; pw_log_info("got follower setup"); nj2_dump_session_params(params); @@ -842,10 +843,18 @@ static int handle_follower_setup(struct impl *impl, struct nj2_session_params *p impl->source.n_ports = peer->params.send_audio_channels + peer->params.send_midi_channels; impl->source.info.rate = peer->params.sample_rate; - impl->source.info.channels = peer->params.send_audio_channels; + if ((uint32_t)peer->params.send_audio_channels != impl->source.info.channels) { + impl->source.info.channels = peer->params.send_audio_channels; + for (i = 0; i < SPA_MIN(impl->source.info.channels, SPA_AUDIO_MAX_CHANNELS); i++) + impl->source.info.position[i] = SPA_AUDIO_CHANNEL_AUX0 + i; + } impl->sink.n_ports = peer->params.recv_audio_channels + peer->params.recv_midi_channels; impl->sink.info.rate = peer->params.sample_rate; - impl->sink.info.channels = peer->params.recv_audio_channels; + if ((uint32_t)peer->params.recv_audio_channels != impl->sink.info.channels) { + impl->sink.info.channels = peer->params.recv_audio_channels; + for (i = 0; i < SPA_MIN(impl->sink.info.channels, SPA_AUDIO_MAX_CHANNELS); i++) + impl->sink.info.position[i] = SPA_AUDIO_CHANNEL_AUX0 + i; + } impl->samplerate = peer->params.sample_rate; impl->period_size = peer->params.period_size; @@ -920,7 +929,7 @@ on_socket_io(void *data, int fd, uint32_t mask) if (len < (int)sizeof(struct nj2_session_params)) goto short_packet; - if (strcmp(params.type, "params") != 0) + if (strncmp(params.type, "params", sizeof(params.type)) != 0) goto wrong_type; switch(ntohl(params.packet_id)) { diff --git a/src/modules/module-netjack2-manager.c b/src/modules/module-netjack2-manager.c index 3afe6a888d9f0837294bd35603961aa8b254c02d..31310ca0b77e036351d53db0d883fb52ad621642 100644 --- a/src/modules/module-netjack2-manager.c +++ b/src/modules/module-netjack2-manager.c @@ -434,7 +434,7 @@ on_setup_io(void *data, int fd, uint32_t mask) if (len < (int)sizeof(params)) goto short_packet; - if (strcmp(params.type, "params") != 0) + if (strncmp(params.type, "params", sizeof(params.type)) != 0) goto wrong_type; switch(ntohl(params.packet_id)) { @@ -1072,7 +1072,7 @@ on_socket_io(void *data, int fd, uint32_t mask) if (len < (int)sizeof(params)) goto short_packet; - if (strcmp(params.type, "params") != 0) + if (strncmp(params.type, "params", sizeof(params.type)) != 0) goto wrong_type; switch(ntohl(params.packet_id)) { diff --git a/src/modules/module-netjack2/packets.h b/src/modules/module-netjack2/packets.h index 4943e941a46fd153518a1751cde40354173f43f4..af60a6c7840504a1e5c881f64a56c8fdbec0a073 100644 --- a/src/modules/module-netjack2/packets.h +++ b/src/modules/module-netjack2/packets.h @@ -110,7 +110,7 @@ static inline void nj2_session_params_hton(struct nj2_session_params *net, } struct nj2_packet_header { - char type[8]; /* packet type ('headr') */ + char type[8]; /* packet type ('header') */ uint32_t data_type; /* 'a' for audio, 'm' for midi and 's' for sync */ uint32_t data_stream; /* 's' for send, 'r' for return */ uint32_t id; /* unique ID of the follower */ diff --git a/src/modules/module-netjack2/peer.c b/src/modules/module-netjack2/peer.c index 5ad4bd7f730e67ec30bdd565bd94eb6dce406667..9417323a1c4c9dc03a58142ac192f52ce27da2d9 100644 --- a/src/modules/module-netjack2/peer.c +++ b/src/modules/module-netjack2/peer.c @@ -663,7 +663,7 @@ static inline int32_t netjack2_driver_sync_wait(struct netjack2_peer *peer) if (len >= (ssize_t)sizeof(sync)) { //nj2_dump_packet_header(&sync); - if (strcmp(sync.type, "header") == 0 && + if (strncmp(sync.type, "header", sizeof(sync.type)) == 0 && ntohl(sync.data_type) == 's' && ntohl(sync.data_stream) == peer->other_stream && ntohl(sync.id) == peer->params.id) @@ -695,7 +695,7 @@ static inline int32_t netjack2_manager_sync_wait(struct netjack2_peer *peer) if (len >= (ssize_t)sizeof(sync)) { //nj2_dump_packet_header(sync); - if (strcmp(sync.type, "header") == 0 && + if (strncmp(sync.type, "header", sizeof(sync.type)) == 0 && ntohl(sync.data_type) == 's' && ntohl(sync.data_stream) == peer->other_stream && ntohl(sync.id) == peer->params.id) diff --git a/src/modules/module-pipe-tunnel.c b/src/modules/module-pipe-tunnel.c index 4acc98c0c572ffd564013944f8c9875ea2fd5268..4935d07f6f8f84a3df0bdd1fb6b93daa6072dbff 100644 --- a/src/modules/module-pipe-tunnel.c +++ b/src/modules/module-pipe-tunnel.c @@ -203,6 +203,7 @@ struct impl { uint64_t next_time; unsigned int have_sync:1; + unsigned int underrun:1; }; static uint64_t get_time_ns(struct impl *impl) @@ -407,7 +408,10 @@ static void capture_stream_process(void *data) if (avail < (int32_t)size) { memset(bd->data, 0, size); if (avail >= 0) { - pw_log_warn("underrun %d < %u", avail, size); + if (!impl->underrun) { + pw_log_warn("underrun %d < %u", avail, size); + impl->underrun = true; + } pause_stream(impl, true); } impl->have_sync = false; @@ -429,6 +433,7 @@ static void capture_stream_process(void *data) index += avail; spa_ringbuffer_read_update(&impl->ring, index); + impl->underrun = false; } bd->chunk->offset = 0; bd->chunk->size = size; diff --git a/src/modules/module-protocol-pulse/manager.c b/src/modules/module-protocol-pulse/manager.c index eb1c667ae2c71807fa28da10d04b5752a06028d7..1cf3944e9eae55888fdbd03d5749777fd6e0877d 100644 --- a/src/modules/module-protocol-pulse/manager.c +++ b/src/modules/module-protocol-pulse/manager.c @@ -373,7 +373,8 @@ static void device_event_param(void *data, int seq, if (p == NULL) return; - if (id == SPA_PARAM_Route && !has_param(&o->this.param_list, p)) { + if ((id == SPA_PARAM_Route || id == SPA_PARAM_EnumRoute) && + !has_param(&o->this.param_list, p)) { uint32_t idx, device; if (spa_pod_parse_object(param, SPA_TYPE_OBJECT_ParamRoute, NULL, diff --git a/src/modules/module-raop-sink.c b/src/modules/module-raop-sink.c index ccc727eea02f163d84cd3da0b8e424e894b9bd5f..b1a28b91f715761296f411e4ec1abd6b5bcbc5c1 100644 --- a/src/modules/module-raop-sink.c +++ b/src/modules/module-raop-sink.c @@ -1521,8 +1521,6 @@ static void stream_state_changed(void *data, bool started, const char *error) pw_impl_module_schedule_destroy(impl->module); return; } - if (started) - rtsp_do_record(impl); } static int rtsp_do_connect(struct impl *impl) diff --git a/src/pipewire/impl-port.c b/src/pipewire/impl-port.c index 8add1cd2a5f0f539720f4658e55456ca578c23cb..1359f3c82cfd80d00c8e4d1fe3ad16476e41a03f 100644 --- a/src/pipewire/impl-port.c +++ b/src/pipewire/impl-port.c @@ -1716,7 +1716,7 @@ int pw_impl_port_set_param(struct pw_impl_port *port, uint32_t id, uint32_t flag pw_log_debug("%p: %d set param on node %d:%d id:%d (%s): %d (%s)", port, port->state, port->direction, port->port_id, id, spa_debug_type_find_name(spa_type_param, id), - res, spa_strerror(res)); + res, res <= 0 ? spa_strerror(res) : "modified"); /* set the parameters on all ports of the mixer node if possible */ if (res >= 0) { diff --git a/src/pipewire/stream.c b/src/pipewire/stream.c index 39e603b2d4d3e8d34842372884cf44a06d8eeae7..d3338074912a59a0f2fcee8487b8ce1278273912 100644 --- a/src/pipewire/stream.c +++ b/src/pipewire/stream.c @@ -2199,9 +2199,10 @@ int pw_stream_update_params(struct pw_stream *stream, if ((res = update_params(impl, SPA_ID_INVALID, params, n_params)) < 0) return res; - emit_node_info(impl, false); - emit_port_info(impl, false); - + if (impl->in_emit_param_changed == 0) { + emit_node_info(impl, false); + emit_port_info(impl, false); + } return res; } diff --git a/src/pipewire/thread.c b/src/pipewire/thread.c index 4f753a9f97a9b22740035174ce9878954c668e90..f7b8c60ad00506465e55ee12c51387ccfa450488 100644 --- a/src/pipewire/thread.c +++ b/src/pipewire/thread.c @@ -102,13 +102,13 @@ static int impl_get_rt_range(void *object, const struct spa_dict *props, } static int impl_acquire_rt(void *object, struct spa_thread *thread, int priority) { - pw_log_warn("acquire_rt thread:%p prio:%d not implemented", thread, priority); + pw_log_info("acquire_rt thread:%p prio:%d not implemented", thread, priority); return -ENOTSUP; } static int impl_drop_rt(void *object, struct spa_thread *thread) { - pw_log_warn("drop_rt thread:%p not implemented", thread); + pw_log_info("drop_rt thread:%p not implemented", thread); return -ENOTSUP; }