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);