diff --git a/NEWS b/NEWS
index 1f0a39a28..6e8b7f6f5 100644
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,69 @@
+# PipeWire 1.6.2 (2026-03-16)
+
+This is a bugfix release that is API and ABI compatible with the previous
+1.6.x releases.
+
+## Highlights
+ - Fix a potential crash when the wrong memory was freed.
+ - Fix a optimization with shared memory over some links that could
+ cause errors later on.
+ - Fix SOFA filter and default control input in LADSPA and LV2.
+ - Some other small fixes and improvements.
+
+
+## PipeWire
+ - Remove an optimization to skip share mem in links, it causes problems
+ later on. (#5159)
+
+## Modules
+ - Don't try to free invalid memory or close invalid fds when the client
+ aborted before allocating buffer memory. (#5162)
+
+## SPA
+ - support ACP_IGNORE_DB in udev.
+ - Use 0x as a prefix for hex values.
+ - Mark Props as write-only in libcamera.
+ - Small optimization in the audio mixer.
+ - Fix initialization of control properties for SOFA and biquads in the
+ filter-graph. (#5152)
+ - Fix min/max default values for LADSPA and LV2.
+
+## JACK
+ - Fix jack_port_type_id(). Return values that are compatible with JACK1/2.
+
+
+Older versions:
+
+# PipeWire 1.6.1 (2026-03-09)
+
+This is a bugfix release that is API and ABI compatible with the previous
+1.6.x releases.
+
+## Highlights
+ - Fix socket activation, which could cause a failure to start PipeWire in
+ some setups.
+ - Fix crashes in many JACK apps when nodes/ports are quickly added/removed
+ such as when there are notifications (like when changing the volume in
+ KDE).
+ - Fix playback of encoded formats in pw-cat again.
+ - Some other smaller fixes and improvements.
+
+## Modules
+ - Fix socket activation. (#5140)
+ - Remove node.link-group from driver nodes.
+
+## SPA
+ - Fix the libcamera stop sequence.
+
+## JACK
+ - Never return NULL from jack_port_by_id(). (#3512)
+
+## GStreamer
+ - Improve the timestamps on buffers.
+
+## Tools
+ - Fix playback of encoded formats. (#5155)
+
# PipeWire 1.6.0 (2026-02-19)
This is the 1.6 release that is API and ABI compatible with previous
@@ -95,8 +161,6 @@ the 1.4 release last year, including:
- Add some more options to pw-cat to list supported containers
and formats. (#5117)
-Older versions:
-
# PipeWire 1.5.85 (2026-01-19)
This is the fifth and hopefully last 1.6 release candidate that
diff --git a/doc/DoxygenLayout.xml b/doc/DoxygenLayout.xml
index 300c8400e..10da55da5 100644
--- a/doc/DoxygenLayout.xml
+++ b/doc/DoxygenLayout.xml
@@ -44,6 +44,7 @@
+
diff --git a/doc/dox/internals/dma-buf.dox b/doc/dox/internals/dma-buf.dox
index 5042f285c..ec02b9da7 100644
--- a/doc/dox/internals/dma-buf.dox
+++ b/doc/dox/internals/dma-buf.dox
@@ -349,10 +349,10 @@ rectangles. For example
params[n_params++] = spa_pod_builder_pop(&b, &f);
```
-After having received the first \ref SPA_PARAM_PeerCapability param, if it contained the \ref
-PW_CAPABILITY_DEVICE_ID set to `true`, the full set of formats can be sent using \ref
-pw_stream_update_params following by activating the stream using
-`pw_stream_set_active(stream, true)`.
+After having received the first \ref SPA_PARAM_PeerCapability param, if it contained the
+\ref PW_CAPABILITY_DEVICE_ID_NEGOTIATION set to a supported API version number, the full
+set of formats can be sent using \ref pw_stream_update_params following by activating the
+stream usina supported API version numberstream_set_active(stream, true)`.
Note that the first \ref SPA_PARAM_Format received may be the result of the initial format
negotian with bare minimum parameters, and will be superseded by the result of the format
diff --git a/meson.build b/meson.build
index c954a644c..536502a68 100644
--- a/meson.build
+++ b/meson.build
@@ -1,5 +1,5 @@
project('pipewire', ['c' ],
- version : '1.6.0',
+ version : '1.6.2',
license : [ 'MIT', 'LGPL-2.1-or-later', 'GPL-2.0-only' ],
meson_version : '>= 0.61.1',
default_options : [ 'warning_level=3',
@@ -116,6 +116,7 @@ cc_flags = common_flags + [
'-Werror=old-style-definition',
'-Werror=missing-parameter-type',
'-Werror=strict-prototypes',
+ '-Werror=discarded-qualifiers',
]
add_project_arguments(cc.get_supported_arguments(cc_flags), language: 'c')
add_project_arguments(cc_native.get_supported_arguments(cc_flags),
diff --git a/pipewire-jack/src/meson.build b/pipewire-jack/src/meson.build
index 0630d96a8..639405bb9 100644
--- a/pipewire-jack/src/meson.build
+++ b/pipewire-jack/src/meson.build
@@ -55,7 +55,7 @@ pipewire_jackserver = shared_library('jackserver',
pipewire_jackserver_sources,
soversion : soversion,
version : libjackversion,
- c_args : pipewire_jack_c_args,
+ c_args : pipewire_jack_c_args + '-DLIBJACKSERVER',
include_directories : [configinc, jack_inc],
dependencies : [pipewire_dep, mathlib],
install : true,
diff --git a/pipewire-jack/src/pipewire-jack.c b/pipewire-jack/src/pipewire-jack.c
index 1bef74283..c7017e936 100644
--- a/pipewire-jack/src/pipewire-jack.c
+++ b/pipewire-jack/src/pipewire-jack.c
@@ -492,6 +492,8 @@ struct client {
jack_position_t jack_position;
jack_transport_state_t jack_state;
struct frame_times jack_times;
+
+ struct object dummy_port;
};
#define return_val_if_fail(expr, val) \
@@ -4468,6 +4470,11 @@ jack_client_t * jack_client_open (const char *client_name,
0, NULL, &client->info);
client->info.change_mask = 0;
+ client->dummy_port.type = INTERFACE_Port;
+ snprintf(client->dummy_port.port.name, sizeof(client->dummy_port.port.name), "%s:dummy", client_name);
+ snprintf(client->dummy_port.port.alias1, sizeof(client->dummy_port.port.alias1), "%s:dummy", client_name);
+ snprintf(client->dummy_port.port.alias2, sizeof(client->dummy_port.port.alias2), "%s:dummy", client_name);
+
client->show_monitor = pw_properties_get_bool(client->props, "jack.show-monitor", true);
client->show_midi = pw_properties_get_bool(client->props, "jack.show-midi", true);
client->merge_monitor = pw_properties_get_bool(client->props, "jack.merge-monitor", true);
@@ -4880,9 +4887,21 @@ int jack_activate (jack_client_t *client)
c->activation->pending_sync = true;
spa_list_for_each(o, &c->context.objects, link) {
+#if !defined(LIBJACKSERVER)
if (o->type != INTERFACE_Port || o->port.port == NULL ||
o->port.port->client != c || !o->port.port->valid)
continue;
+#else
+ /* emits all foreign active ports, skips own (already announced via jack_port_register) */
+ if (o->type != INTERFACE_Port || o->removed)
+ continue;
+ /* own ports are handled by jack_port_register */
+ if (o->port.port != NULL && o->port.port->client == c)
+ continue;
+ /* only announce ports whose node is active */
+ if (o->port.node != NULL && !node_is_active(c, o->port.node))
+ continue;
+#endif
o->registered = 0;
queue_notify(c, NOTIFY_TYPE_PORTREGISTRATION, o, 1, NULL);
}
@@ -5318,7 +5337,7 @@ int jack_set_freewheel(jack_client_t* client, int onoff)
pw_thread_loop_lock(c->context.loop);
str = pw_properties_get(c->props, PW_KEY_NODE_GROUP);
if (str != NULL) {
- char *p = strstr(str, ",pipewire.freewheel");
+ const char *p = strstr(str, ",pipewire.freewheel");
if (p == NULL)
p = strstr(str, "pipewire.freewheel");
if (p == NULL && onoff)
@@ -5951,9 +5970,7 @@ static const char *port_name(struct object *o)
{
const char *name;
struct client *c = o->client;
- if (c == NULL)
- return NULL;
- if (c->default_as_system && is_port_default(c, o))
+ if (c != NULL && c->default_as_system && is_port_default(c, o))
name = o->port.system;
else
name = o->port.name;
@@ -6007,7 +6024,16 @@ jack_port_type_id_t jack_port_type_id (const jack_port_t *port)
return_val_if_fail(o != NULL, 0);
if (o->type != INTERFACE_Port)
return TYPE_ID_OTHER;
- return o->port.type_id;
+
+ /* map internal type IDs to jack1/jack2 compatible public values */
+ switch (o->port.type_id) {
+ case TYPE_ID_AUDIO: return 0;
+ case TYPE_ID_MIDI:
+ case TYPE_ID_OSC:
+ case TYPE_ID_UMP: return 1; /* all MIDI variants map to 1 */
+ case TYPE_ID_VIDEO: return 3; /* video maps to 3 */
+ default: return o->port.type_id;
+ }
}
SPA_EXPORT
@@ -6999,13 +7025,11 @@ jack_port_t * jack_port_by_id (jack_client_t *client,
pthread_mutex_lock(&c->context.lock);
res = find_by_serial(c, port_id);
- if (res && res->type != INTERFACE_Port)
- res = NULL;
- pw_log_debug("%p: port %d -> %p", c, port_id, res);
pthread_mutex_unlock(&c->context.lock);
+ if (res == NULL || res->type != INTERFACE_Port)
+ res = &c->dummy_port;
- if (res == NULL)
- pw_log_info("%p: port %d not found", c, port_id);
+ pw_log_debug("%p: port %d -> %p", c, port_id, res);
return object_to_port(res);
}
diff --git a/spa/plugins/alsa/acp/acp.c b/spa/plugins/alsa/acp/acp.c
index bee8d1ef4..8f5ea18c2 100644
--- a/spa/plugins/alsa/acp/acp.c
+++ b/spa/plugins/alsa/acp/acp.c
@@ -485,13 +485,11 @@ static int add_pro_profile(pa_card *impl, uint32_t index)
if ((n_capture == 1 && n_playback == 1) || is_firewire) {
PA_IDXSET_FOREACH(m, ap->output_mappings, idx) {
pa_proplist_setf(m->output_proplist, "node.group", "pro-audio-%u", index);
- pa_proplist_setf(m->output_proplist, "node.link-group", "pro-audio-%u", index);
pa_proplist_setf(m->output_proplist, "api.alsa.auto-link", "true");
pa_proplist_setf(m->output_proplist, "api.alsa.disable-tsched", "true");
}
PA_IDXSET_FOREACH(m, ap->input_mappings, idx) {
pa_proplist_setf(m->input_proplist, "node.group", "pro-audio-%u", index);
- pa_proplist_setf(m->input_proplist, "node.link-group", "pro-audio-%u", index);
pa_proplist_setf(m->input_proplist, "api.alsa.auto-link", "true");
pa_proplist_setf(m->input_proplist, "api.alsa.disable-tsched", "true");
}
diff --git a/spa/plugins/alsa/alsa-udev.c b/spa/plugins/alsa/alsa-udev.c
index 8537c9760..fdc255864 100644
--- a/spa/plugins/alsa/alsa-udev.c
+++ b/spa/plugins/alsa/alsa-udev.c
@@ -538,6 +538,9 @@ static int emit_added_object_info(struct impl *this, struct card *card)
if ((str = udev_device_get_property_value(udev_device, "ACP_PROFILE_SET")) && *str)
items[n_items++] = SPA_DICT_ITEM_INIT(SPA_KEY_DEVICE_PROFILE_SET, str);
+ if ((str = udev_device_get_property_value(udev_device, "ACP_IGNORE_DB")) && *str)
+ items[n_items++] = SPA_DICT_ITEM_INIT(SPA_KEY_API_ALSA_IGNORE_DB, str);
+
if ((str = udev_device_get_property_value(udev_device, "SOUND_CLASS")) && *str)
items[n_items++] = SPA_DICT_ITEM_INIT(SPA_KEY_DEVICE_CLASS, str);
diff --git a/spa/plugins/audiomixer/audiomixer.c b/spa/plugins/audiomixer/audiomixer.c
index 99b0658a0..54549bce8 100644
--- a/spa/plugins/audiomixer/audiomixer.c
+++ b/spa/plugins/audiomixer/audiomixer.c
@@ -725,7 +725,7 @@ static int do_port_set_io(struct spa_loop *loop, bool async, uint32_t seq,
port->io[0] = info->data;
port->io[1] = info->data;
}
- if (!port->active) {
+ if (port->direction == SPA_DIRECTION_INPUT && !port->active) {
spa_list_append(&info->impl->mix_list, &port->mix_link);
port->active = true;
}
diff --git a/spa/plugins/audiomixer/mixer-dsp.c b/spa/plugins/audiomixer/mixer-dsp.c
index 5bd4e1a1e..698426d21 100644
--- a/spa/plugins/audiomixer/mixer-dsp.c
+++ b/spa/plugins/audiomixer/mixer-dsp.c
@@ -718,7 +718,7 @@ static int do_port_set_io(struct spa_loop *loop, bool async, uint32_t seq,
port->io[0] = info->data;
port->io[1] = info->data;
}
- if (!port->active) {
+ if (port->direction == SPA_DIRECTION_INPUT && !port->active) {
spa_list_append(&info->impl->mix_list, &port->mix_link);
port->active = true;
}
diff --git a/spa/plugins/bluez5/bluez5-dbus.c b/spa/plugins/bluez5/bluez5-dbus.c
index 21a5e53de..7dfe45911 100644
--- a/spa/plugins/bluez5/bluez5-dbus.c
+++ b/spa/plugins/bluez5/bluez5-dbus.c
@@ -720,14 +720,12 @@ static const char *bap_features_get_uuid(struct bap_features *feat, size_t i)
/** Get feature name at \a i, or NULL if uuid doesn't match */
static const char *bap_features_get_name(struct bap_features *feat, size_t i, const char *uuid)
{
- char *pos;
-
if (i >= feat->dict.n_items)
return NULL;
if (!spa_streq(feat->dict.items[i].value, uuid))
return NULL;
- pos = strchr(feat->dict.items[i].key, ':');
+ const char *pos = strchr(feat->dict.items[i].key, ':');
if (!pos)
return NULL;
return pos + 1;
@@ -1336,7 +1334,6 @@ static struct spa_bt_adapter *adapter_find(struct spa_bt_monitor *monitor, const
static int parse_modalias(const char *modalias, uint16_t *source, uint16_t *vendor,
uint16_t *product, uint16_t *version)
{
- char *pos;
unsigned int src, i, j, k;
if (spa_strstartswith(modalias, "bluetooth:"))
@@ -1346,7 +1343,7 @@ static int parse_modalias(const char *modalias, uint16_t *source, uint16_t *vend
else
return -EINVAL;
- pos = strchr(modalias, ':');
+ const char *pos = strchr(modalias, ':');
if (pos == NULL)
return -EINVAL;
diff --git a/spa/plugins/filter-graph/filter-graph.c b/spa/plugins/filter-graph/filter-graph.c
index 52609f2c6..05ba7b3fb 100644
--- a/spa/plugins/filter-graph/filter-graph.c
+++ b/spa/plugins/filter-graph/filter-graph.c
@@ -1035,8 +1035,8 @@ static struct descriptor *descriptor_load(struct impl *impl, const char *type,
}
} else if (SPA_FGA_IS_PORT_CONTROL(fp->flags)) {
if (SPA_FGA_IS_PORT_INPUT(fp->flags)) {
- spa_log_info(impl->log, "using port %lu ('%s') as control %d", p,
- fp->name, desc->n_control);
+ spa_log_info(impl->log, "using port %lu ('%s') as control %d %f/%f/%f", p,
+ fp->name, desc->n_control, fp->def, fp->min, fp->max);
desc->control[desc->n_control++] = p;
}
else if (SPA_FGA_IS_PORT_OUTPUT(fp->flags)) {
@@ -1622,6 +1622,7 @@ static int impl_activate(void *object, const struct spa_dict *props)
goto error;
}
}
+ node->control_changed = true;
}
/* then link ports */
diff --git a/spa/plugins/filter-graph/plugin_ladspa.c b/spa/plugins/filter-graph/plugin_ladspa.c
index d5c8ef488..1aebafe35 100644
--- a/spa/plugins/filter-graph/plugin_ladspa.c
+++ b/spa/plugins/filter-graph/plugin_ladspa.c
@@ -7,6 +7,7 @@
#include
#include
#include
+#include
#include
#include
@@ -113,8 +114,14 @@ static void ladspa_port_update_ranges(struct descriptor *dd, struct spa_fga_port
LADSPA_PortRangeHintDescriptor hint = d->PortRangeHints[p].HintDescriptor;
LADSPA_Data lower, upper;
- lower = d->PortRangeHints[p].LowerBound;
- upper = d->PortRangeHints[p].UpperBound;
+ if (hint & LADSPA_HINT_BOUNDED_BELOW)
+ lower = d->PortRangeHints[p].LowerBound;
+ else
+ lower = -FLT_MAX;
+ if (hint & LADSPA_HINT_BOUNDED_ABOVE)
+ upper = d->PortRangeHints[p].UpperBound;
+ else
+ upper = FLT_MAX;
port->hint = 0;
if (hint & LADSPA_HINT_TOGGLED)
diff --git a/spa/plugins/filter-graph/plugin_lv2.c b/spa/plugins/filter-graph/plugin_lv2.c
index 712b728e2..b2d3fc6cc 100644
--- a/spa/plugins/filter-graph/plugin_lv2.c
+++ b/spa/plugins/filter-graph/plugin_lv2.c
@@ -560,6 +560,17 @@ static const struct spa_fga_descriptor *lv2_plugin_make_desc(void *plugin, const
fp->min = mins[i];
fp->max = maxes[i];
fp->def = controls[i];
+
+ if (isnan(fp->min))
+ fp->min = -FLT_MAX;
+ if (isnan(fp->max))
+ fp->max = FLT_MAX;
+ if (isnan(fp->def))
+ fp->def = 0.0f;
+ if (fp->max <= fp->min)
+ fp->max = FLT_MAX;
+ if (fp->def <= fp->min)
+ fp->min = -FLT_MAX;
}
return &desc->desc;
}
diff --git a/spa/plugins/libcamera/libcamera-source.cpp b/spa/plugins/libcamera/libcamera-source.cpp
index f0eaa68f0..fba3d8b0b 100644
--- a/spa/plugins/libcamera/libcamera-source.cpp
+++ b/spa/plugins/libcamera/libcamera-source.cpp
@@ -184,9 +184,6 @@ struct impl {
0, nullptr, 0, this
);
- if (source.fd >= 0)
- spa_system_close(system, std::exchange(source.fd, -1));
-
camera->requestCompleted.disconnect(this, &impl::requestComplete);
if (int res = camera->stop(); res < 0) {
@@ -194,6 +191,9 @@ struct impl {
camera->id().c_str(), spa_strerror(res));
}
+ if (source.fd >= 0)
+ spa_system_close(system, std::exchange(source.fd, -1));
+
completed_requests_rb = SPA_RINGBUFFER_INIT();
active = false;
@@ -2163,7 +2163,7 @@ impl::impl(spa_log *log, spa_loop *data_loop, spa_system *system,
&impl_node, this);
params[NODE_PropInfo] = SPA_PARAM_INFO(SPA_PARAM_PropInfo, SPA_PARAM_INFO_READ);
- params[NODE_Props] = SPA_PARAM_INFO(SPA_PARAM_Props, SPA_PARAM_INFO_READWRITE);
+ params[NODE_Props] = SPA_PARAM_INFO(SPA_PARAM_Props, SPA_PARAM_INFO_WRITE);
params[NODE_EnumFormat] = SPA_PARAM_INFO(SPA_PARAM_EnumFormat, SPA_PARAM_INFO_READ);
params[NODE_Format] = SPA_PARAM_INFO(SPA_PARAM_Format, SPA_PARAM_INFO_WRITE);
diff --git a/spa/plugins/support/logger.c b/spa/plugins/support/logger.c
index c6e6ca4b8..6ea5f31b5 100644
--- a/spa/plugins/support/logger.c
+++ b/spa/plugins/support/logger.c
@@ -73,7 +73,7 @@ impl_log_logtv(void *object,
char timestamp[18] = {0};
char topicstr[32] = {0};
char filename[64] = {0};
- char location[1000 + RESERVED_LENGTH], *p, *s;
+ char location[1000 + RESERVED_LENGTH], *p;
static const char * const levels[] = { "-", "E", "W", "I", "D", "T", "*T*" };
const char *prefix = "", *suffix = "";
int size, len;
@@ -118,7 +118,7 @@ impl_log_logtv(void *object,
if (impl->line && line != 0) {
- s = strrchr(file, '/');
+ const char *s = strrchr(file, '/');
spa_scnprintf(filename, sizeof(filename), "[%16.16s:%5i %s()]",
s ? s + 1 : file, line, func);
}
diff --git a/spa/plugins/v4l2/v4l2-device.c b/spa/plugins/v4l2/v4l2-device.c
index 2379d2105..f29252d16 100644
--- a/spa/plugins/v4l2/v4l2-device.c
+++ b/spa/plugins/v4l2/v4l2-device.c
@@ -98,9 +98,9 @@ static int emit_info(struct impl *this, bool full)
(this->dev.cap.version >> 8) & 0xFF,
(this->dev.cap.version) & 0xFF);
ADD_ITEM(SPA_KEY_API_V4L2_CAP_VERSION, version);
- snprintf(capabilities, sizeof(capabilities), "%08x", this->dev.cap.capabilities);
+ snprintf(capabilities, sizeof(capabilities), "0x%08x", this->dev.cap.capabilities);
ADD_ITEM(SPA_KEY_API_V4L2_CAP_CAPABILITIES, capabilities);
- snprintf(device_caps, sizeof(device_caps), "%08x", this->dev.cap.device_caps);
+ snprintf(device_caps, sizeof(device_caps), "0x%08x", this->dev.cap.device_caps);
ADD_ITEM(SPA_KEY_API_V4L2_CAP_DEVICE_CAPS, device_caps);
#undef ADD_ITEM
info.props = &SPA_DICT_INIT(items, n_items);
diff --git a/src/gst/gstpipewiresrc.c b/src/gst/gstpipewiresrc.c
index b0de17dfd..6ca9599d6 100644
--- a/src/gst/gstpipewiresrc.c
+++ b/src/gst/gstpipewiresrc.c
@@ -531,6 +531,7 @@ gst_pipewire_src_init (GstPipeWireSrc * src)
src->autoconnect = DEFAULT_AUTOCONNECT;
src->min_latency = 0;
src->max_latency = GST_CLOCK_TIME_NONE;
+ src->last_buffer_clock_time = GST_CLOCK_TIME_NONE;
src->n_buffers = 0;
src->flushing_on_remove_buffer = FALSE;
src->on_disconnect = DEFAULT_ON_DISCONNECT;
@@ -1598,16 +1599,31 @@ gst_pipewire_src_create (GstPushSrc * psrc, GstBuffer ** buffer)
GST_LOG_OBJECT (pwsrc, "EOS, send last buffer");
break;
} else if (timeout && pwsrc->last_buffer != NULL) {
+ buf = gst_buffer_copy (pwsrc->last_buffer);
update_time = TRUE;
- buf = gst_buffer_ref(pwsrc->last_buffer);
GST_LOG_OBJECT (pwsrc, "timeout, send keepalive buffer");
break;
} else {
buf = dequeue_buffer (pwsrc);
GST_LOG_OBJECT (pwsrc, "popped buffer %p", buf);
if (buf != NULL) {
- if (pwsrc->resend_last || pwsrc->keepalive_time > 0)
- gst_buffer_replace (&pwsrc->last_buffer, buf);
+ if (pwsrc->resend_last || pwsrc->keepalive_time > 0) {
+ GstClock *clock;
+ GstBuffer *old;
+
+ old = pwsrc->last_buffer;
+ pwsrc->last_buffer = gst_buffer_copy (buf);
+ gst_buffer_unref (old);
+ gst_buffer_add_parent_buffer_meta (pwsrc->last_buffer, buf);
+
+ clock = gst_element_get_clock (GST_ELEMENT_CAST (pwsrc));
+ if (clock != NULL) {
+ pwsrc->last_buffer_clock_time = gst_clock_get_time (clock);
+ gst_object_unref (clock);
+ } else {
+ pwsrc->last_buffer_clock_time = GST_CLOCK_TIME_NONE;
+ }
+ }
break;
}
}
@@ -1632,21 +1648,33 @@ gst_pipewire_src_create (GstPushSrc * psrc, GstBuffer ** buffer)
if (update_time) {
GstClock *clock;
- GstClockTime pts, dts;
+ GstClockTime current_clock_time;
clock = gst_element_get_clock (GST_ELEMENT_CAST (pwsrc));
if (clock != NULL) {
- pts = dts = gst_clock_get_time (clock);
+ current_clock_time = gst_clock_get_time (clock);
gst_object_unref (clock);
} else {
- pts = dts = GST_CLOCK_TIME_NONE;
+ current_clock_time = GST_CLOCK_TIME_NONE;
}
- GST_BUFFER_PTS (*buffer) = pts;
- GST_BUFFER_DTS (*buffer) = dts;
+ if (GST_CLOCK_TIME_IS_VALID (current_clock_time) &&
+ GST_CLOCK_TIME_IS_VALID (pwsrc->last_buffer_clock_time) &&
+ GST_CLOCK_TIME_IS_VALID (GST_BUFFER_PTS (*buffer)) &&
+ GST_CLOCK_TIME_IS_VALID (GST_BUFFER_DTS (*buffer))) {
+ GstClockTime diff;
+
+ diff = current_clock_time - pwsrc->last_buffer_clock_time;
+
+ GST_BUFFER_PTS (*buffer) += diff;
+ GST_BUFFER_DTS (*buffer) += diff;
+ } else {
+ GST_BUFFER_PTS (*buffer) = GST_BUFFER_DTS (*buffer) = current_clock_time;
+ }
GST_LOG_OBJECT (pwsrc, "Sending keepalive buffer pts/dts: %" GST_TIME_FORMAT
- " (%" G_GUINT64_FORMAT ")", GST_TIME_ARGS (pts), pts);
+ " (%" G_GUINT64_FORMAT ")", GST_TIME_ARGS (current_clock_time),
+ current_clock_time);
}
return GST_FLOW_OK;
diff --git a/src/gst/gstpipewiresrc.h b/src/gst/gstpipewiresrc.h
index 869877fcb..4b0f57e0e 100644
--- a/src/gst/gstpipewiresrc.h
+++ b/src/gst/gstpipewiresrc.h
@@ -83,6 +83,7 @@ struct _GstPipeWireSrc {
GstClockTime max_latency;
GstBuffer *last_buffer;
+ GstClockTime last_buffer_clock_time;
enum spa_meta_videotransform_value transform_value;
diff --git a/src/modules/module-client-node/client-node.c b/src/modules/module-client-node/client-node.c
index 0253591cf..9740e1c89 100644
--- a/src/modules/module-client-node/client-node.c
+++ b/src/modules/module-client-node/client-node.c
@@ -264,7 +264,8 @@ static void clear_data(struct impl *impl, struct spa_data *d)
case SPA_DATA_DmaBuf:
case SPA_DATA_SyncObj:
pw_log_debug("%p: close fd:%d", impl, (int)d->fd);
- close(d->fd);
+ if (d->fd != -1)
+ close(d->fd);
break;
}
}
@@ -864,8 +865,11 @@ do_port_use_buffers(struct impl *impl,
memcpy(&b->datas[j], d, sizeof(struct spa_data));
- if (flags & SPA_NODE_BUFFERS_FLAG_ALLOC)
+ if (flags & SPA_NODE_BUFFERS_FLAG_ALLOC) {
+ b->datas[j].fd = -1;
+ b->datas[j].data = SPA_UINT32_TO_PTR(SPA_ID_INVALID);
continue;
+ }
switch (d->type) {
case SPA_DATA_DmaBuf:
diff --git a/src/modules/module-ffado-driver.c b/src/modules/module-ffado-driver.c
index f6a76bed0..05b9218ce 100644
--- a/src/modules/module-ffado-driver.c
+++ b/src/modules/module-ffado-driver.c
@@ -1556,8 +1556,6 @@ int pipewire__module_init(struct pw_impl_module *module, const char *args)
pw_properties_set(props, PW_KEY_NODE_VIRTUAL, "true");
if (pw_properties_get(props, PW_KEY_NODE_GROUP) == NULL)
pw_properties_set(props, PW_KEY_NODE_GROUP, "ffado-group");
- if (pw_properties_get(props, PW_KEY_NODE_LINK_GROUP) == NULL)
- pw_properties_set(props, PW_KEY_NODE_LINK_GROUP, "ffado-group");
if (pw_properties_get(props, PW_KEY_NODE_PAUSE_ON_IDLE) == NULL)
pw_properties_set(props, PW_KEY_NODE_PAUSE_ON_IDLE, "false");
diff --git a/src/modules/module-netjack2-manager.c b/src/modules/module-netjack2-manager.c
index 0ff62d93b..a9af6a9b0 100644
--- a/src/modules/module-netjack2-manager.c
+++ b/src/modules/module-netjack2-manager.c
@@ -1393,8 +1393,6 @@ int pipewire__module_init(struct pw_impl_module *module, const char *args)
pw_properties_set(props, PW_KEY_NODE_VIRTUAL, "true");
if (pw_properties_get(props, PW_KEY_NODE_NETWORK) == NULL)
pw_properties_set(props, PW_KEY_NODE_NETWORK, "true");
- if (pw_properties_get(props, PW_KEY_NODE_LINK_GROUP) == NULL)
- pw_properties_set(props, PW_KEY_NODE_LINK_GROUP, "jack-group");
if (pw_properties_get(props, PW_KEY_NODE_ALWAYS_PROCESS) == NULL)
pw_properties_set(props, PW_KEY_NODE_ALWAYS_PROCESS, "true");
if (pw_properties_get(props, PW_KEY_NODE_LOCK_QUANTUM) == NULL)
diff --git a/src/modules/module-protocol-native.c b/src/modules/module-protocol-native.c
index 2be92a847..98a43829b 100644
--- a/src/modules/module-protocol-native.c
+++ b/src/modules/module-protocol-native.c
@@ -907,7 +907,7 @@ static int add_socket(struct pw_protocol *protocol, struct server *s, struct soc
bool activated = false;
{
- int i, n = listen_fd();
+ int i, n = listen_fds();
for (i = 0; i < n; ++i) {
if (is_socket_unix(LISTEN_FDS_START + i, SOCK_STREAM,
s->addr.sun_path) > 0) {
diff --git a/src/modules/module-protocol-pulse/server.c b/src/modules/module-protocol-pulse/server.c
index aeab710b0..637757dfd 100644
--- a/src/modules/module-protocol-pulse/server.c
+++ b/src/modules/module-protocol-pulse/server.c
@@ -576,7 +576,7 @@ static bool is_stale_socket(int fd, const struct sockaddr_un *addr_un)
static int check_socket_activation(const char *path)
{
- const int n = listen_fd();
+ const int n = listen_fds();
for (int i = 0; i < n; i++) {
const int fd = LISTEN_FDS_START + i;
diff --git a/src/modules/network-utils.h b/src/modules/network-utils.h
index a89b7d3bd..6ff80dd7a 100644
--- a/src/modules/network-utils.h
+++ b/src/modules/network-utils.h
@@ -143,7 +143,7 @@ static inline bool pw_net_addr_is_any(struct sockaddr_storage *addr)
/* Returns the number of file descriptors passed for socket activation.
* Returns 0 if none, -1 on error. */
-static inline int listen_fd(void)
+static inline int listen_fds(void)
{
uint32_t n;
int i, flags;
@@ -161,8 +161,6 @@ static inline int listen_fd(void)
return -1;
}
- unsetenv("LISTEN_FDS");
-
return (int)n;
}
@@ -192,12 +190,10 @@ static inline int is_socket_unix(int fd, int type, const char *path)
if (addr.sun_family != AF_UNIX)
return 0;
size_t length = strlen(path);
- if (length > 0) {
- if (len < offsetof(struct sockaddr_un, sun_path) + length)
- return 0;
- if (memcmp(addr.sun_path, path, length) != 0)
- return 0;
- }
+ if (len < offsetof(struct sockaddr_un, sun_path) + length + 1)
+ return 0;
+ if (memcmp(addr.sun_path, path, length + 1) != 0)
+ return 0;
}
return 1;
diff --git a/src/pipewire/capabilities.h b/src/pipewire/capabilities.h
index d3040b761..b8431ac85 100644
--- a/src/pipewire/capabilities.h
+++ b/src/pipewire/capabilities.h
@@ -21,11 +21,14 @@ extern "C" {
* \{
*/
-/**< Link capable of device ID negotiation. The value is either "true" or "false" */
+/**< Link capable of device ID negotiation. The value is to the version of the
+ * API specification. */
#define PW_CAPABILITY_DEVICE_ID_NEGOTIATION "pipewire.device-id-negotiation"
/**< Link with device ID negotition capability supports negotiating with
- * provided list of devices. The value consists of a JSON encoded string array
- * of base64 encoded dev_t values. */
+ * a specific set of devices. The value of API version 1 consists of a JSON
+ * object containing a single key "available-devices" that contain a list of
+ * hexadecimal encoded `dev_t` device IDs.
+ */
#define PW_CAPABILITY_DEVICE_IDS "pipewire.device-ids"
#define PW_CAPABILITY_DEVICE_ID "pipewire.device-id" /**< Link capable of device Id negotation */
diff --git a/src/pipewire/impl-link.c b/src/pipewire/impl-link.c
index a77dcf35f..a7ea65cb2 100644
--- a/src/pipewire/impl-link.c
+++ b/src/pipewire/impl-link.c
@@ -701,7 +701,10 @@ static int do_allocation(struct pw_impl_link *this)
/* always enable async mode */
alloc_flags = PW_BUFFERS_FLAG_ASYNC;
- if (output->node->remote || input->node->remote)
+ /* shared mem can only be used if both nodes are in the same process
+ * and we are sure that the buffers are never going to be shared
+ * because of the exclusive flag */
+ if (output->node->remote || input->node->remote || !output->exclusive)
alloc_flags |= PW_BUFFERS_FLAG_SHARED;
if (output->node->driver)
diff --git a/src/tools/pw-cat.c b/src/tools/pw-cat.c
index f90ebffc9..6b1916d55 100644
--- a/src/tools/pw-cat.c
+++ b/src/tools/pw-cat.c
@@ -196,55 +196,60 @@ struct data {
};
static const struct format_info {
- const char *name;
+ const char *sf_name;
int sf_format;
+ uint32_t sf_width;
+ const char *spa_name;
uint32_t spa_format;
- uint32_t width;
+ uint32_t spa_width;
+#define FORMAT_ENCODED (1<<0)
+ uint32_t flags;
} format_info[] = {
- { "ulaw", SF_FORMAT_ULAW, SPA_AUDIO_FORMAT_ULAW, 1 },
- { "alaw", SF_FORMAT_ULAW, SPA_AUDIO_FORMAT_ALAW, 1 },
- { "s8", SF_FORMAT_PCM_S8, SPA_AUDIO_FORMAT_S8, 1 },
- { "u8", SF_FORMAT_PCM_U8, SPA_AUDIO_FORMAT_U8, 1 },
- { "s16", SF_FORMAT_PCM_16, SPA_AUDIO_FORMAT_S16, 2 },
- { "s24", SF_FORMAT_PCM_24, SPA_AUDIO_FORMAT_S24, 3 },
- { "s32", SF_FORMAT_PCM_32, SPA_AUDIO_FORMAT_S32, 4 },
- { "f32", SF_FORMAT_FLOAT, SPA_AUDIO_FORMAT_F32, 4 },
- { "f64", SF_FORMAT_DOUBLE, SPA_AUDIO_FORMAT_F32, 8 },
+ { "ulaw", SF_FORMAT_ULAW, 1, "ulaw", SPA_AUDIO_FORMAT_ULAW, 1, 0 },
+ { "alaw", SF_FORMAT_ULAW, 1, "alaw", SPA_AUDIO_FORMAT_ALAW, 1, 0 },
+ { "s8", SF_FORMAT_PCM_S8, 1, "s8", SPA_AUDIO_FORMAT_S8, 1, 0 },
+ { "u8", SF_FORMAT_PCM_U8, 1, "u8", SPA_AUDIO_FORMAT_U8, 1, 0 },
+ { "s16", SF_FORMAT_PCM_16, 2, "s16", SPA_AUDIO_FORMAT_S16, 2, 0 },
+ /* we read and write S24 as S32 with sndfile */
+ { "s24", SF_FORMAT_PCM_24, 3, "s32", SPA_AUDIO_FORMAT_S32, 4, 0 },
+ { "s32", SF_FORMAT_PCM_32, 4, "s32", SPA_AUDIO_FORMAT_S32, 4, 0 },
+ { "f32", SF_FORMAT_FLOAT, 4, "f32", SPA_AUDIO_FORMAT_F32, 4, 0 },
+ { "f64", SF_FORMAT_DOUBLE, 8, "f64", SPA_AUDIO_FORMAT_F32, 8, 0 },
- { "mp1", SF_FORMAT_MPEG_LAYER_I, SPA_AUDIO_FORMAT_F32, 1 },
- { "mp2", SF_FORMAT_MPEG_LAYER_II, SPA_AUDIO_FORMAT_F32, 1 },
- { "mp3", SF_FORMAT_MPEG_LAYER_III, SPA_AUDIO_FORMAT_F32, 1 },
- { "vorbis", SF_FORMAT_VORBIS, SPA_AUDIO_FORMAT_F32, 1 },
- { "opus", SF_FORMAT_OPUS, SPA_AUDIO_FORMAT_F32, 1 },
+ { "mp1", SF_FORMAT_MPEG_LAYER_I, 1, "f32", SPA_AUDIO_FORMAT_F32, 4, FORMAT_ENCODED },
+ { "mp2", SF_FORMAT_MPEG_LAYER_II, 1, "f32", SPA_AUDIO_FORMAT_F32, 4, FORMAT_ENCODED },
+ { "mp3", SF_FORMAT_MPEG_LAYER_III, 1, "f32", SPA_AUDIO_FORMAT_F32, 4, FORMAT_ENCODED },
+ { "vorbis", SF_FORMAT_VORBIS, 1, "f32", SPA_AUDIO_FORMAT_F32, 4, FORMAT_ENCODED },
+ { "opus", SF_FORMAT_OPUS, 1, "f32", SPA_AUDIO_FORMAT_F32, 4, FORMAT_ENCODED },
- { "ima-adpcm", SF_FORMAT_IMA_ADPCM, SPA_AUDIO_FORMAT_F32, 1 },
- { "ms-adpcm", SF_FORMAT_MS_ADPCM, SPA_AUDIO_FORMAT_F32, 1 },
- { "nms-adpcm-16", SF_FORMAT_NMS_ADPCM_16, SPA_AUDIO_FORMAT_F32, 1 },
- { "nms-adpcm-24", SF_FORMAT_NMS_ADPCM_24, SPA_AUDIO_FORMAT_F32, 1 },
- { "nms-adpcm-32", SF_FORMAT_NMS_ADPCM_32, SPA_AUDIO_FORMAT_F32, 1 },
+ { "ima-adpcm", SF_FORMAT_IMA_ADPCM, 1, "f32", SPA_AUDIO_FORMAT_F32, 4, FORMAT_ENCODED },
+ { "ms-adpcm", SF_FORMAT_MS_ADPCM, 1, "f32", SPA_AUDIO_FORMAT_F32, 4, FORMAT_ENCODED },
+ { "nms-adpcm-16", SF_FORMAT_NMS_ADPCM_16, 1, "f32", SPA_AUDIO_FORMAT_F32, 4, FORMAT_ENCODED },
+ { "nms-adpcm-24", SF_FORMAT_NMS_ADPCM_24, 1, "f32", SPA_AUDIO_FORMAT_F32, 4, FORMAT_ENCODED },
+ { "nms-adpcm-32", SF_FORMAT_NMS_ADPCM_32, 1, "f32", SPA_AUDIO_FORMAT_F32, 4, FORMAT_ENCODED },
- { "alac-16", SF_FORMAT_ALAC_16, SPA_AUDIO_FORMAT_F32, 1 },
- { "alac-20", SF_FORMAT_ALAC_20, SPA_AUDIO_FORMAT_F32, 1 },
- { "alac-24", SF_FORMAT_ALAC_24, SPA_AUDIO_FORMAT_F32, 1 },
- { "alac-32", SF_FORMAT_ALAC_32, SPA_AUDIO_FORMAT_F32, 1 },
+ { "alac-16", SF_FORMAT_ALAC_16, 1, "f32", SPA_AUDIO_FORMAT_F32, 4, FORMAT_ENCODED },
+ { "alac-20", SF_FORMAT_ALAC_20, 1, "f32", SPA_AUDIO_FORMAT_F32, 4, FORMAT_ENCODED },
+ { "alac-24", SF_FORMAT_ALAC_24, 1, "f32", SPA_AUDIO_FORMAT_F32, 4, FORMAT_ENCODED },
+ { "alac-32", SF_FORMAT_ALAC_32, 1, "f32", SPA_AUDIO_FORMAT_F32, 4, FORMAT_ENCODED },
- { "gsm610", SF_FORMAT_GSM610, SPA_AUDIO_FORMAT_F32, 1 },
- { "g721-32", SF_FORMAT_G721_32, SPA_AUDIO_FORMAT_F32, 1 },
- { "g723-24", SF_FORMAT_G723_24, SPA_AUDIO_FORMAT_F32, 1 },
- { "g723-40", SF_FORMAT_G723_40, SPA_AUDIO_FORMAT_F32, 1 },
- { "dwvw-12", SF_FORMAT_DWVW_12, SPA_AUDIO_FORMAT_F32, 1 },
- { "dwvw-16", SF_FORMAT_DWVW_16, SPA_AUDIO_FORMAT_F32, 1 },
- { "dwvw-24", SF_FORMAT_DWVW_24, SPA_AUDIO_FORMAT_F32, 1 },
- { "vox", SF_FORMAT_VOX_ADPCM, SPA_AUDIO_FORMAT_F32, 1 },
- { "dpcm-16", SF_FORMAT_DPCM_16, SPA_AUDIO_FORMAT_F32, 1 },
- { "dpcm-8", SF_FORMAT_DPCM_8, SPA_AUDIO_FORMAT_F32, 1 },
+ { "gsm610", SF_FORMAT_GSM610, 1, "f32", SPA_AUDIO_FORMAT_F32, 4, FORMAT_ENCODED },
+ { "g721-32", SF_FORMAT_G721_32, 1, "f32", SPA_AUDIO_FORMAT_F32, 4, FORMAT_ENCODED },
+ { "g723-24", SF_FORMAT_G723_24, 1, "f32", SPA_AUDIO_FORMAT_F32, 4, FORMAT_ENCODED },
+ { "g723-40", SF_FORMAT_G723_40, 1, "f32", SPA_AUDIO_FORMAT_F32, 4, FORMAT_ENCODED },
+ { "dwvw-12", SF_FORMAT_DWVW_12, 1, "f32", SPA_AUDIO_FORMAT_F32, 4, FORMAT_ENCODED },
+ { "dwvw-16", SF_FORMAT_DWVW_16, 1, "f32", SPA_AUDIO_FORMAT_F32, 4, FORMAT_ENCODED },
+ { "dwvw-24", SF_FORMAT_DWVW_24, 1, "f32", SPA_AUDIO_FORMAT_F32, 4, FORMAT_ENCODED },
+ { "vox", SF_FORMAT_VOX_ADPCM, 1, "f32", SPA_AUDIO_FORMAT_F32, 4, FORMAT_ENCODED },
+ { "dpcm-16", SF_FORMAT_DPCM_16, 1, "f32", SPA_AUDIO_FORMAT_F32, 4, FORMAT_ENCODED },
+ { "dpcm-8", SF_FORMAT_DPCM_8, 1, "f32", SPA_AUDIO_FORMAT_F32, 4, FORMAT_ENCODED },
};
static const struct format_info *format_info_by_name(const char *str)
{
SPA_FOR_EACH_ELEMENT_VAR(format_info, i)
- if (spa_streq(str, i->name))
+ if (spa_streq(str, i->sf_name))
return i;
return NULL;
}
@@ -263,7 +268,7 @@ static void list_formats(struct data *d)
fprintf(stdout, _("Supported formats:\n"));
SPA_FOR_EACH_ELEMENT_VAR(format_info, i)
- fprintf(stdout, " %s\n", i->name);
+ fprintf(stdout, " %s\n", i->sf_name);
}
static int sf_playback_fill_x8(struct data *d, void *dest, unsigned int n_frames, bool *null_frame)
@@ -1676,8 +1681,13 @@ static int setup_raw(struct data *data)
if (info == NULL)
return -EINVAL;
+ if (info->flags & FORMAT_ENCODED) {
+ fprintf(stderr, "raw: raw encoded format %s not supported\n", info->sf_name);
+ return -ENOTSUP;
+ }
+
data->spa_format = info->spa_format;
- data->stride = info->width * data->channels;
+ data->stride = info->spa_width * data->channels;
data->fill = data->mode == mode_playback ? raw_play : raw_record;
if (spa_streq(data->filename, "-")) {
@@ -1696,7 +1706,7 @@ static int setup_raw(struct data *data)
if (data->verbose)
fprintf(stderr, "raw: rate=%u channels=%u fmt=%s samplesize=%u stride=%u\n",
data->rate, data->channels,
- info->name, info->width, data->stride);
+ info->spa_name, info->spa_width, data->stride);
return 0;
}
@@ -2034,14 +2044,10 @@ static int setup_sndfile(struct data *data)
if (data->verbose)
fprintf(stderr, "PCM: fmt:%s rate:%u channels:%u width:%u\n",
- fi->name, data->rate, data->channels, fi->width);
-
- /* we read and write S24 as S32 with sndfile */
- if (fi->spa_format == SPA_AUDIO_FORMAT_S24)
- fi = format_info_by_sf_format(SF_FORMAT_PCM_32);
+ fi->spa_name, data->rate, data->channels, fi->spa_width);
data->spa_format = fi->spa_format;
- data->stride = fi->width * data->channels;
+ data->stride = fi->spa_width * data->channels;
data->fill = data->mode == mode_playback ?
playback_fill_fn(data->spa_format) :
record_fill_fn(data->spa_format);