Compare commits

...

28 commits

Author SHA1 Message Date
Wim Taymans
95da54a482 1.6.2 2026-03-16 12:54:17 +01:00
Wim Taymans
54d0d4e55a filter-graph: fix up def/min/max values for lv2
lv2 filters can return NAN for the min/max and default values when not
specified. Handle them so that we don't end up clamping NAN numbers.

Fixes #5166
2026-03-16 12:32:46 +01:00
Wim Taymans
31874b3764 filter-graph: only use min/max when defined in LADSPA
Fixes #5170
2026-03-16 12:32:42 +01:00
Wim Taymans
53b870934b doc: add 1.6 link 2026-03-13 13:25:57 +01:00
Nedko Arnaudov
aa55e43275 pipewire-jack: emit foreign port registration callbacks on jack_activate
The jack_activate loop was only queuing NOTIFY_TYPE_PORTREGISTRATION
for the activating client's own ports. Ports belonging to other clients
— including all WirePlumber-managed ports and MIDI ports — were silently
skipped due to the o->port.port->client != c condition.

This caused two observable bugs for clients using libjackserver (e.g.
jackdbus):
- JackPortRegistrationCallback was not fired for any pre-existing
  foreign ports at activate time, leaving the patchbay empty unless
  the session manager happened to start after the client.
- JACK MIDI ports were never announced via callback, even though they
  are correctly returned by jack_get_ports().

The graph_order_callback fallback (used by jackdbus for initial port
enumeration) is also ineffective here because pipewire-jack only fires
it on connection events, not on activate.

Fix by iterating all non-removed foreign ports in the object list and
queuing registration callbacks for those whose node is active, matching
the semantics already implemented in node_info() for ports of nodes
that transition to running state after activate.

The change is libjackserver.so only. libjack.so behaviour is left
unmodifed, as carla is showing ports of each client twice.
2026-03-13 09:23:05 +01:00
Wim Taymans
cab633b4f8 Revert "pipewire-jack: emit foreign port registration callbacks on jack_activate"
This reverts commit 45633303aa.
2026-03-12 15:01:25 +01:00
Nedko Arnaudov
b00e8f8bb2 pipewire-jack: fix jack_port_type_id() to return jack1/jack2 compatible values
pipewire-jack defines TYPE_ID_VIDEO=1 between audio and MIDI, shifting
TYPE_ID_MIDI to 2. This caused jack_port_type_id() to return 2 for MIDI
ports, breaking compatibility with jack1/jack2 which return 1.

The jack_port_type_id() return value is part of the public JACK API and
consumers such as jackdbus rely on the conventional values established
by jack1/jack2: 0 for audio, 1 for MIDI.

Map internal TYPE_ID_* values to their jack1/jack2 compatible equivalents
before returning. All MIDI variants (MIDI, OSC, UMP) map to 1. Video has
no jack1/jack2 equivalent so maps to 3, beyond the conventional range.
2026-03-12 10:14:14 +01:00
Nedko Arnaudov
45633303aa pipewire-jack: emit foreign port registration callbacks on jack_activate
The jack_activate loop was only queuing NOTIFY_TYPE_PORTREGISTRATION
for the activating client's own ports. Ports belonging to other clients
— including all WirePlumber-managed ports and MIDI ports — were silently
skipped due to the o->port.port->client != c condition.

This caused two observable bugs for clients using libjackserver (e.g.
jackdbus):
- JackPortRegistrationCallback was not fired for any pre-existing
  foreign ports at activate time, leaving the patchbay empty unless
  the session manager happened to start after the client.
- JACK MIDI ports were never announced via callback, even though they
  are correctly returned by jack_get_ports().

The graph_order_callback fallback (used by jackdbus for initial port
enumeration) is also ineffective here because pipewire-jack only fires
it on connection events, not on activate.

Fix by iterating all non-removed foreign ports in the object list and
queuing registration callbacks for those whose node is active, matching
the semantics already implemented in node_info() for ports of nodes
that transition to running state after activate.
2026-03-12 10:14:10 +01:00
Wim Taymans
f4a6648aa5 filter-graph: emit control change after loading
Always do a control change to the instances when they are created. This
is to make sure the internal state is synced to the control values.

The sofa filter and biquads need this to correctly configure themselves
after a suspend.

