Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package pipewire for openSUSE:Factory checked in at 2026-07-15 16:26:11 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/pipewire (Old) and /work/SRC/openSUSE:Factory/.pipewire.new.1991 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "pipewire" Wed Jul 15 16:26:11 2026 rev:158 rq:1365717 version:1.6.8 Changes: -------- --- /work/SRC/openSUSE:Factory/pipewire/pipewire.changes 2026-06-19 17:20:45.704815038 +0200 +++ /work/SRC/openSUSE:Factory/.pipewire.new.1991/pipewire.changes 2026-07-15 16:31:37.254075904 +0200 @@ -1,0 +2,36 @@ +Tue Jul 14 15:40:23 UTC 2026 - Antonio Larrosa <[email protected]> + +- Update to version 1.6.8: + * This is a bugfix release that is API and ABI compatible with + the previous 1.6.x releases. + * Highlights + - Fix a data race in JACK that could cause lost MIDI events in + ardour. + - Fix some unbounded memory allocations. + - Various small fixes. + * PipeWire + - Avoid some graph recalcs, which fixes a bug when suspending + a node while it is active. + * Modules + - Do Content-Length and allocation check in RAOP to avoid + OOM errors. + - Fix a potential memory leak in the error path of client-node. + (#5348 (closed)) + * SPA + - Fix filter-graph dynamic graph updates. + - Avoid 100% when unplugging a card. + - Fix filter-graph volumes when the filter is loaded inside a + node with hardware volume. (#5344 (closed)) + - Add normalize and latency options to the SOFA filter. (#5322) + * Bluetooth + - Fix a potential leak when transport fails to start. + * Pulse-server + - Avoid stack exhaustion via unbounded alloca. + * JACK + - Fix a data race in jack_port_get_buffer() when called from + concurrent threads, like in ardour. (#5324 (closed)) + * GStreamer + - Skip invalid crop metadata. + - Avoid crash because metadata listener was registered twice. + +------------------------------------------------------------------- Old: ---- pipewire-1.6.7.tar.xz New: ---- pipewire-1.6.8.tar.xz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ pipewire.spec ++++++ --- /var/tmp/diff_new_pack.5IURdx/_old 2026-07-15 16:31:39.646158783 +0200 +++ /var/tmp/diff_new_pack.5IURdx/_new 2026-07-15 16:31:39.650158921 +0200 @@ -71,7 +71,7 @@ %bcond_with aptx Name: pipewire -Version: 1.6.7 +Version: 1.6.8 Release: 0 Summary: A Multimedia Framework designed to be an audio and video server and more License: MIT ++++++ _service ++++++ --- /var/tmp/diff_new_pack.5IURdx/_old 2026-07-15 16:31:39.690160307 +0200 +++ /var/tmp/diff_new_pack.5IURdx/_new 2026-07-15 16:31:39.694160446 +0200 @@ -3,7 +3,7 @@ <service name="obs_scm" mode="manual"> <param name="scm">git</param> <param name="url">https://gitlab.freedesktop.org/pipewire/pipewire.git</param> - <param name="revision">1.6.7</param> + <param name="revision">1.6.8</param> <param name="versionformat">@PARENT_TAG@</param> <!-- <param name="revision">636cbae9b6cebc4ba55903e2a686f4c4a2bd4193</param> ++++++ pipewire-1.6.7.tar.xz -> pipewire-1.6.8.tar.xz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pipewire-1.6.7/NEWS new/pipewire-1.6.8/NEWS --- old/pipewire-1.6.7/NEWS 2026-06-18 10:01:36.000000000 +0200 +++ new/pipewire-1.6.8/NEWS 2026-07-09 12:25:35.000000000 +0200 @@ -1,3 +1,46 @@ +# PipeWire 1.6.8 (2026-07-09) + +This is a bugfix release that is API and ABI compatible with the previous +1.6.x releases. + +## Highlights + - Fix a data race in JACK that could cause lost MIDI events in ardour. + - Fix some unbounded memory allocations. + - Various small fixes. + + +## PipeWire + - Avoid some graph recalcs, which fixes a bug when suspending a node + while it is active. + +## Modules + - Do Content-Length and allocation check in RAOP to avoid OOM errors. + - Fix a potential memory leak in the error path of client-node. (#5348) + +## SPA + - Fix filter-graph dynamic graph updates. + - Avoid 100% when unplugging a card. + - Fix filter-graph volumes when the filter is loaded inside a node with + hardware volume. (#5344) + - Add normalize and latency options to the SOFA filter. (#5322) + +## Bluetooth + - Fix a potential leak when transport fails to start. + +## Pulse-server + - Avoid stack exhaustion via unbounded alloca. + +## JACK + - Fix a data race in jack_port_get_buffer() when called from concurrent + threads, like in ardour. (#5324) + +## GStreamer + - Skip invalid crop metadata. + - Avoid crash because metadata listener was registered twice. + + +Older versions: + # PipeWire 1.6.7 (2026-06-18) This is a bugfix release that is API and ABI compatible with the previous @@ -34,9 +77,6 @@ ## Misc - Make sure we don't deal with uninitialized spa_dict. - -Older versions: - # PipeWire 1.6.6 (2026-05-26) This is a bugfix release that is API and ABI compatible with the previous diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pipewire-1.6.7/meson.build new/pipewire-1.6.8/meson.build --- old/pipewire-1.6.7/meson.build 2026-06-18 10:01:36.000000000 +0200 +++ new/pipewire-1.6.8/meson.build 2026-07-09 12:25:35.000000000 +0200 @@ -1,5 +1,5 @@ project('pipewire', ['c' ], - version : '1.6.7', + version : '1.6.8', license : [ 'MIT', 'LGPL-2.1-or-later', 'GPL-2.0-only' ], meson_version : '>= 0.61.1', default_options : [ 'warning_level=3', diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pipewire-1.6.7/pipewire-jack/src/pipewire-jack.c new/pipewire-1.6.8/pipewire-jack/src/pipewire-jack.c --- old/pipewire-1.6.7/pipewire-jack/src/pipewire-jack.c 2026-06-18 10:01:36.000000000 +0200 +++ new/pipewire-1.6.8/pipewire-jack/src/pipewire-jack.c 2026-07-09 12:25:35.000000000 +0200 @@ -248,6 +248,7 @@ struct port *peer_port; struct spa_io_buffers *io[2]; + uint32_t io_busy; struct spa_list queue; struct buffer buffers[MAX_BUFFERS]; @@ -652,6 +653,7 @@ mix->io[0] = mix->io[1] = NULL; mix->n_buffers = 0; spa_list_init(&mix->queue); + SPA_ATOMIC_STORE(mix->io_busy, 0); if (mix_id == SPA_ID_INVALID) { port->global_mix = mix; if (port->n_mix > 0) @@ -733,6 +735,7 @@ } mix->n_buffers = 0; spa_list_init(&mix->queue); + SPA_ATOMIC_STORE(mix->io_busy, 0); return 0; } @@ -1718,11 +1721,11 @@ if (SPA_UNLIKELY((io = mix->io[cycle]) == NULL || mix->n_buffers == 0)) return NULL; - if (io->status == SPA_STATUS_HAVE_DATA && + if (SPA_ATOMIC_LOAD(io->status) == SPA_STATUS_HAVE_DATA && io->buffer_id < mix->n_buffers) { b = &mix->buffers[io->buffer_id]; d = &b->datas[0]; - } else { + } else if (SPA_ATOMIC_CAS(mix->io_busy, 0, 1)) { if (mix->n_buffers == 1) { b = &mix->buffers[0]; } else { @@ -1733,6 +1736,7 @@ if (SPA_UNLIKELY(b == NULL)) { pw_log_warn("port %p: out of buffers %d", p, mix->n_buffers); io->buffer_id = SPA_ID_INVALID; + SPA_ATOMIC_STORE(mix->io_busy, 0); return NULL; } } @@ -1742,7 +1746,15 @@ d->chunk->stride = stride; io->buffer_id = b->id; - io->status = SPA_STATUS_HAVE_DATA; + SPA_ATOMIC_STORE(io->status, SPA_STATUS_HAVE_DATA); + SPA_ATOMIC_STORE(mix->io_busy, 0); + } else { + while (SPA_ATOMIC_LOAD(io->status) != SPA_STATUS_HAVE_DATA) + ; + if (SPA_UNLIKELY(io->buffer_id >= mix->n_buffers)) + return NULL; + b = &mix->buffers[io->buffer_id]; + d = &b->datas[0]; } ptr = d->data; if (buf) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pipewire-1.6.7/spa/include/spa/pod/body.h new/pipewire-1.6.8/spa/include/spa/pod/body.h --- old/pipewire-1.6.7/spa/include/spa/pod/body.h 2026-06-18 10:01:36.000000000 +0200 +++ new/pipewire-1.6.8/spa/include/spa/pod/body.h 2026-07-09 12:25:35.000000000 +0200 @@ -136,8 +136,8 @@ return SPA_POD_CHECK(pod, SPA_TYPE_Bool, sizeof(int32_t)); } -#define SPA_POD_BODY_LOAD_ONCE(a, b) (*(a) = SPA_LOAD_ONCE((__typeof__(a))(b))) -#define SPA_POD_BODY_LOAD_FIELD_ONCE(a, b, field) ((a)->field = SPA_LOAD_ONCE(&((__typeof__(a))(b))->field)) +#define SPA_POD_BODY_LOAD_ONCE(a, b) (*(a) = SPA_LOAD_ONCE(*(__typeof__(a))(b))) +#define SPA_POD_BODY_LOAD_FIELD_ONCE(a, b, field) ((a)->field = SPA_LOAD_ONCE(((__typeof__(a))(b))->field)) SPA_API_POD_BODY int spa_pod_body_get_bool(const struct spa_pod *pod, const void *body, bool *value) { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pipewire-1.6.7/spa/include/spa/utils/atomic.h new/pipewire-1.6.8/spa/include/spa/utils/atomic.h --- old/pipewire-1.6.7/spa/include/spa/utils/atomic.h 2026-06-18 10:01:36.000000000 +0200 +++ new/pipewire-1.6.8/spa/include/spa/utils/atomic.h 2026-07-09 12:25:35.000000000 +0200 @@ -19,8 +19,8 @@ #define SPA_ATOMIC_DEC(s) __atomic_sub_fetch(&(s), 1, __ATOMIC_SEQ_CST) #define SPA_ATOMIC_INC(s) __atomic_add_fetch(&(s), 1, __ATOMIC_SEQ_CST) #define SPA_ATOMIC_LOAD(s) __atomic_load_n(&(s), __ATOMIC_SEQ_CST) -#define SPA_LOAD_ONCE(s) __atomic_load_n((s), __ATOMIC_RELAXED) -#define SPA_STORE_ONCE(s) __atomic_store_n((s), __ATOMIC_RELAXED) +#define SPA_LOAD_ONCE(s) __atomic_load_n(&(s), __ATOMIC_RELAXED) +#define SPA_STORE_ONCE(s,v) __atomic_store_n(&(s), (v), __ATOMIC_RELAXED) #define SPA_ATOMIC_STORE(s,v) __atomic_store_n(&(s), (v), __ATOMIC_SEQ_CST) #define SPA_ATOMIC_XCHG(s,v) __atomic_exchange_n(&(s), (v), __ATOMIC_SEQ_CST) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pipewire-1.6.7/spa/plugins/aec/aec-webrtc.cpp new/pipewire-1.6.8/spa/plugins/aec/aec-webrtc.cpp --- old/pipewire-1.6.7/spa/plugins/aec/aec-webrtc.cpp 2026-06-18 10:01:36.000000000 +0200 +++ new/pipewire-1.6.8/spa/plugins/aec/aec-webrtc.cpp 2026-07-09 12:25:35.000000000 +0200 @@ -385,9 +385,9 @@ SPA_TYPE_INTERFACE_AUDIO_AEC, SPA_VERSION_AUDIO_AEC, &impl_aec, impl); - impl->aec.name = "webrtc", + impl->aec.name = "webrtc"; impl->aec.info = NULL; - impl->aec.latency = "480/48000", + impl->aec.latency = "480/48000"; impl->log = static_cast<struct spa_log *>(spa_support_find(support, n_support, SPA_TYPE_INTERFACE_Log)); spa_log_topic_init(impl->log, &log_topic); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pipewire-1.6.7/spa/plugins/alsa/alsa-acp-device.c new/pipewire-1.6.8/spa/plugins/alsa/alsa-acp-device.c --- old/pipewire-1.6.7/spa/plugins/alsa/alsa-acp-device.c 2026-06-18 10:01:36.000000000 +0200 +++ new/pipewire-1.6.8/spa/plugins/alsa/alsa-acp-device.c 2026-07-09 12:25:35.000000000 +0200 @@ -85,17 +85,34 @@ }; static int emit_info(struct impl *this, bool full); +static void remove_sources(struct impl *this); static void handle_acp_poll(struct spa_source *source) { struct impl *this = source->data; + uint32_t rmask = 0; int i; - for (i = 0; i < this->n_pfds; i++) + for (i = 0; i < this->n_pfds; i++) { this->pfds[i].revents = this->sources[i].rmask; + rmask |= this->sources[i].rmask; + } acp_card_handle_events(this->card); for (i = 0; i < this->n_pfds; i++) this->sources[i].rmask = 0; + + /* A POLLERR/POLLHUP on a poll fd (e.g. the ALSA control device was + * unplugged) is level-triggered and never clears, so the loop would + * redispatch this handler on every iteration and spin at 100% CPU. + * Stop polling the dead descriptors; the udev monitor handles the + * actual device removal, and setup_sources() re-arms on the next + * profile change if the card comes back. */ + if (rmask & (SPA_IO_ERR | SPA_IO_HUP)) { + spa_log_warn(this->log, "%p: poll fd error/hangup (card removed?), " + "removing poll sources", this); + remove_sources(this); + return; + } emit_info(this, false); } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pipewire-1.6.7/spa/plugins/audioconvert/audioconvert.c new/pipewire-1.6.8/spa/plugins/audioconvert/audioconvert.c --- old/pipewire-1.6.7/spa/plugins/audioconvert/audioconvert.c 2026-06-18 10:01:36.000000000 +0200 +++ new/pipewire-1.6.8/spa/plugins/audioconvert/audioconvert.c 2026-07-09 12:25:35.000000000 +0200 @@ -1290,6 +1290,28 @@ return 0; } +static int do_sync_filter_graph(struct spa_loop *loop, bool async, uint32_t seq, + const void *data, size_t size, void *user_data) +{ + struct impl *impl = user_data; + struct filter_graph *g; + + impl->n_graph = 0; + spa_list_for_each(g, &impl->active_graphs, link) + if (g->setup && !g->removing) + impl->filter_graph[impl->n_graph++] = g; + + impl->recalc = true; + return 0; +} + +static void sync_filter_graph(struct impl *impl) +{ + if (impl->data_loop) + spa_loop_locked(impl->data_loop, do_sync_filter_graph, 0, NULL, 0, impl); + else + do_sync_filter_graph(NULL, false, 0, NULL, 0, impl); +} static int setup_filter_graphs(struct impl *impl, bool force) { @@ -1305,11 +1327,23 @@ position = in->format.info.raw.position; impl->maxports = SPA_MAX(in->format.info.raw.channels, out->format.info.raw.channels); + if (force) { + /* A forced setup deactivates and re-instantiates each graph below, + * which frees and recreates the underlying plugin handles. Pull the + * graphs out of the data-loop's view first (under the loop lock, via + * sync_filter_graph) so the RT thread cannot run a graph while its + * handles are NULL during the rebuild. They are republished by the + * sync_filter_graph() that follows setup. */ + spa_list_for_each(g, &impl->active_graphs, link) { + if (!g->removing) + g->setup = false; + } + sync_filter_graph(impl); + } + spa_list_for_each_safe(g, t, &impl->active_graphs, link) { if (g->removing) continue; - if (force) - g->setup = false; if ((res = setup_filter_graph(impl, g, channels, position)) < 0) { g->removing = true; spa_log_warn(impl->log, "failed to activate graph %d: %s", g->order, @@ -1328,29 +1362,6 @@ return 0; } -static int do_sync_filter_graph(struct spa_loop *loop, bool async, uint32_t seq, - const void *data, size_t size, void *user_data) -{ - struct impl *impl = user_data; - struct filter_graph *g; - - impl->n_graph = 0; - spa_list_for_each(g, &impl->active_graphs, link) - if (g->setup && !g->removing) - impl->filter_graph[impl->n_graph++] = g; - - impl->recalc = true; - return 0; -} - -static void sync_filter_graph(struct impl *impl) -{ - if (impl->data_loop) - spa_loop_locked(impl->data_loop, do_sync_filter_graph, 0, NULL, 0, impl); - else - do_sync_filter_graph(NULL, false, 0, NULL, 0, impl); -} - static void clean_filter_handles(struct impl *impl, bool force) { struct filter_graph *g, *t; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pipewire-1.6.7/spa/plugins/bluez5/bluez5-dbus.c new/pipewire-1.6.8/spa/plugins/bluez5/bluez5-dbus.c --- old/pipewire-1.6.7/spa/plugins/bluez5/bluez5-dbus.c 2026-06-18 10:01:36.000000000 +0200 +++ new/pipewire-1.6.8/spa/plugins/bluez5/bluez5-dbus.c 2026-07-09 12:25:35.000000000 +0200 @@ -7152,14 +7152,14 @@ goto parse_failed; spa_log_debug(monitor->log, "bis_entry->qos_preset %s", bis_entry->qos_preset); } else if (spa_streq(bis_key, "retransmissions")) { - if (spa_json_get_int(&it[2], &bis_entry->retransmissions) <= 0) + if (spa_json_get_int(&it[1], &bis_entry->retransmissions) <= 0) goto parse_failed; if (bis_entry->retransmissions > RTN_MAX) goto parse_failed; bis_entry->rtn_manual_set = 1; spa_log_debug(monitor->log, "bis_entry->retransmissions %d", bis_entry->retransmissions); } else if (spa_streq(bis_key, "max_transport_latency")) { - if (spa_json_get_int(&it[2], &bis_entry->max_transport_latency) <= 0) + if (spa_json_get_int(&it[1], &bis_entry->max_transport_latency) <= 0) goto parse_failed; if (bis_entry->max_transport_latency < MAX_TRANSPORT_LATENCY_MIN && bis_entry->max_transport_latency > MAX_TRANSPORT_LATENCY_MAX) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pipewire-1.6.7/spa/plugins/bluez5/media-sink.c new/pipewire-1.6.8/spa/plugins/bluez5/media-sink.c --- old/pipewire-1.6.7/spa/plugins/bluez5/media-sink.c 2026-06-18 10:01:36.000000000 +0200 +++ new/pipewire-1.6.8/spa/plugins/bluez5/media-sink.c 2026-07-09 12:25:35.000000000 +0200 @@ -1685,6 +1685,10 @@ return 0; fail: + if (this->update_delay_event) { + spa_loop_utils_destroy_source(this->loop_utils, this->update_delay_event); + this->update_delay_event = NULL; + } if (this->codec_data) { if (this->own_codec_data) this->codec->deinit(this->codec_data); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pipewire-1.6.7/spa/plugins/filter-graph/filter-graph.c new/pipewire-1.6.8/spa/plugins/filter-graph/filter-graph.c --- old/pipewire-1.6.7/spa/plugins/filter-graph/filter-graph.c 2026-06-18 10:01:36.000000000 +0200 +++ new/pipewire-1.6.8/spa/plugins/filter-graph/filter-graph.c 2026-07-09 12:25:35.000000000 +0200 @@ -760,6 +760,9 @@ char buf[1024]; struct spa_pod_dynamic_builder b; struct volume *vol = &graph->volume[direction]; + float soft_vols[MAX_CHANNELS]; + uint32_t n_soft_vols = 0; + bool soft_mute = false, have_soft_mute = false; bool do_volume = false; spa_pod_dynamic_builder_init(&b, buf, sizeof(buf), 1024); @@ -805,7 +808,12 @@ break; } case SPA_PROP_softVolumes: + n_soft_vols = spa_pod_copy_array(&prop->value, SPA_TYPE_Float, soft_vols, + SPA_N_ELEMENTS(soft_vols)); + break; case SPA_PROP_softMute: + if (spa_pod_get_bool(&prop->value, &soft_mute) == 0) + have_soft_mute = true; break; default: spa_pod_builder_raw_padded(&b.b, prop, SPA_POD_PROP_SIZE(prop)); @@ -813,24 +821,27 @@ } } if (do_volume && vol->n_ports != 0) { - float soft_vols[MAX_CHANNELS]; uint32_t i; for (i = 0; i < vol->n_volumes; i++) soft_vols[i] = (vol->mute || vol->volumes[i] == 0.0f) ? 0.0f : 1.0f; + n_soft_vols = vol->n_volumes; + soft_mute = vol->mute; + have_soft_mute = true; + sync_volume(graph, vol); + } + if (have_soft_mute) { spa_pod_builder_prop(&b.b, SPA_PROP_softMute, 0); - spa_pod_builder_bool(&b.b, vol->mute); + spa_pod_builder_bool(&b.b, soft_mute); + } + if (n_soft_vols > 0) { spa_pod_builder_prop(&b.b, SPA_PROP_softVolumes, 0); spa_pod_builder_array(&b.b, sizeof(float), SPA_TYPE_Float, - vol->n_volumes, soft_vols); - props = spa_pod_builder_pop(&b.b, &f[0]); - - sync_volume(graph, vol); - - } else { - props = spa_pod_builder_pop(&b.b, &f[0]); + n_soft_vols, soft_vols); } + props = spa_pod_builder_pop(&b.b, &f[0]); + spa_filter_graph_emit_apply_props(&impl->hooks, direction, props); spa_pod_dynamic_builder_clean(&b); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pipewire-1.6.7/spa/plugins/filter-graph/plugin_sofa.c new/pipewire-1.6.8/spa/plugins/filter-graph/plugin_sofa.c --- old/pipewire-1.6.7/spa/plugins/filter-graph/plugin_sofa.c 2026-06-18 10:01:36.000000000 +0200 +++ new/pipewire-1.6.8/spa/plugins/filter-graph/plugin_sofa.c 2026-07-09 12:25:35.000000000 +0200 @@ -34,6 +34,7 @@ int n_samples, blocksize, tailsize; float gain; float *tmp[2]; + float latency; struct MYSOFA_EASY *sofa; unsigned int interpolate:1; @@ -50,6 +51,8 @@ const char *val; char key[256]; char filename[PATH_MAX] = ""; + bool normalize = false; + float normalized; int len; errno = EINVAL; @@ -73,6 +76,7 @@ impl->dsp = pl->dsp; impl->log = pl->log; impl->gain = 1.0f; + impl->latency = 0.0f; while ((len = spa_json_object_next(&it[0], key, sizeof(key), &val)) > 0) { if (spa_streq(key, "blocksize")) { @@ -103,6 +107,23 @@ goto error; } } + else if (spa_streq(key, "normalize")) { + if (spa_json_parse_bool(val, len, &normalize) <= 0) { + spa_log_error(impl->log, "spatializer:normalize requires a bool"); + errno = EINVAL; + goto error; + } + } + else if (spa_streq(key, "latency")) { + if (spa_json_parse_float(val, len, &impl->latency) <= 0) { + spa_log_error(impl->log, "spatializer:latency requires a number"); + errno = EINVAL; + goto error; + } + } + else { + spa_log_warn(pl->log, "spatializer: ignoring config key: '%s'", key); + } } if (!filename[0]) { spa_log_error(impl->log, "spatializer:filename was not given"); @@ -195,8 +216,13 @@ if (impl->tailsize <= 0) impl->tailsize = SPA_CLAMP(4096, impl->blocksize, 32768); - spa_log_info(impl->log, "using n_samples:%u %d:%d blocksize gain:%f sofa:%s", impl->n_samples, - impl->blocksize, impl->tailsize, impl->gain, filename); + if (normalize) + normalized = mysofa_loudness(impl->sofa->hrtf); + + spa_log_info(impl->log, "using n_samples:%u %d:%d blocksize %sgain:%f sofa:%s", impl->n_samples, + impl->blocksize, impl->tailsize, + normalize ? "auto" : "", normalize ? normalized : impl->gain, filename); + impl->tmp[0] = calloc(impl->plugin->quantum_limit, sizeof(float)); impl->tmp[1] = calloc(impl->plugin->quantum_limit, sizeof(float)); @@ -331,7 +357,7 @@ convolver_run(impl->l_conv[0], impl->port[2], impl->port[0], SampleCount); convolver_run(impl->r_conv[0], impl->port[2], impl->port[1], SampleCount); } - impl->port[6][0] = impl->n_samples; + impl->port[6][0] = impl->latency; } static void spatializer_connect_port(void * Instance, unsigned long Port, @@ -367,7 +393,7 @@ static void spatializer_activate(void * Instance) { struct spatializer_impl *impl = Instance; - impl->port[6][0] = impl->n_samples; + impl->port[6][0] = impl->latency; } static void spatializer_deactivate(void * Instance) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pipewire-1.6.7/src/gst/gstpipewiredeviceprovider.c new/pipewire-1.6.8/src/gst/gstpipewiredeviceprovider.c --- old/pipewire-1.6.7/src/gst/gstpipewiredeviceprovider.c 2026-06-18 10:01:36.000000000 +0200 +++ new/pipewire-1.6.8/src/gst/gstpipewiredeviceprovider.c 2026-07-09 12:25:35.000000000 +0200 @@ -729,8 +729,16 @@ if (!spa_streq(name, "default")) return; + /* PipeWire re-emits the global event for the same id when the client's + * permissions change. Only bind and add the listener once, otherwise we + * would re-append the already-linked metadata_listener hook and corrupt + * the proxy's listener list. */ + if (self->metadata != NULL) + return; + self->metadata = pw_registry_bind(self->registry, id, type, PW_VERSION_METADATA, 0); + self->metadata_id = id; pw_metadata_add_listener(self->metadata, &self->metadata_listener, &metadata_events, self); } @@ -744,6 +752,13 @@ static void registry_event_global_remove(void *data, uint32_t id) { + GstPipeWireDeviceProvider *self = data; + + if (self->metadata != NULL && self->metadata_id == id) { + spa_hook_remove(&self->metadata_listener); + pw_proxy_destroy((struct pw_proxy *)self->metadata); + self->metadata = NULL; + } } static const struct pw_registry_events registry_events = { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pipewire-1.6.7/src/gst/gstpipewiredeviceprovider.h new/pipewire-1.6.8/src/gst/gstpipewiredeviceprovider.h --- old/pipewire-1.6.7/src/gst/gstpipewiredeviceprovider.h 2026-06-18 10:01:36.000000000 +0200 +++ new/pipewire-1.6.8/src/gst/gstpipewiredeviceprovider.h 2026-07-09 12:25:35.000000000 +0200 @@ -52,6 +52,7 @@ struct spa_hook registry_listener; struct pw_metadata *metadata; + uint32_t metadata_id; struct spa_hook metadata_listener; gchar *default_audio_source_name; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pipewire-1.6.7/src/gst/gstpipewiresrc.c new/pipewire-1.6.8/src/gst/gstpipewiresrc.c --- old/pipewire-1.6.7/src/gst/gstpipewiresrc.c 2026-06-18 10:01:36.000000000 +0200 +++ new/pipewire-1.6.8/src/gst/gstpipewiresrc.c 2026-07-09 12:25:35.000000000 +0200 @@ -781,7 +781,7 @@ } crop = data->crop; - if (crop) { + if (crop && spa_meta_region_is_valid(crop)) { GstVideoCropMeta *meta = gst_buffer_add_video_crop_meta(buf); if (meta) { meta->x = crop->region.position.x; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pipewire-1.6.7/src/modules/module-client-node/remote-node.c new/pipewire-1.6.8/src/modules/module-client-node/remote-node.c --- old/pipewire-1.6.7/src/modules/module-client-node/remote-node.c 2026-06-18 10:01:36.000000000 +0200 +++ new/pipewire-1.6.8/src/modules/module-client-node/remote-node.c 2026-07-09 12:25:35.000000000 +0200 @@ -972,12 +972,10 @@ { struct mix *mix; - if (d->have_transport) { - spa_list_consume(mix, &d->mix[SPA_DIRECTION_INPUT], link) - clear_mix(d, mix); - spa_list_consume(mix, &d->mix[SPA_DIRECTION_OUTPUT], link) - clear_mix(d, mix); - } + spa_list_consume(mix, &d->mix[SPA_DIRECTION_INPUT], link) + clear_mix(d, mix); + spa_list_consume(mix, &d->mix[SPA_DIRECTION_OUTPUT], link) + clear_mix(d, mix); spa_list_consume(mix, &d->free_mix, link) { spa_list_remove(&mix->link); free(mix); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pipewire-1.6.7/src/modules/module-filter-chain.c new/pipewire-1.6.8/src/modules/module-filter-chain.c --- old/pipewire-1.6.7/src/modules/module-filter-chain.c 2026-06-18 10:01:36.000000000 +0200 +++ new/pipewire-1.6.8/src/modules/module-filter-chain.c 2026-07-09 12:25:35.000000000 +0200 @@ -686,6 +686,8 @@ * tailsize = ... * filename = ... * gain = ... + * latency = ... + * normalize = ... * } * control = { * "Azimuth" = ... @@ -705,7 +707,9 @@ * - `tailsize` specifies the size of the tail blocks to use in the FFT. * - `filename` The SOFA file to load. SOFA files usually end in the .sofa extension * and contain the HRTF for the various spatial positions. - * - `gain` the overall gain to apply to the IR file. + * - `gain` the overall gain to apply to the IR file, default 1.0. + * - `latency` the latency introduced by the filter, default 0 + * - `normalize` automatically normalize the loudness of the IR, default false * * - `Azimuth` controls the azimuth, this is the direction the sound is coming from * in degrees between 0 and 360. 0 is straight ahead. 90 is left, 180 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pipewire-1.6.7/src/modules/module-protocol-pulse/pulse-server.c new/pipewire-1.6.8/src/modules/module-protocol-pulse/pulse-server.c --- old/pipewire-1.6.7/src/modules/module-protocol-pulse/pulse-server.c 2026-06-18 10:01:36.000000000 +0200 +++ new/pipewire-1.6.8/src/modules/module-protocol-pulse/pulse-server.c 2026-07-09 12:25:35.000000000 +0200 @@ -71,6 +71,7 @@ /* The max amount of data we send in one block when capturing. In PulseAudio this * size is derived from the mempool PA_MEMPOOL_SLOT_SIZE */ #define MAX_BLOCK (64*1024) +#define MAX_ALLOCA_SIZE (64*1024) #define TEMPORARY_MOVE_TIMEOUT (SPA_NSEC_PER_SEC) @@ -3173,6 +3174,8 @@ return -ENOENT; collect_card_info(card, &card_info); + if (card_info.n_ports > MAX_ALLOCA_SIZE / sizeof(*port_info)) + return -ENOMEM; port_info = alloca(card_info.n_ports * sizeof(*port_info)); card_info.active_profile = SPA_ID_INVALID; n_ports = collect_port_info(card, &card_info, NULL, port_info); @@ -3314,6 +3317,8 @@ } dict.n_items = props->dict.n_items; + if (dict.n_items > MAX_ALLOCA_SIZE / sizeof(struct spa_dict_item)) + return -ENOMEM; dict.items = items = alloca(sizeof(struct spa_dict_item) * dict.n_items); for (i = 0; i < dict.n_items; i++) { items[i].key = props->dict.items[i].key; @@ -3597,6 +3602,8 @@ TAG_U32, card_info.n_profiles, /* n_profiles */ TAG_INVALID); + if (card_info.n_profiles > MAX_ALLOCA_SIZE / sizeof(*profile_info)) + return -ENOMEM; profile_info = alloca(card_info.n_profiles * sizeof(*profile_info)); n_profiles = collect_profile_info(o, &card_info, profile_info); @@ -3626,6 +3633,8 @@ uint32_t n_ports; struct port_info *port_info, *pi; + if (card_info.n_ports > MAX_ALLOCA_SIZE / sizeof(*port_info)) + return -ENOMEM; port_info = alloca(card_info.n_ports * sizeof(*port_info)); card_info.active_profile = SPA_ID_INVALID; n_ports = collect_port_info(o, &card_info, NULL, port_info); @@ -3641,7 +3650,8 @@ pi = &port_info[n]; - if (pi->info && pi->n_props > 0) { + if (pi->info && pi->n_props > 0 && + pi->n_props <= MAX_ALLOCA_SIZE / sizeof(*items)) { items = alloca(pi->n_props * sizeof(*items)); pdict = collect_props(pi->info, &dict, items, pi->n_props); } @@ -3829,6 +3839,8 @@ uint32_t n_ports, n; struct port_info *port_info, *pi; + if (card_info.n_ports > MAX_ALLOCA_SIZE / sizeof(*port_info)) + return -ENOMEM; port_info = alloca(card_info.n_ports * sizeof(*port_info)); n_ports = collect_port_info(card, &card_info, &dev_info, port_info); @@ -4025,6 +4037,8 @@ uint32_t n_ports, n; struct port_info *port_info, *pi; + if (card_info.n_ports > MAX_ALLOCA_SIZE / sizeof(*port_info)) + return -ENOMEM; port_info = alloca(card_info.n_ports * sizeof(*port_info)); n_ports = collect_port_info(card, &card_info, &dev_info, port_info); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pipewire-1.6.7/src/modules/module-raop/rtsp-client.c new/pipewire-1.6.8/src/modules/module-raop/rtsp-client.c --- old/pipewire-1.6.7/src/modules/module-raop/rtsp-client.c 2026-06-18 10:01:36.000000000 +0200 +++ new/pipewire-1.6.8/src/modules/module-raop/rtsp-client.c 2026-07-09 12:25:35.000000000 +0200 @@ -322,6 +322,11 @@ pw_log_info(" %s: %s", it->key, it->value); client->content_length = pw_properties_get_uint32(client->headers, "Content-Length", 0); + if (client->content_length > 64 * 1024) { + pw_log_error("Content-Length %zu exceeds maximum", + client->content_length); + return -EOVERFLOW; + } if (client->content_length > 0) client->recv_state = CLIENT_RECV_CONTENT; else @@ -351,6 +356,8 @@ } void *p = pw_array_add(&client->content, res); + if (p == NULL) + return -ENOMEM; memcpy(p, buf, res); spa_assert((size_t) res <= client->content_length); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pipewire-1.6.7/src/modules/module-rtp/stream.c new/pipewire-1.6.8/src/modules/module-rtp/stream.c --- old/pipewire-1.6.7/src/modules/module-rtp/stream.c 2026-06-18 10:01:36.000000000 +0200 +++ new/pipewire-1.6.8/src/modules/module-rtp/stream.c 2026-07-09 12:25:35.000000000 +0200 @@ -1014,6 +1014,13 @@ return (struct rtp_stream*)impl; out: pw_properties_free(props); + if (impl) { + if (impl->stream) + pw_stream_destroy(impl->stream); + if (impl->data_loop) + pw_context_release_loop(impl->context, impl->data_loop); + free(impl); + } errno = -res; return NULL; } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pipewire-1.6.7/src/pipewire/context.c new/pipewire-1.6.8/src/pipewire/context.c --- old/pipewire-1.6.7/src/pipewire/context.c 2026-06-18 10:01:36.000000000 +0200 +++ new/pipewire-1.6.8/src/pipewire/context.c 2026-07-09 12:25:35.000000000 +0200 @@ -58,7 +58,7 @@ struct pw_context this; struct spa_handle *dbus_handle; struct spa_plugin_loader plugin_loader; - unsigned int recalc:1; + int recalc; unsigned int recalc_pending:1; uint32_t cpu_count; @@ -1445,13 +1445,13 @@ pw_log_info("%p: busy:%d reason:%s", context, impl->recalc, reason); - if (impl->recalc) { + if (impl->recalc > 0) { impl->recalc_pending = true; return -EBUSY; } again: - impl->recalc = true; + impl->recalc++; freewheel = false; /* clean up the flags first */ @@ -1775,6 +1775,7 @@ if (do_reconfigure) { reconfigure_driver(context, n); /* we might be suspended now and the links need to be prepared again */ + impl->recalc--; goto again; } /* we have a pending change. We place the new values in the @@ -1832,8 +1833,7 @@ /* now that all the followers are ready, start the driver */ ensure_state(n, running); } - impl->recalc = false; - if (impl->recalc_pending) { + if (--impl->recalc == 0 && impl->recalc_pending) { impl->recalc_pending = false; goto again; } @@ -1841,6 +1841,23 @@ return 0; } +int pw_context_freeze_recalc_graph(struct pw_context *context) +{ + struct impl *impl = SPA_CONTAINER_OF(context, struct impl, this); + impl->recalc++; + return 0; +} + +int pw_context_thaw_recalc_graph(struct pw_context *context, const char *reason) +{ + struct impl *impl = SPA_CONTAINER_OF(context, struct impl, this); + if (--impl->recalc == 0 && impl->recalc_pending) { + impl->recalc_pending = false; + pw_context_recalc_graph(context, reason); + } + return 0; +} + SPA_EXPORT int pw_context_add_spa_lib(struct pw_context *context, const char *factory_regexp, const char *lib) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pipewire-1.6.7/src/pipewire/impl-node.c new/pipewire-1.6.8/src/pipewire/impl-node.c --- old/pipewire-1.6.7/src/pipewire/impl-node.c 2026-06-18 10:01:36.000000000 +0200 +++ new/pipewire-1.6.8/src/pipewire/impl-node.c 2026-07-09 12:25:35.000000000 +0200 @@ -286,6 +286,8 @@ /* make sure the node doesn't get woken up while not active */ remove_node_from_graph(this); + pw_context_freeze_recalc_graph(this->context); + spa_list_for_each(port, &this->input_ports, link) { spa_list_for_each(link, &port->links, input_link) pw_impl_link_deactivate(link); @@ -294,6 +296,7 @@ spa_list_for_each(link, &port->links, output_link) pw_impl_link_deactivate(link); } + pw_context_thaw_recalc_graph(this->context, "node deactivate"); } static int idle_node(struct pw_impl_node *this) @@ -327,6 +330,8 @@ struct pw_impl_port *port; pw_log_debug("%p: activate", this); + pw_context_freeze_recalc_graph(this->context); + spa_list_for_each(port, &this->output_ports, link) { struct pw_impl_link *link; spa_list_for_each(link, &port->links, output_link) @@ -337,6 +342,7 @@ spa_list_for_each(link, &port->links, input_link) pw_impl_link_activate(link); } + pw_context_thaw_recalc_graph(this->context, "node activate"); } static int start_node(struct pw_impl_node *this) @@ -537,6 +543,8 @@ if (this->info.state > 0 && this->info.state <= PW_NODE_STATE_SUSPENDED) return 0; + pw_context_freeze_recalc_graph(this->context); + node_deactivate(this); pw_log_debug("%p: suspend node driving:%d driver:%d prepared:%d", this, @@ -555,6 +563,8 @@ spa_list_for_each(p, &this->output_ports, link) pw_impl_port_suspend(p); + pw_context_thaw_recalc_graph(this->context, "node suspend"); + node_update_state(this, PW_NODE_STATE_SUSPENDED, 0, NULL); return res; @@ -2399,6 +2409,8 @@ pw_log_debug("%p: destroy", impl); pw_log_info("(%s-%u) destroy", node->name, node->info.id); + pw_context_freeze_recalc_graph(context); + node_deactivate(node); suspend_node(node); @@ -2440,11 +2452,12 @@ spa_hook_remove(&node->global_listener); pw_global_destroy(node->global); } - if (active || had_driver) pw_context_recalc_graph(context, "active node destroy"); + pw_context_thaw_recalc_graph(context, "node destroy"); + pw_log_debug("%p: free", node); pw_impl_node_emit_free(node); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pipewire-1.6.7/src/pipewire/impl-port.c new/pipewire-1.6.8/src/pipewire/impl-port.c --- old/pipewire-1.6.7/src/pipewire/impl-port.c 2026-06-18 10:01:36.000000000 +0200 +++ new/pipewire-1.6.8/src/pipewire/impl-port.c 2026-07-09 12:25:35.000000000 +0200 @@ -1507,7 +1507,10 @@ void pw_impl_port_unlink(struct pw_impl_port *port) { + struct pw_context *context = port->node->context; + pw_context_freeze_recalc_graph(context); pw_impl_port_for_each_link(port, do_destroy_link, port); + pw_context_thaw_recalc_graph(context, "port unlink"); } static int do_remove_port(struct spa_loop *loop, diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pipewire-1.6.7/src/pipewire/private.h new/pipewire-1.6.8/src/pipewire/private.h --- old/pipewire-1.6.7/src/pipewire/private.h 2026-06-18 10:01:36.000000000 +0200 +++ new/pipewire-1.6.8/src/pipewire/private.h 2026-07-09 12:25:35.000000000 +0200 @@ -1274,6 +1274,8 @@ void pw_proxy_remove(struct pw_proxy *proxy); int pw_context_recalc_graph(struct pw_context *context, const char *reason); +int pw_context_freeze_recalc_graph(struct pw_context *context); +int pw_context_thaw_recalc_graph(struct pw_context *context, const char *reason); void pw_impl_port_update_info(struct pw_impl_port *port, const struct spa_port_info *info); ++++++ pipewire.obsinfo ++++++ --- /var/tmp/diff_new_pack.5IURdx/_old 2026-07-15 16:31:40.782198143 +0200 +++ /var/tmp/diff_new_pack.5IURdx/_new 2026-07-15 16:31:40.786198282 +0200 @@ -1,5 +1,5 @@ name: pipewire -version: 1.6.7 -mtime: 1781769696 -commit: 3b2cb4fb037bf6033b87d3c87ee917b2f686d309 +version: 1.6.8 +mtime: 1783592735 +commit: b741e0c74f5436f0c925f7741140db0efd32cf4e