Fixes #5152
2026-03-11 12:46:17 +01:00
Wim Taymans
cb9b3861ce audiomixer: only add the input port to mix_list
Adding the output port is not a problem because there is never a buffer
to consume and mix but it wastes cycles.
2026-03-11 12:46:12 +01:00
Barnabás Pőcze
17f423b8f6 spa: libcamera: source: SPA_PARAM_Props is write-only
There are no readable `SPA_PARAM_Props` on the node, so mark it write-only.

(cherry picked from commit 810617997b)
2026-03-10 23:50:37 +01:00
Wim Taymans
55f6c35e78 client-node: avoid using invalid fd or mem in clear_data
Don't close an -1 fd in clear_data.

If we let the client allocate buffer, set our fd and data to invalid
values. If the client decides to renegotiate before we get the buffer
data we might otherwise try to clear the mem_id (default 0) or
close the fd (also default 0).

Fixes #5162
2026-03-10 17:58:07 +01:00
Jonas Ådahl
3a62ea0217 pipewire/capabilities: Update device ID negotation according to API docs
It was was documenting a previous iteration of the protocol which used a
boolean and a base64 encoded list.
2026-03-10 17:54:10 +01:00
Jonas Ådahl
f26eb9501e docs/dma-buf: Document the correct device ID negotation key
The correct key is PW_CAPABILITY_DEVICE_ID_NEGOTIATION which contains a
number.
2026-03-10 17:54:01 +01:00
Ripley Tom
92f8e16f11
meson.build: Add -Werror=discarded-qualifiers
(cherry picked from commit ff04b47942)
2026-03-10 00:03:33 +00:00
Barnabás Pőcze
c3d7373cf9
treewide: fix some -Wdiscarded-qualifiers
Newer glibc versions have made certain `str*()` functions into macros
that ensure that the const-ness of the argument is propagated to the
return type.

(cherry picked from commit e46bfe67b6)
2026-03-10 00:03:14 +00:00
Wim Taymans
4c2692342e impl-link: fix shared mem test
We can only use non-shared memory when both nodes live in the same
process _and_ we can be sure the output port is never going to be linked
to a remote node because it is exclusive.

This fixes the case where a null-sink is loaded inside the process space
of the server and linked to the ALSA sink. This would create a link
without shared mem and then as soon as something else (out of process)
wants to link to the null-sink output, it would get a -22 EINVAL
negotiation error because the memory can't be shared.

Fixes #5159
2026-03-09 16:28:21 +01:00
Wim Taymans
299902bd86 v4l2: use 0x as the prefix for hex values
fixes #5161
2026-03-09 16:28:15 +01:00
qaqland
d99a932b9c alsa-udev: support alsa.ignore-dB
Some sound cards are only adapted for Android/macOS and other
systems, without considering Linux. The hardware-reported dB
volume is incorrect (while the percentage volume is normal).
Add support for the ignore-dB option to simplify compatibility.

For example, the 3206:0798 HP SIMGOT GEW1 Sound Card reports:

numid=4,iface=MIXER,name='PCM Playback Volume'
  ; type=INTEGER,access=rw---R--,values=2,min=0,max=100,step=0
  : values=100,100
  | dBminmax-min=0.00dB,max=0.39dB

This dB value does not match actual audio perception, and the
vendor attributed this issue to non-target system compatibility.
2026-03-09 16:26:54 +01:00
Wim Taymans
b7341d0689 1.6.1 2026-03-09 10:17:11 +01:00
Wim Taymans
5f77a7ae2b pw-cat: fix encoded format playback
We keep a mapping between the sndfile formats and the format we would
like to decode them to for encoded formats. Make sure we don't mix up
the sample widths between them.

Make sure we don't send encoded formats as raw.

Debug the uncompressed format name correctly.

Fixes #5155
2026-03-06 17:09:28 +01:00
Wim Taymans
d3946c0b10 node: remove node.link-group from drivers
Sink/Source pairs should not have the same link-group otherwise the
session manager will not be able to autoconnect them with a loopback or
some other internally linked stream.
2026-03-06 11:25:24 +01:00
Wim Taymans
8daf4ba6b6 gst: fix compilation on older GStreamer
These functions are since 1.28, which is a little too new.
2026-03-04 17:57:31 +01:00
Robert Mader
86da3e1183 pipewiresrc: Use clock time difference to update last_buffer time
Setting the current clock time when resending buffers is often wrong.
Especially for pseudo-live sources - the default mode - it discards
the original buffer time, which again is used by the base-class to
adjust the timestamps further, ultimately resulting in very wrong
timestamps.

Instead, try to calculate the delta between when we originally got the
buffer and now.

(cherry picked from commit efd1526423)
2026-03-04 13:31:56 +01:00
Robert Mader
e208a465ea pipewiresrc: Take a copy instead of a reference for last_buffer
Buffer timestamps get adjusted by the base class, GstBaseSrc, even if we
take an additional ref. Arguably the base class should check if buffers
are writable (gst_buffer_make_writable()), which would trigger a buffer
copy. That is currently not the case, though, thus do so on our side.

Notes:
1. Usually a buffer copy doesn't copy the underlying memory, i.e.
copying is cheap.
2. The copy holds a reference to the copied buffer, preventing the
buffer from getting recycled as before.

(cherry picked from commit 49300d8ee0)
2026-03-04 13:31:49 +01:00
Wim Taymans
0d14f44f47 jack: never return NULL from jack_port_by_id()
JACK will never return NULL from jack_port_by_id() because the id
and the port_t are the same for JACK.

In PipeWire however we use the serial number as the id and so it can
be removed and become invalid. In this case, return a dummy port
from the client that can be used for some of the basic operations
you can do on a port_t, like get the name etc.

Also make sure that port_name() doesn't return NULL in case we use the
dummy port (which has the client set to NULL).

Fixes #3512
2026-03-03 09:44:39 +01:00
Jonas Holmberg
5cd734e8c0 module-protocol-native: Fix socket activation
Fix path comparison in is_socket_unix() and don't unset LISTEN_FDS since
the function that uses it is called more than once and it was not unset
when sd_listen_fds() was used.

Fixes #5140
2026-03-03 09:44:14 +01:00
Barnabás Pőcze
87d1206fb8 spa: libcamera: source: fix stop sequence
Currently it is possible for the request completion handler (`impl::requestComplete`)
to observe `impl::source.fd` while it is being modified in `impl::stop()`.

Fix that by closing the eventfd after the camera has been stopped.

Fixes: 3e28f3e859 ("spa: libcamera: source: rework startup sequence")
(cherry picked from commit 848ac24490)
2026-02-27 11:39:28 +01:00
28 changed files with 258 additions and 114 deletions

68
NEWS
View file

@ -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

View file

@ -44,6 +44,7 @@
<tab type="usergroup" title="PipeWire Versions">
<tab type="user" url="https://docs.pipewire.org/1.2/" title="1.2.x"/>
<tab type="user" url="https://docs.pipewire.org/1.4/" title="1.4.x"/>
<tab type="user" url="https://docs.pipewire.org/1.6/" title="1.6.x"/>
<tab type="user" url="https://docs.pipewire.org/devel/" title="Development"/>
</tab>
</navindex>

View file

@ -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

View file

@ -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),

View file

@ -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,

View file

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

View file

@ -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");
}

View file

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

View file

@ -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;
}

View file

@ -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;
}

View file

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

View file

@ -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 */

View file

@ -7,6 +7,7 @@
#include <dlfcn.h>
#include <math.h>
#include <limits.h>
#include <float.h>
#include <spa/utils/result.h>
#include <spa/utils/defs.h>
@ -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;
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)

View file

@ -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;
}

View file

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

View file

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

View file

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

View file

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

View file

@ -83,6 +83,7 @@ struct _GstPipeWireSrc {
GstClockTime max_latency;
GstBuffer *last_buffer;
GstClockTime last_buffer_clock_time;
enum spa_meta_videotransform_value transform_value;

View file

@ -264,6 +264,7 @@ 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);
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:

View file

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

View file

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

View file

@ -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) {

View file

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

View file

@ -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,13 +190,11 @@ 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)
if (len < offsetof(struct sockaddr_un, sun_path) + length + 1)
return 0;
if (memcmp(addr.sun_path, path, length) != 0)
if (memcmp(addr.sun_path, path, length + 1) != 0)
return 0;
}
}
return 1;
}

View file

@ -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 */

View file

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

View file

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