Overview
pipewire-aptx.changes
Changed
x
1
2
-------------------------------------------------------------------
3
+Mon Jul 31 19:02:32 UTC 2023 - Bjørn Lie <zaitor@opensuse.org>
4
+
5
+- Update to version 0.3.76
6
+
7
+-------------------------------------------------------------------
8
Wed Jul 26 14:05:18 UTC 2023 - Bjørn Lie <zaitor@opensuse.org>
9
10
- Update to version 0.3.74
11
pipewire-aptx.spec
Changed
10
1
2
%define soversion 0_2
3
4
Name: pipewire-aptx
5
-Version: 0.3.74
6
+Version: 0.3.76
7
Release: 0
8
Summary: PipeWire Bluetooth aptX codec plugin
9
License: MIT
10
pipewire-0.3.74.tar.gz/NEWS -> pipewire-0.3.76.tar.gz/NEWS
Changed
106
1
2
+# PipeWire 0.3.76 (2023-07-28)
3
+
4
+This is a quick bugfix release that is API and ABI compatible with previous
5
+0.3.x releases.
6
+
7
+## Highlights
8
+ - Fix a regression that would cause the MPV pipewire backend to fail because
9
+ of a spurious thread-loop signal.
10
+ - Fix a crash when DBus is not found.
11
+ - ALSA hires timestamps are now disabled by default.
12
+ - Some more fixes and improvements.
13
+
14
+## PipeWire
15
+ - A new option was added to pw-thread-loop to signal when the thread starts.
16
+ This is only used in module-rt to avoid regressions in mpv. (#3374)
17
+ - Fix a compilation problem.
18
+ - Stream flags now only set the properties when not already set. This fixes
19
+ a regression with node autoconnect. (#3382)
20
+
21
+## Tools
22
+ - pw-cat will now stop when the stream is disconnected. (#2731)
23
+ - Improve the pw-cat man page, mention that stdin/stdout handling is only
24
+ on raw data.
25
+
26
+## modules
27
+ - module-rt will now not crash when dbus is not available but error out as
28
+ before.
29
+ - A new VBAN (vb-audio.com) sender and receiver was added. (#3380)
30
+
31
+## SPA
32
+ - Add an option in audioconvert to disable volume updates. (#3361)
33
+ - ALSA hires timestamps are disabled by default because many drivers seem to
34
+ give wrong timestamps and cause extra delay.
35
+
36
+## bluetooth
37
+ - LE Audio support is now enabled by default when liblc3 is available now that
38
+ bluez has support for detecting the hardware features.
39
+
40
+Older versions:
41
+
42
+
43
+# PipeWire 0.3.75 (2023-07-21)
44
+
45
+This is a bugfix release that is API and ABI compatible with previous
46
+0.3.x releases.
47
+
48
+## Highlights
49
+ - Link permissions between nodes are now enforced. This avoids potential portal
50
+ managed screencast nodes to link to the camera even though it was not assigned
51
+ permissions to do so by the session manager.
52
+ - Libcamera and v4l2 devices now have properties so that duplicates can be
53
+ filtered out by the session manager.
54
+ - A bug with draining was fixed where a buffer would be marked EMPTY and would not
55
+ play when it contained drained samples. (#3365)
56
+ - Many fixes and improvements.
57
+
58
+
59
+## PipeWire
60
+ - Permissions for links between nodes are now enforced. The link will now check
61
+ that the owner clients of the nodes can see eachother before allowing the link.
62
+ This avoids screensharing clients to accidentally being linked to the camera
63
+ nodes by the session manager. A side effect is that patchbay tools will no longer
64
+ be able to link portal managed screencast nodes to the camera, for this we need
65
+ a new permission for those patchbay clients. (wireplumber#218)
66
+ - The stream.rules/filter.rules are now evaluated when connecting the stream/filter
67
+ so that more properties can be matched. (#3355)
68
+ - Move some internal events from the context to the nodes to better handle per-node
69
+ threads in the future.
70
+ - The thread-loop will now signal when the thread is started.
71
+
72
+## modules
73
+ - A timestamp workaround in module-raop was reverted because it does not work
74
+ in all cases. Instead latency was increased to 1.5 seconds, which also makes
75
+ the problematic device in question work. (#3247)
76
+ - The profiler module was reworked a bit to use the new node realtime events. It
77
+ should now also handle dynamically added and removed drivers.
78
+ - The module-rt now does the rtkit calls from a separate thread so that it does
79
+ not block the main thread. This could cause deadlocks during startup in some
80
+ cases. (#3357)
81
+
82
+## SPA
83
+ - Atomic operation macros were move from internal pipewire API to public API.
84
+ - The video-info structure now has a new SPA_VIDEO_FLAG_MODIFIER_FIXATION_REQUIRED
85
+ flag to instruct the application to fixate the modifiers. This simplifies some
86
+ logic in applications a lot.
87
+ - The libcamera and v4l2 nodes now have properties to enumerate the device id
88
+ they are using. This can be used to match v4l2 devices and libcamera devices
89
+ and filter out duplicates.
90
+ - A bug with draining was fixed where a buffer would be marked EMPTY and would not
91
+ play when it contained drained samples. (#3365)
92
+
93
# PipeWire 0.3.74 (2023-07-12)
94
95
This is a quick bugfix release that is API and ABI compatible with previous
96
97
- Merge scope based cleanup macros.
98
- Add ratelimit function.
99
100
-Older versions:
101
-
102
-
103
# PipeWire 0.3.73 (2023-07-06)
104
105
This is a bugfix release that is API and ABI compatible with previous
106
pipewire-0.3.74.tar.gz/doc/pipewire-modules.dox -> pipewire-0.3.76.tar.gz/doc/pipewire-modules.dox
Changed
10
1
2
- \subpage page_module_rtp_session
3
- \subpage page_module_rt
4
- \subpage page_module_session_manager
5
+- \subpage page_module_vban_recv
6
+- \subpage page_module_vban_send
7
- \subpage page_module_x11_bell
8
- \subpage page_module_zeroconf_discover
9
10
pipewire-0.3.74.tar.gz/man/pw-cat.1.rst.in -> pipewire-0.3.76.tar.gz/man/pw-cat.1.rst.in
Changed
21
1
2
**pw-cat** is a simple tool for playing back or
3
capturing raw or encoded media files on a PipeWire
4
server. It understands all audio file formats supported by
5
-``libsndfile`` for PCM capture and playback.
6
+``libsndfile`` for PCM capture and playback. When capturing PCM, the filename
7
+extension is used to guess the file format with the WAV file format as
8
+the default.
9
10
It understands standard MIDI files for playback and recording. This tool
11
will not render MIDI files, it will simply make the MIDI events available
12
13
with native DSD capable hardware and will produce an error when no such
14
hardware was found.
15
16
-When the *FILE* is - input and output will be from STDIN and
17
+When the *FILE* is - input and output will be raw data from STDIN and
18
STDOUT respectively.
19
20
OPTIONS
21
pipewire-0.3.74.tar.gz/meson.build -> pipewire-0.3.76.tar.gz/meson.build
Changed
8
1
2
project('pipewire', 'c' ,
3
- version : '0.3.74',
4
+ version : '0.3.76',
5
license : 'MIT', 'LGPL-2.1-or-later', 'GPL-2.0-only' ,
6
meson_version : '>= 0.61.1',
7
default_options : 'warning_level=3',
8
pipewire-0.3.74.tar.gz/meson_options.txt -> pipewire-0.3.76.tar.gz/meson_options.txt
Changed
10
1
2
option('bluez5-codec-lc3',
3
description: 'Enable LC3 open source codec implementation',
4
type: 'feature',
5
- value: 'disabled')
6
+ value: 'auto')
7
option('control',
8
description: 'Enable control spa plugin integration',
9
type: 'feature',
10
pipewire-0.3.74.tar.gz/pipewire-alsa/alsa-plugins/pcm_pipewire.c -> pipewire-0.3.76.tar.gz/pipewire-alsa/alsa-plugins/pcm_pipewire.c
Changed
61
1
2
#include <spa/param/audio/format-utils.h>
3
#include <spa/debug/types.h>
4
#include <spa/param/props.h>
5
+#include <spa/utils/atomic.h>
6
#include <spa/utils/result.h>
7
#include <spa/utils/string.h>
8
9
#include <pipewire/pipewire.h>
10
11
-#define ATOMIC_INC(s) __atomic_add_fetch(&(s), 1, __ATOMIC_SEQ_CST)
12
-#define ATOMIC_LOAD(s) __atomic_load_n(&(s), __ATOMIC_SEQ_CST)
13
-
14
-#define SEQ_WRITE(s) ATOMIC_INC(s)
15
-#define SEQ_WRITE_SUCCESS(s1,s2) ((s1) + 1 == (s2) && ((s2) & 1) == 0)
16
-
17
-#define SEQ_READ(s) ATOMIC_LOAD(s)
18
-#define SEQ_READ_SUCCESS(s1,s2) ((s1) == (s2) && ((s2) & 1) == 0)
19
-
20
PW_LOG_TOPIC_STATIC(alsa_log_topic, "alsa.pcm");
21
#define PW_LOG_TOPIC_DEFAULT alsa_log_topic
22
23
24
int64_t diff;
25
26
do {
27
- seq1 = SEQ_READ(pw->seq);
28
+ seq1 = SPA_SEQ_READ(pw->seq);
29
30
delay = pw->delay + pw->transfered;
31
now = pw->now;
32
33
else
34
avail = snd_pcm_ioplug_avail(io, pw->hw_ptr, io->appl_ptr);
35
36
- seq2 = SEQ_READ(pw->seq);
37
- } while (!SEQ_READ_SUCCESS(seq1, seq2));
38
+ seq2 = SPA_SEQ_READ(pw->seq);
39
+ } while (!SPA_SEQ_READ_SUCCESS(seq1, seq2));
40
41
if (now != 0 && (io->state == SND_PCM_STATE_RUNNING ||
42
io->state == SND_PCM_STATE_DRAINING)) {
43
44
45
want = b->requested ? b->requested : hw_avail;
46
47
- SEQ_WRITE(pw->seq);
48
+ SPA_SEQ_WRITE(pw->seq);
49
50
if (pw->now != pwt.now) {
51
pw->transfered = pw->buffered;
52
53
pw->buffered = (want == 0 || pw->transfered < want) ? 0 : (pw->transfered % want);
54
55
pw->now = pwt.now;
56
- SEQ_WRITE(pw->seq);
57
+ SPA_SEQ_WRITE(pw->seq);
58
59
pw_log_trace("%p: avail-before:%lu avail:%lu want:%lu xfer:%lu hw:%lu appl:%lu",
60
pw, before, hw_avail, want, xfer, pw->hw_ptr, io->appl_ptr);
61
pipewire-0.3.74.tar.gz/pipewire-jack/src/pipewire-jack.c -> pipewire-0.3.76.tar.gz/pipewire-jack/src/pipewire-jack.c
Changed
88
1
2
pw_log_trace_fp("%p: link %p %p %d/%d", c, l, state,
3
state->pending, state->required);
4
5
- if (pw_node_activation_state_dec(state, 1)) {
6
+ if (pw_node_activation_state_dec(state)) {
7
l->activation->status = PW_NODE_ACTIVATION_TRIGGERED;
8
l->activation->signal_time = nsec;
9
10
11
pw_log_debug("%p: activation %p", c, a);
12
13
/* was ok */
14
- owner = ATOMIC_LOAD(a->segment_owner0);
15
+ owner = SPA_ATOMIC_LOAD(a->segment_owner0);
16
if (owner == c->node_id)
17
return 0;
18
19
/* try to become owner */
20
if (c->timeowner_conditional) {
21
- if (!ATOMIC_CAS(a->segment_owner0, 0, c->node_id)) {
22
+ if (!SPA_ATOMIC_CAS(a->segment_owner0, 0, c->node_id)) {
23
pw_log_debug("%p: owner:%u id:%u", c, owner, c->node_id);
24
return -EBUSY;
25
}
26
} else {
27
- ATOMIC_STORE(a->segment_owner0, c->node_id);
28
+ SPA_ATOMIC_STORE(a->segment_owner0, c->node_id);
29
}
30
31
pw_log_debug("%p: timebase installed for id:%u", c, c->node_id);
32
33
uint32_t n_support;
34
const char *str;
35
struct spa_cpu *cpu_iface;
36
+ const struct pw_properties *props;
37
va_list ap;
38
39
if (getenv("PIPEWIRE_NOJACK") != NULL ||
40
41
client->notify_buffer = calloc(1, NOTIFY_BUFFER_SIZE + sizeof(struct notify));
42
spa_ringbuffer_init(&client->notify_ring);
43
44
- client->allow_mlock = client->context.context->settings.mem_allow_mlock;
45
- client->warn_mlock = client->context.context->settings.mem_warn_mlock;
46
-
47
pw_context_conf_update_props(client->context.context,
48
"jack.properties", client->props);
49
50
+ props = pw_context_get_properties(client->context.context);
51
+
52
+ client->allow_mlock = pw_properties_get_bool(props, "mem.allow-mlock", true);
53
+ client->warn_mlock = pw_properties_get_bool(props, "mem.warn-mlock", false);
54
+
55
pw_context_conf_section_match_rules(client->context.context, "jack.rules",
56
- &client->context.context->properties->dict, execute_match, client);
57
+ &props->dict, execute_match, client);
58
59
support = pw_context_get_support(client->context.context, &n_support);
60
61
62
if ((a = c->driver_activation) == NULL)
63
return -EIO;
64
65
- if (!ATOMIC_CAS(a->segment_owner0, c->node_id, 0))
66
+ if (!SPA_ATOMIC_CAS(a->segment_owner0, c->node_id, 0))
67
return -EINVAL;
68
69
c->timebase_callback = NULL;
70
71
na->reposition.duration = 0;
72
na->reposition.position = pos->frame;
73
na->reposition.rate = 1.0;
74
- ATOMIC_STORE(a->reposition_owner, c->node_id);
75
+ SPA_ATOMIC_STORE(a->reposition_owner, c->node_id);
76
77
return 0;
78
}
79
80
struct pw_node_activation *a = c->rt.driver_activation;
81
if (!a)
82
return;
83
- ATOMIC_STORE(a->command, command);
84
+ SPA_ATOMIC_STORE(a->command, command);
85
}
86
87
SPA_EXPORT
88
pipewire-0.3.74.tar.gz/pipewire-v4l2/src/pipewire-v4l2.c -> pipewire-0.3.76.tar.gz/pipewire-v4l2/src/pipewire-v4l2.c
Changed
54
1
2
3
#include "pipewire-v4l2.h"
4
5
+#include <spa/utils/atomic.h>
6
#include <spa/utils/result.h>
7
#include <spa/pod/iter.h>
8
#include <spa/pod/parser.h>
9
10
}
11
}
12
}
13
-#define ATOMIC_DEC(s) __atomic_sub_fetch(&(s), 1, __ATOMIC_SEQ_CST)
14
-#define ATOMIC_INC(s) __atomic_add_fetch(&(s), 1, __ATOMIC_SEQ_CST)
15
16
static struct file *make_file(void)
17
{
18
19
static void unref_file(struct file *file)
20
{
21
pw_log_debug("file:%d ref:%d", file->fd, file->ref);
22
- if (ATOMIC_DEC(file->ref) <= 0)
23
+ if (SPA_ATOMIC_DEC(file->ref) <= 0)
24
free_file(file);
25
}
26
27
28
map->fd = fd;
29
map->flags = flags;
30
map->file = file;
31
- ATOMIC_INC(file->ref);
32
+ SPA_ATOMIC_INC(file->ref);
33
pw_log_debug("fd:%d -> file:%d ref:%d", fd, file->fd, file->ref);
34
}
35
pthread_mutex_unlock(&globals.lock);
36
37
struct fd_map *map;
38
pw_array_for_each(map, &globals.fd_maps) {
39
if (map->fd == fd) {
40
- ATOMIC_INC(map->file->ref);
41
+ SPA_ATOMIC_INC(map->file->ref);
42
pw_log_debug("fd:%d find:%d ref:%d", map->fd, fd, map->file->ref);
43
return map;
44
}
45
46
if (tmp->file->dev_id == dev) {
47
if (tmp->file->closed)
48
tmp->file->fd = tmp->fd;
49
- ATOMIC_INC(tmp->file->ref);
50
+ SPA_ATOMIC_INC(tmp->file->ref);
51
map = tmp;
52
pw_log_debug("dev:%d find:%d ref:%d",
53
tmp->file->dev_id, dev, tmp->file->ref);
54
pipewire-0.3.74.tar.gz/spa/include/spa/graph/graph.h -> pipewire-0.3.76.tar.gz/spa/include/spa/graph/graph.h
Changed
27
1
2
* \{
3
*/
4
5
+#include <spa/utils/atomic.h>
6
#include <spa/utils/defs.h>
7
#include <spa/utils/list.h>
8
#include <spa/utils/hook.h>
9
10
11
#define spa_graph_link_signal(l) ((l)->signal((l)->signal_data))
12
13
-#define spa_graph_state_dec(s,c) (__atomic_sub_fetch(&(s)->pending, c, __ATOMIC_SEQ_CST) == 0)
14
+#define spa_graph_state_dec(s) (SPA_ATOMIC_DEC(s->pending) == 0)
15
16
static inline int spa_graph_link_trigger(struct spa_graph_link *link)
17
{
18
19
spa_debug("link %p: state %p: pending %d/%d", link, state,
20
state->pending, state->required);
21
22
- if (spa_graph_state_dec(state, 1))
23
+ if (spa_graph_state_dec(state))
24
spa_graph_link_signal(link);
25
26
return state->status;
27
pipewire-0.3.74.tar.gz/spa/include/spa/monitor/device.h -> pipewire-0.3.76.tar.gz/spa/include/spa/monitor/device.h
Changed
11
1
2
#define SPA_KEY_DEVICE_PROFILE_SET "device.profile-set" /**< profile set for the device */
3
#define SPA_KEY_DEVICE_STRING "device.string" /**< device string in the underlying
4
* layer's format. E.g. "surround51:0" */
5
-
6
+#define SPA_KEY_DEVICE_DEVIDS "device.devids" /**< space separated list of device ids (dev_t) of the
7
+ * underlying device(s) if applicable */
8
/**
9
* \}
10
*/
11
pipewire-0.3.74.tar.gz/spa/include/spa/param/props.h -> pipewire-0.3.76.tar.gz/spa/include/spa/param/props.h
Changed
36
1
2
SPA_PROP_START_Audio = 0x10000, /**< audio related properties */
3
SPA_PROP_waveType,
4
SPA_PROP_frequency,
5
- SPA_PROP_volume, /**< a volume (Float), 0.0 silence, 1.0 normal */
6
+ SPA_PROP_volume, /**< a volume (Float), 0.0 silence, 1.0 no attenutation */
7
SPA_PROP_mute, /**< mute (Bool) */
8
SPA_PROP_patternType,
9
SPA_PROP_ditherType,
10
SPA_PROP_truncate,
11
- SPA_PROP_channelVolumes, /**< a volume array, one volume per
12
- * channel (Array of Float) */
13
+ SPA_PROP_channelVolumes, /**< a volume array, one volume per channel
14
+ * (Array of Float). 0.0 is silence, 1.0 is
15
+ * without attenuation. This is the effective volume
16
+ * that is applied. It can result in a hardware volume
17
+ * and software volume (see softVolumes) */
18
SPA_PROP_volumeBase, /**< a volume base (Float) */
19
SPA_PROP_volumeStep, /**< a volume step (Float) */
20
SPA_PROP_channelMap, /**< a channelmap array
21
22
SPA_PROP_monitorVolumes, /**< a volume array, one volume per
23
* channel (Array of Float) */
24
SPA_PROP_latencyOffsetNsec, /**< delay adjustment */
25
- SPA_PROP_softMute, /**< mute (Bool) */
26
- SPA_PROP_softVolumes, /**< a volume array, one volume per
27
- * channel (Array of Float) */
28
+ SPA_PROP_softMute, /**< mute (Bool) applied in software */
29
+ SPA_PROP_softVolumes, /**< a volume array, one volume per channel
30
+ * (Array of Float). 0.0 is silence, 1.0 is without
31
+ * attenuation. This is the volume applied in software,
32
+ * there might be a part applied in hardware. */
33
34
SPA_PROP_iec958Codecs, /**< enabled IEC958 (S/PDIF) codecs,
35
* (Array (Id enum spa_audio_iec958_codec) */
36
pipewire-0.3.74.tar.gz/spa/include/spa/param/video/dsp-utils.h -> pipewire-0.3.76.tar.gz/spa/include/spa/param/video/dsp-utils.h
Changed
14
1
2
struct spa_video_info_dsp *info)
3
{
4
info->flags = SPA_VIDEO_FLAG_NONE;
5
- if (spa_pod_find_prop (format, NULL, SPA_FORMAT_VIDEO_modifier)) {
6
+ const struct spa_pod_prop *mod_prop;
7
+ if ((mod_prop = spa_pod_find_prop (format, NULL, SPA_FORMAT_VIDEO_modifier)) != NULL) {
8
info->flags |= SPA_VIDEO_FLAG_MODIFIER;
9
+ if ((mod_prop->flags & SPA_POD_PROP_FLAG_DONT_FIXATE) == SPA_POD_PROP_FLAG_DONT_FIXATE)
10
+ info->flags |= SPA_VIDEO_FLAG_MODIFIER_FIXATION_REQUIRED;
11
}
12
13
return spa_pod_parse_object(format,
14
pipewire-0.3.74.tar.gz/spa/include/spa/param/video/raw-utils.h -> pipewire-0.3.76.tar.gz/spa/include/spa/param/video/raw-utils.h
Changed
14
1
2
struct spa_video_info_raw *info)
3
{
4
info->flags = SPA_VIDEO_FLAG_NONE;
5
- if (spa_pod_find_prop (format, NULL, SPA_FORMAT_VIDEO_modifier)) {
6
+ const struct spa_pod_prop *mod_prop;
7
+ if ((mod_prop = spa_pod_find_prop (format, NULL, SPA_FORMAT_VIDEO_modifier)) != NULL) {
8
info->flags |= SPA_VIDEO_FLAG_MODIFIER;
9
+ if ((mod_prop->flags & SPA_POD_PROP_FLAG_DONT_FIXATE) == SPA_POD_PROP_FLAG_DONT_FIXATE)
10
+ info->flags |= SPA_VIDEO_FLAG_MODIFIER_FIXATION_REQUIRED;
11
}
12
13
return spa_pod_parse_object(format,
14
pipewire-0.3.74.tar.gz/spa/include/spa/param/video/raw.h -> pipewire-0.3.76.tar.gz/spa/include/spa/param/video/raw.h
Changed
19
1
2
* Extra video flags
3
*/
4
enum spa_video_flags {
5
- SPA_VIDEO_FLAG_NONE = 0, /**< no flags */
6
- SPA_VIDEO_FLAG_VARIABLE_FPS = (1 << 0), /**< a variable fps is selected, fps_n and fps_d
7
- * denote the maximum fps of the video */
8
- SPA_VIDEO_FLAG_PREMULTIPLIED_ALPHA = (1 << 1), /**< Each color has been scaled by the alpha value. */
9
- SPA_VIDEO_FLAG_MODIFIER = (1 << 2), /**< use the format modifier */
10
+ SPA_VIDEO_FLAG_NONE = 0, /**< no flags */
11
+ SPA_VIDEO_FLAG_VARIABLE_FPS = (1 << 0), /**< a variable fps is selected, fps_n and fps_d
12
+ * denote the maximum fps of the video */
13
+ SPA_VIDEO_FLAG_PREMULTIPLIED_ALPHA = (1 << 1), /**< Each color has been scaled by the alpha value. */
14
+ SPA_VIDEO_FLAG_MODIFIER = (1 << 2), /**< use the format modifier */
15
+ SPA_VIDEO_FLAG_MODIFIER_FIXATION_REQUIRED = (1 << 3), /**< format modifier was not fixated yet */
16
};
17
18
/**
19
pipewire-0.3.76.tar.gz/spa/include/spa/utils/atomic.h
Added
37
1
2
+/* Atomic operations */
3
+/* SPDX-FileCopyrightText: Copyright © 2023 Wim Taymans */
4
+/* SPDX-License-Identifier: MIT */
5
+
6
+#ifndef SPA_ATOMIC_H
7
+#define SPA_ATOMIC_H
8
+
9
+#ifdef __cplusplus
10
+extern "C" {
11
+#endif
12
+
13
+#define SPA_ATOMIC_CAS(v,ov,nv) \
14
+({ \
15
+ __typeof__(v) __ov = (ov); \
16
+ __atomic_compare_exchange_n(&(v), &__ov, (nv), \
17
+ 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); \
18
+})
19
+
20
+#define SPA_ATOMIC_DEC(s) __atomic_sub_fetch(&(s), 1, __ATOMIC_SEQ_CST)
21
+#define SPA_ATOMIC_INC(s) __atomic_add_fetch(&(s), 1, __ATOMIC_SEQ_CST)
22
+#define SPA_ATOMIC_LOAD(s) __atomic_load_n(&(s), __ATOMIC_SEQ_CST)
23
+#define SPA_ATOMIC_STORE(s,v) __atomic_store_n(&(s), (v), __ATOMIC_SEQ_CST)
24
+#define SPA_ATOMIC_XCHG(s,v) __atomic_exchange_n(&(s), (v), __ATOMIC_SEQ_CST)
25
+
26
+#define SPA_SEQ_WRITE(s) SPA_ATOMIC_INC(s)
27
+#define SPA_SEQ_WRITE_SUCCESS(s1,s2) ((s1) + 1 == (s2) && ((s2) & 1) == 0)
28
+
29
+#define SPA_SEQ_READ(s) SPA_ATOMIC_LOAD(s)
30
+#define SPA_SEQ_READ_SUCCESS(s1,s2) ((s1) == (s2) && ((s2) & 1) == 0)
31
+
32
+#ifdef __cplusplus
33
+} /* extern "C" */
34
+#endif
35
+
36
+#endif /* SPA_ATOMIC_H */
37
pipewire-0.3.74.tar.gz/spa/meson.build -> pipewire-0.3.76.tar.gz/spa/meson.build
Changed
17
1
2
summary({'Opus': opus_dep.found()}, bool_yn: true, section: 'Bluetooth audio codecs')
3
lc3_dep = dependency('lc3', required : get_option('bluez5-codec-lc3'))
4
summary({'LC3': lc3_dep.found()}, bool_yn: true, section: 'Bluetooth audio codecs')
5
- cdata.set('HAVE_BLUETOOTH_BAP', get_option('bluez5-codec-lc3').allowed() and lc3_dep.found())
6
if get_option('bluez5-backend-hsp-native').allowed() or get_option('bluez5-backend-hfp-native').allowed()
7
mm_dep = dependency('ModemManager', version : '>= 1.10.0', required : get_option('bluez5-backend-native-mm'))
8
summary({'ModemManager': mm_dep.found()}, bool_yn: true, section: 'Bluetooth backends')
9
10
11
libcamera_dep = dependency('libcamera', required: get_option('libcamera'))
12
summary({'libcamera': libcamera_dep.found()}, bool_yn: true, section: 'Backend')
13
+ cdata.set('HAVE_LIBCAMERA_SYSTEM_DEVICES', libcamera_dep.version().version_compare('>= 0.1.0'))
14
15
compress_offload_option = get_option('compress-offload')
16
summary({'Compress-Offload': compress_offload_option.allowed()}, bool_yn: true, section: 'Backend')
17
pipewire-0.3.74.tar.gz/spa/plugins/alsa/alsa-pcm.c -> pipewire-0.3.76.tar.gz/spa/plugins/alsa/alsa-pcm.c
Changed
10
1
2
snd_config_update_free_global();
3
4
state->multi_rate = true;
5
- state->htimestamp = true;
6
+ state->htimestamp = false;
7
for (i = 0; info && i < info->n_items; i++) {
8
const char *k = info->itemsi.key;
9
const char *s = info->itemsi.value;
10
pipewire-0.3.74.tar.gz/spa/plugins/audioconvert/audioconvert.c -> pipewire-0.3.76.tar.gz/spa/plugins/audioconvert/audioconvert.c
Changed
115
1
2
unsigned int resample_quality;
3
double rate;
4
char wav_path512;
5
+ unsigned int lock_volumes:1;
6
};
7
8
static void props_reset(struct props *props)
9
10
props->resample_quality = RESAMPLE_DEFAULT_QUALITY;
11
props->rate = 1.0;
12
spa_zero(props->wav_path);
13
+ props->lock_volumes = false;
14
}
15
16
struct buffer {
17
18
SPA_PROP_INFO_type, SPA_POD_String(p->wav_path),
19
SPA_PROP_INFO_params, SPA_POD_Bool(true));
20
break;
21
+ case 27:
22
+ param = spa_pod_builder_add_object(&b,
23
+ SPA_TYPE_OBJECT_PropInfo, id,
24
+ SPA_PROP_INFO_name, SPA_POD_String("channelmix.lock-volumes"),
25
+ SPA_PROP_INFO_description, SPA_POD_String("Disable volume updates"),
26
+ SPA_PROP_INFO_type, SPA_POD_CHOICE_Bool(p->lock_volumes),
27
+ SPA_PROP_INFO_params, SPA_POD_Bool(true));
28
+ break;
29
default:
30
return 0;
31
}
32
33
spa_pod_builder_string(&b, dither_method_infothis->dir1.conv.method.label);
34
spa_pod_builder_string(&b, "debug.wav-path");
35
spa_pod_builder_string(&b, p->wav_path);
36
+ spa_pod_builder_string(&b, "channelmix.lock-volumes");
37
+ spa_pod_builder_bool(&b, p->lock_volumes);
38
spa_pod_builder_pop(&b, &f1);
39
param = spa_pod_builder_pop(&b, &f0);
40
break;
41
42
spa_scnprintf(this->props.wav_path,
43
sizeof(this->props.wav_path), "%s", s ? s : "");
44
}
45
+ else if (spa_streq(k, "channelmix.lock-volumes"))
46
+ this->props.lock_volumes = spa_atob(s);
47
else
48
return 0;
49
return 1;
50
51
case SPA_PROP_volume:
52
p->prev_volume = p->volume;
53
54
- if (spa_pod_get_float(&prop->value, &p->volume) == 0) {
55
+ if (!p->lock_volumes &&
56
+ spa_pod_get_float(&prop->value, &p->volume) == 0) {
57
spa_log_debug(this->log, "%p new volume %f", this, p->volume);
58
changed++;
59
}
60
break;
61
case SPA_PROP_mute:
62
- if (spa_pod_get_bool(&prop->value, &p->channel.mute) == 0) {
63
+ if (!p->lock_volumes &&
64
+ spa_pod_get_bool(&prop->value, &p->channel.mute) == 0) {
65
have_channel_volume = true;
66
changed++;
67
}
68
69
}
70
break;
71
case SPA_PROP_channelVolumes:
72
- if ((n = spa_pod_copy_array(&prop->value, SPA_TYPE_Float,
73
+ if (!p->lock_volumes &&
74
+ (n = spa_pod_copy_array(&prop->value, SPA_TYPE_Float,
75
p->channel.volumes, SPA_AUDIO_MAX_CHANNELS)) > 0) {
76
have_channel_volume = true;
77
p->channel.n_volumes = n;
78
79
}
80
break;
81
case SPA_PROP_softMute:
82
- if (spa_pod_get_bool(&prop->value, &p->soft.mute) == 0) {
83
+ if (!p->lock_volumes &&
84
+ spa_pod_get_bool(&prop->value, &p->soft.mute) == 0) {
85
have_soft_volume = true;
86
changed++;
87
}
88
break;
89
case SPA_PROP_softVolumes:
90
- if ((n = spa_pod_copy_array(&prop->value, SPA_TYPE_Float,
91
+ if (!p->lock_volumes &&
92
+ (n = spa_pod_copy_array(&prop->value, SPA_TYPE_Float,
93
p->soft.volumes, SPA_AUDIO_MAX_CHANNELS)) > 0) {
94
have_soft_volume = true;
95
p->soft.n_volumes = n;
96
97
set_volume(this);
98
}
99
100
- if (vol_ramp_params_changed) {
101
+ if (!p->lock_volumes && vol_ramp_params_changed) {
102
void *sequence = NULL;
103
if (p->volume == p->prev_volume)
104
spa_log_error(this->log, "no change in volume, cannot ramp volume");
105
106
struct dir *dir;
107
int tmp = 0, res = 0;
108
bool in_passthrough, mix_passthrough, resample_passthrough, out_passthrough;
109
- bool in_avail = false, flush_in = false, flush_out = false, draining = false, in_empty = true;
110
+ bool in_avail = false, flush_in = false, flush_out = false;
111
+ bool draining = false, in_empty = this->out_offset == 0;
112
struct spa_io_buffers *io, *ctrlio = NULL;
113
const struct spa_pod_sequence *ctrl = NULL;
114
115
pipewire-0.3.74.tar.gz/spa/plugins/audiomixer/mixer-dsp.c -> pipewire-0.3.76.tar.gz/spa/plugins/audiomixer/mixer-dsp.c
Changed
14
1
2
size = SPA_MIN(bd->maxsize - offs, bd->chunk->size);
3
maxsize = SPA_MIN(maxsize, size);
4
5
- spa_log_trace_fp(this->log, "%p: mix input %d %p->%p %d %d %d:%d/%d", this,
6
+ spa_log_trace_fp(this->log, "%p: mix input %d %p->%p %d %d %d:%d/%d %u", this,
7
i, inio, outio, inio->status, inio->buffer_id,
8
- offs, size, (int)sizeof(float));
9
+ offs, size, (int)sizeof(float),
10
+ bd->chunk->flags);
11
12
if (!SPA_FLAG_IS_SET(bd->chunk->flags, SPA_CHUNK_FLAG_EMPTY)) {
13
datasn_buffers = SPA_PTROFF(bd->data, offs, void);
14
pipewire-0.3.74.tar.gz/spa/plugins/bluez5/backend-hsphfpd.c -> pipewire-0.3.76.tar.gz/spa/plugins/bluez5/backend-hsphfpd.c
Changed
87
1
2
#include <spa/param/audio/raw.h>
3
4
#include "defs.h"
5
+#include "dbus-helpers.h"
6
7
static struct spa_log_topic log_topic = SPA_LOG_TOPIC(0, "spa.bluez5.hsphfpd");
8
#undef SPA_LOG_TOPIC_DEFAULT
9
10
{
11
const char *interface_name = HSPHFPD_AUDIO_AGENT_INTERFACE;
12
DBusMessageIter object, array, entry, dict, codec, data;
13
- char *str = "AgentCodec";
14
15
dbus_message_iter_open_container(iter, DBUS_TYPE_DICT_ENTRY, NULL, &object);
16
dbus_message_iter_append_basic(&object, DBUS_TYPE_OBJECT_PATH, &endpoint);
17
18
dbus_message_iter_open_container(&entry, DBUS_TYPE_ARRAY, "{sv}", &dict);
19
20
dbus_message_iter_open_container(&dict, DBUS_TYPE_DICT_ENTRY, NULL, &codec);
21
- dbus_message_iter_append_basic(&codec, DBUS_TYPE_STRING, &str);
22
+ dbus_message_iter_append_basic(&codec, DBUS_TYPE_STRING, &(const char *) { "AgentCodec" });
23
dbus_message_iter_open_container(&codec, DBUS_TYPE_VARIANT, "s", &data);
24
dbus_message_iter_append_basic(&data, DBUS_TYPE_STRING, &agent_codec);
25
dbus_message_iter_close_container(&codec, &data);
26
27
28
backend->acquire_in_progress = false;
29
30
- r = dbus_pending_call_steal_reply(pending);
31
+ r = steal_reply_and_unref(&pending);
32
if (r == NULL)
33
return;
34
35
36
37
finish:
38
dbus_message_unref(r);
39
- dbus_pending_call_unref(pending);
40
41
if (ret < 0)
42
spa_bt_transport_set_state(transport, SPA_BT_TRANSPORT_STATE_ERROR);
43
44
const char *air_codec = HSPHFP_AIR_CODEC_CVSD;
45
const char *agent_codec = HSPHFP_AGENT_CODEC_PCM;
46
DBusPendingCall *call;
47
- DBusError err;
48
49
spa_log_debug(backend->log, "transport %p: Acquire %s",
50
transport, transport->path);
51
52
return -ENOMEM;
53
dbus_message_append_args(m, DBUS_TYPE_STRING, &air_codec, DBUS_TYPE_STRING, &agent_codec, DBUS_TYPE_INVALID);
54
55
- dbus_error_init(&err);
56
-
57
dbus_connection_send_with_reply(backend->conn, m, &call, -1);
58
dbus_pending_call_set_notify(call, hsphfpd_audio_acquire_reply, transport, NULL);
59
dbus_message_unref(m);
60
61
DBusMessage *r;
62
DBusMessageIter i, array_i;
63
64
- r = dbus_pending_call_steal_reply(pending);
65
+ r = steal_reply_and_unref(&pending);
66
if (r == NULL)
67
return;
68
69
70
71
finish:
72
dbus_message_unref(r);
73
- dbus_pending_call_unref(pending);
74
}
75
76
static int backend_hsphfpd_register(void *data)
77
78
{
79
const char *sender;
80
struct impl *backend = user_data;
81
- DBusError err;
82
-
83
- dbus_error_init(&err);
84
85
sender = dbus_message_get_sender(m);
86
87
pipewire-0.3.74.tar.gz/spa/plugins/bluez5/backend-native.c -> pipewire-0.3.76.tar.gz/spa/plugins/bluez5/backend-native.c
Changed
72
1
2
#include <libusb.h>
3
#endif
4
5
+#include "dbus-helpers.h"
6
#include "modemmanager.h"
7
#include "upower.h"
8
9
10
return true;
11
}
12
} else if (spa_strstartswith(buf, "AT+VTS=")) {
13
- char *dtmf;
14
+ char dtmf2;
15
enum cmee_error error;
16
17
- dtmf = calloc(1, 2);
18
if (sscanf(buf, "AT+VTS=%1s", dtmf) != 1) {
19
spa_log_debug(backend->log, "Failed to parse AT+VTS: \"%s\"", buf);
20
rfcomm_send_error(rfcomm, CMEE_AG_FAILURE);
21
22
{
23
struct impl *backend = userdata;
24
DBusMessage *r;
25
- DBusMessageIter it5;
26
+ DBusMessageIter it;
27
const char *handler, *path;
28
enum spa_bt_profile profile;
29
struct rfcomm *rfcomm;
30
31
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
32
}
33
34
- dbus_message_iter_init(m, &it0);
35
- dbus_message_iter_get_basic(&it0, &path);
36
+ dbus_message_iter_init(m, &it);
37
+ dbus_message_iter_get_basic(&it, &path);
38
39
d = spa_bt_device_find(backend->monitor, path);
40
if (d == NULL || d->adapter == NULL) {
41
42
}
43
spa_bt_device_add_profile(d, profile);
44
45
- dbus_message_iter_next(&it0);
46
- dbus_message_iter_get_basic(&it0, &fd);
47
+ dbus_message_iter_next(&it);
48
+ dbus_message_iter_get_basic(&it, &fd);
49
50
spa_log_debug(backend->log, "NewConnection path=%s, fd=%d, profile %s", path, fd, handler);
51
52
53
struct impl *backend = user_data;
54
DBusMessage *r;
55
56
- r = dbus_pending_call_steal_reply(pending);
57
+ r = steal_reply_and_unref(&pending);
58
if (r == NULL)
59
return;
60
61
62
goto finish;
63
}
64
65
- finish:
66
+finish:
67
dbus_message_unref(r);
68
- dbus_pending_call_unref(pending);
69
}
70
71
static int register_profile(struct impl *backend, const char *profile, const char *uuid)
72
pipewire-0.3.74.tar.gz/spa/plugins/bluez5/backend-ofono.c -> pipewire-0.3.76.tar.gz/spa/plugins/bluez5/backend-ofono.c
Changed
26
1
2
#include <spa/param/audio/raw.h>
3
4
#include "defs.h"
5
+#include "dbus-helpers.h"
6
7
#define INITIAL_INTERVAL_NSEC (500 * SPA_NSEC_PER_MSEC)
8
#define ACTION_INTERVAL_NSEC (3000 * SPA_NSEC_PER_MSEC)
9
10
DBusMessage *r;
11
DBusMessageIter i, array_i, struct_i, props_i;
12
13
- r = dbus_pending_call_steal_reply(pending);
14
+ r = steal_reply_and_unref(&pending);
15
if (r == NULL)
16
return;
17
18
19
20
finish:
21
dbus_message_unref(r);
22
- dbus_pending_call_unref(pending);
23
}
24
25
static int backend_ofono_register(void *data)
26
pipewire-0.3.74.tar.gz/spa/plugins/bluez5/bluez5-dbus.c -> pipewire-0.3.76.tar.gz/spa/plugins/bluez5/bluez5-dbus.c
Changed
221
1
2
3
#include "config.h"
4
#include "codec-loader.h"
5
+#include "dbus-helpers.h"
6
#include "player.h"
7
#include "iso-io.h"
8
#include "defs.h"
9
10
DBusMessage *m;
11
const char *interface;
12
13
- if (device->battery_pending_call) {
14
- spa_log_debug(device->monitor->log, "Cancelling and freeing pending battery provider register call");
15
- dbus_pending_call_cancel(device->battery_pending_call);
16
- dbus_pending_call_unref(device->battery_pending_call);
17
- device->battery_pending_call = NULL;
18
- }
19
+ cancel_and_unref(&device->battery_pending_call);
20
21
if (!device->adapter || !device->adapter->has_battery_provider || !device->has_battery)
22
return;
23
24
DBusMessage *reply;
25
struct spa_bt_device *device = data;
26
27
- reply = dbus_pending_call_steal_reply(pending_call);
28
- dbus_pending_call_unref(pending_call);
29
-
30
- device->battery_pending_call = NULL;
31
+ spa_assert(device->battery_pending_call == pending_call);
32
+ reply = steal_reply_and_unref(&device->battery_pending_call);
33
34
if (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR) {
35
spa_log_error(device->monitor->log, "Failed to register battery provider. Error: %s", dbus_message_get_error_name(reply));
36
37
device->battery_pending_call, on_battery_provider_registered,
38
device, NULL)) {
39
spa_log_error(device->monitor->log, "Failed to register battery provider");
40
- dbus_pending_call_cancel(device->battery_pending_call);
41
- dbus_pending_call_unref(device->battery_pending_call);
42
- device->battery_pending_call = NULL;
43
+ cancel_and_unref(&device->battery_pending_call);
44
}
45
}
46
47
48
if (j >= size) {
49
const struct media_codec **p;
50
size = size * 2;
51
-#ifdef HAVE_REALLOCARRRAY
52
+#ifdef HAVE_REALLOCARRAY
53
p = reallocarray(supported_codecs, size, sizeof(const struct media_codec *));
54
#else
55
p = realloc(supported_codecs, size * sizeof(const struct media_codec *));
56
57
58
spa_bt_transport_destroy(transport);
59
60
- if (transport->acquire_call) {
61
- dbus_pending_call_cancel(transport->acquire_call);
62
- dbus_pending_call_unref(transport->acquire_call);
63
- transport->acquire_call = NULL;
64
- }
65
-
66
- if (transport->volume_call) {
67
- dbus_pending_call_cancel(transport->volume_call);
68
- dbus_pending_call_unref(transport->volume_call);
69
- transport->volume_call = NULL;
70
- }
71
+ cancel_and_unref(&transport->acquire_call);
72
+ cancel_and_unref(&transport->volume_call);
73
74
if (transport->fd >= 0) {
75
spa_bt_player_set_state(transport->device->adapter->dummy_player, SPA_BT_PLAYER_STOPPED);
76
77
78
spa_list_remove(&transport->bap_transport_linked);
79
80
+ free(transport->configuration);
81
free(transport->endpoint_path);
82
free(transport->path);
83
free(transport);
84
85
DBusError err = DBUS_ERROR_INIT;
86
DBusMessage *r;
87
88
- r = dbus_pending_call_steal_reply(pending);
89
-
90
spa_assert(transport->volume_call == pending);
91
- dbus_pending_call_unref(pending);
92
- transport->volume_call = NULL;
93
+ r = steal_reply_and_unref(&transport->volume_call);
94
95
if (dbus_set_error_from_message(&err, r)) {
96
spa_log_info(monitor->log, "transport %p: set volume failed for transport %s: %s",
97
98
int res = 0;
99
dbus_bool_t ret;
100
101
- if (transport->volume_call) {
102
- dbus_pending_call_cancel(transport->volume_call);
103
- dbus_pending_call_unref(transport->volume_call);
104
- transport->volume_call = NULL;
105
- }
106
+ cancel_and_unref(&transport->volume_call);
107
108
m = dbus_message_new_method_call(BLUEZ_SERVICE,
109
transport->path,
110
111
DBusMessage *r;
112
struct spa_bt_transport *t, *t_linked;
113
114
- r = dbus_pending_call_steal_reply(pending);
115
-
116
spa_assert(transport->acquire_call == pending);
117
- dbus_pending_call_unref(pending);
118
- transport->acquire_call = NULL;
119
+ r = steal_reply_and_unref(&transport->acquire_call);
120
121
spa_bt_device_update_last_bluez_action_time(device);
122
123
124
125
spa_bt_transport_set_state(transport, SPA_BT_TRANSPORT_STATE_IDLE);
126
127
- if (transport->acquire_call) {
128
- dbus_pending_call_cancel(transport->acquire_call);
129
- dbus_pending_call_unref(transport->acquire_call);
130
- transport->acquire_call = NULL;
131
- }
132
+ cancel_and_unref(&transport->acquire_call);
133
134
if (transport->iso_io) {
135
spa_log_debug(monitor->log, "transport %p: remove ISO IO", transport);
136
137
138
media_codec_switch_stop_timer(sw);
139
140
- if (sw->pending != NULL) {
141
- dbus_pending_call_cancel(sw->pending);
142
- dbus_pending_call_unref(sw->pending);
143
- }
144
+ cancel_and_unref(&sw->pending);
145
146
if (sw->device != NULL)
147
spa_list_remove(&sw->device_link);
148
149
struct spa_bt_device *device = sw->device;
150
DBusMessage *r;
151
152
- r = dbus_pending_call_steal_reply(pending);
153
-
154
spa_assert(sw->pending == pending);
155
- dbus_pending_call_unref(pending);
156
- sw->pending = NULL;
157
+ r = steal_reply_and_unref(&sw->pending);
158
159
spa_bt_device_update_last_bluez_action_time(device);
160
161
162
struct spa_bt_monitor *monitor = adapter->monitor;
163
DBusMessage *r;
164
165
- r = dbus_pending_call_steal_reply(pending);
166
- dbus_pending_call_unref(pending);
167
-
168
+ r = steal_reply_and_unref(&pending);
169
if (r == NULL)
170
return;
171
172
173
DBusMessage *r;
174
bool fallback = true;
175
176
- r = dbus_pending_call_steal_reply(pending);
177
- dbus_pending_call_unref(pending);
178
-
179
+ r = steal_reply_and_unref(&pending);
180
if (r == NULL)
181
return;
182
183
184
struct spa_bt_monitor *monitor = adapter->monitor;
185
DBusMessage *r;
186
187
- r = dbus_pending_call_steal_reply(pending);
188
- dbus_pending_call_unref(pending);
189
-
190
+ r = steal_reply_and_unref(&pending);
191
if (r == NULL)
192
return;
193
194
195
DBusMessage *r;
196
DBusMessageIter it6;
197
198
- spa_assert(pending == monitor->get_managed_objects_call);
199
- monitor->get_managed_objects_call = NULL;
200
-
201
- r = dbus_pending_call_steal_reply(pending);
202
- dbus_pending_call_unref(pending);
203
-
204
+ spa_assert(monitor->get_managed_objects_call == pending);
205
+ r = steal_reply_and_unref(&monitor->get_managed_objects_call);
206
if (r == NULL)
207
return;
208
209
210
monitor->filters_added = false;
211
}
212
213
- if (monitor->get_managed_objects_call) {
214
- dbus_pending_call_cancel(monitor->get_managed_objects_call);
215
- dbus_pending_call_unref(monitor->get_managed_objects_call);
216
- }
217
+ cancel_and_unref(&monitor->get_managed_objects_call);
218
219
spa_list_consume(t, &monitor->transport_list, link)
220
spa_bt_transport_free(t);
221
pipewire-0.3.76.tar.gz/spa/plugins/bluez5/dbus-helpers.h
Added
34
1
2
+/* Spa Bluez5 DBus helpers */
3
+/* SPDX-FileCopyrightText: Copyright © 2023 PipeWire authors */
4
+/* SPDX-License-Identifier: MIT */
5
+
6
+#ifndef SPA_BLUEZ5_DBUS_HELPERS_H
7
+#define SPA_BLUEZ5_DBUS_HELPERS_H
8
+
9
+#include <dbus/dbus.h>
10
+
11
+#include <spa/utils/cleanup.h>
12
+
13
+static inline void cancel_and_unref(DBusPendingCall **pp)
14
+{
15
+ DBusPendingCall *pending_call = spa_steal_ptr(*pp);
16
+
17
+ if (pending_call) {
18
+ dbus_pending_call_cancel(pending_call);
19
+ dbus_pending_call_unref(pending_call);
20
+ }
21
+}
22
+
23
+static inline DBusMessage *steal_reply_and_unref(DBusPendingCall **pp)
24
+{
25
+ DBusPendingCall *pending_call = spa_steal_ptr(*pp);
26
+
27
+ DBusMessage *reply = dbus_pending_call_steal_reply(pending_call);
28
+ dbus_pending_call_unref(pending_call);
29
+
30
+ return reply;
31
+}
32
+
33
+#endif /* SPA_BLUEZ5_DBUS_HELPERS_H */
34
pipewire-0.3.74.tar.gz/spa/plugins/bluez5/modemmanager.c -> pipewire-0.3.76.tar.gz/spa/plugins/bluez5/modemmanager.c
Changed
108
1
2
3
#include <ModemManager.h>
4
5
+#include "dbus-helpers.h"
6
#include "modemmanager.h"
7
8
#define DBUS_INTERFACE_OBJECTMANAGER "org.freedesktop.DBus.ObjectManager"
9
10
ret = dbus_pending_call_set_notify(pending_call, function, user_data, NULL);
11
if (!ret) {
12
spa_log_debug(this->log, "dbus set notify failure");
13
- dbus_pending_call_cancel(pending_call);
14
- dbus_pending_call_unref(pending_call);
15
+ cancel_and_unref(&pending_call);
16
goto out;
17
}
18
19
20
MMCallState state;
21
22
spa_assert(call->pending == pending);
23
- call->pending = NULL;
24
-
25
- r = dbus_pending_call_steal_reply(pending);
26
- dbus_pending_call_unref(pending);
27
+ r = steal_reply_and_unref(&call->pending);
28
if (r == NULL)
29
return;
30
31
32
DBusMessageIter i, array_i;
33
34
spa_assert(this->pending == pending);
35
- this->pending = NULL;
36
-
37
- r = dbus_pending_call_steal_reply(pending);
38
- dbus_pending_call_unref(pending);
39
+ r = steal_reply_and_unref(&this->pending);
40
if (r == NULL)
41
return;
42
43
44
dbus_message_unref(r);
45
}
46
47
-static void call_free(struct call *call) {
48
+static void call_free(struct call *call)
49
+{
50
spa_list_remove(&call->link);
51
52
- if (call->pending != NULL) {
53
- dbus_pending_call_cancel(call->pending);
54
- dbus_pending_call_unref(call->pending);
55
- }
56
+ cancel_and_unref(&call->pending);
57
58
if (call->number)
59
free(call->number);
60
61
spa_list_consume(call, &this->call_list, link)
62
call_free(call);
63
64
- if (this->voice_pending != NULL) {
65
- dbus_pending_call_cancel(this->voice_pending);
66
- dbus_pending_call_unref(this->voice_pending);
67
- }
68
+ cancel_and_unref(&this->voice_pending);
69
70
if (this->ops->set_call_setup)
71
this->ops->set_call_setup(CIND_CALLSETUP_NONE, this->user_data);
72
73
free(data);
74
75
spa_assert(call->pending == pending);
76
- call->pending = NULL;
77
-
78
- r = dbus_pending_call_steal_reply(pending);
79
- dbus_pending_call_unref(pending);
80
+ r = steal_reply_and_unref(&call->pending);
81
if (r == NULL)
82
return;
83
84
85
free(data);
86
87
spa_assert(this->voice_pending == pending);
88
- this->voice_pending = NULL;
89
-
90
- r = dbus_pending_call_steal_reply(pending);
91
- dbus_pending_call_unref(pending);
92
+ r = steal_reply_and_unref(&this->voice_pending);
93
if (r == NULL)
94
return;
95
96
97
{
98
struct impl *this = data;
99
100
- if (this->pending != NULL) {
101
- dbus_pending_call_cancel(this->pending);
102
- dbus_pending_call_unref(this->pending);
103
- }
104
+ cancel_and_unref(&this->pending);
105
106
mm_clean_voice(this);
107
mm_clean_modem3gpp(this);
108
pipewire-0.3.74.tar.gz/spa/plugins/bluez5/upower.c -> pipewire-0.3.76.tar.gz/spa/plugins/bluez5/upower.c
Changed
121
1
2
#include <errno.h>
3
#include <spa/utils/string.h>
4
5
+#include "dbus-helpers.h"
6
#include "upower.h"
7
8
#define UPOWER_SERVICE "org.freedesktop.UPower"
9
10
struct spa_log *log;
11
DBusConnection *conn;
12
13
+ DBusPendingCall *pending_get_call;
14
+
15
bool filters_added;
16
17
void *user_data;
18
19
DBusMessage *r;
20
DBusMessageIter i, variant_i;
21
22
- r = dbus_pending_call_steal_reply(pending);
23
- dbus_pending_call_unref(pending);
24
+ spa_assert(backend->pending_get_call == pending);
25
+ r = steal_reply_and_unref(&backend->pending_get_call);
26
if (r == NULL)
27
return;
28
29
30
dbus_message_unref(r);
31
}
32
33
+static int update_battery_percentage(struct impl *this)
34
+{
35
+ cancel_and_unref(&this->pending_get_call);
36
+
37
+ DBusMessage *m = dbus_message_new_method_call(UPOWER_SERVICE,
38
+ UPOWER_DISPLAY_DEVICE_OBJECT,
39
+ DBUS_INTERFACE_PROPERTIES,
40
+ "Get");
41
+ if (!m)
42
+ return -ENOMEM;
43
+
44
+ dbus_message_append_args(m,
45
+ DBUS_TYPE_STRING, &(const char *){ UPOWER_DEVICE_INTERFACE },
46
+ DBUS_TYPE_STRING, &(const char *){ "Percentage" },
47
+ DBUS_TYPE_INVALID);
48
+ dbus_message_set_auto_start(m, false);
49
+
50
+ dbus_connection_send_with_reply(this->conn, m, &this->pending_get_call, -1);
51
+ dbus_pending_call_set_notify(this->pending_get_call, upower_get_percentage_properties_reply, this, NULL);
52
+
53
+ dbus_message_unref(m);
54
+
55
+ return 0;
56
+}
57
+
58
static void upower_clean(struct impl *this)
59
{
60
this->set_battery_level(0, this->user_data);
61
62
}
63
64
if (new_owner && *new_owner) {
65
- DBusPendingCall *call;
66
- static const char* upower_device_interface = UPOWER_DEVICE_INTERFACE;
67
- static const char* percentage_property = "Percentage";
68
-
69
spa_log_debug(this->log, "UPower daemon appeared (%s)", new_owner);
70
-
71
- m = dbus_message_new_method_call(UPOWER_SERVICE, UPOWER_DISPLAY_DEVICE_OBJECT, DBUS_INTERFACE_PROPERTIES, "Get");
72
- if (m == NULL)
73
- goto finish;
74
- dbus_message_append_args(m, DBUS_TYPE_STRING, &upower_device_interface,
75
- DBUS_TYPE_STRING, &percentage_property, DBUS_TYPE_INVALID);
76
- dbus_connection_send_with_reply(this->conn, m, &call, -1);
77
- dbus_pending_call_set_notify(call, upower_get_percentage_properties_reply, this, NULL);
78
- dbus_message_unref(m);
79
+ update_battery_percentage(this);
80
}
81
}
82
} else if (dbus_message_is_signal(m, DBUS_INTERFACE_PROPERTIES, DBUS_SIGNAL_PROPERTIES_CHANGED)) {
83
84
this->set_battery_level = set_battery_level;
85
this->user_data = user_data;
86
87
- if (add_filters(this) < 0) {
88
+ if (add_filters(this) < 0)
89
goto fail4;
90
- }
91
-
92
- DBusMessage *m;
93
- DBusPendingCall *call;
94
95
- m = dbus_message_new_method_call(UPOWER_SERVICE, UPOWER_DISPLAY_DEVICE_OBJECT, DBUS_INTERFACE_PROPERTIES, "Get");
96
- if (m == NULL)
97
+ if (update_battery_percentage(this) < 0)
98
goto fail4;
99
100
- dbus_message_append_args(m,
101
- DBUS_TYPE_STRING, &(const char *){ UPOWER_DEVICE_INTERFACE },
102
- DBUS_TYPE_STRING, &(const char *){ "Percentage" },
103
- DBUS_TYPE_INVALID);
104
- dbus_message_set_auto_start(m, false);
105
- dbus_connection_send_with_reply(this->conn, m, &call, -1);
106
- dbus_pending_call_set_notify(call, upower_get_percentage_properties_reply, this, NULL);
107
- dbus_message_unref(m);
108
-
109
return this;
110
111
fail4:
112
113
{
114
struct impl *this = data;
115
116
+ cancel_and_unref(&this->pending_get_call);
117
+
118
if (this->filters_added) {
119
dbus_connection_remove_filter(this->conn, upower_filter_cb, this);
120
this->filters_added = false;
121
pipewire-0.3.74.tar.gz/spa/plugins/libcamera/libcamera-device.cpp -> pipewire-0.3.76.tar.gz/spa/plugins/libcamera/libcamera-device.cpp
Changed
68
1
2
/* SPDX-FileCopyrightText: Copyright © 2021 Wim Taymans <wim.taymans@gmail.com> */
3
/* SPDX-License-Identifier: MIT */
4
5
+#include "config.h"
6
+
7
#include <stddef.h>
8
#include <sys/types.h>
9
#include <sys/stat.h>
10
11
12
#include <libcamera/camera.h>
13
#include <libcamera/property_ids.h>
14
+#include <libcamera/base/span.h>
15
16
using namespace libcamera;
17
18
19
20
}
21
22
+static const libcamera::Span<const int64_t> cameraDevice(
23
+ const Camera *camera)
24
+{
25
+#ifdef HAVE_LIBCAMERA_SYSTEM_DEVICES
26
+ const ControlList &props = camera->properties();
27
+
28
+ if (auto devices = props.get(properties::SystemDevices))
29
+ return devices.value();
30
+#endif
31
+
32
+ return {};
33
+}
34
+
35
static std::string cameraModel(const Camera *camera)
36
{
37
const ControlList &props = camera->properties();
38
39
uint32_t n_items = 0;
40
struct spa_device_info info;
41
struct spa_param_info params2;
42
- char path256, model256, name256;
43
+ char path256, model256, name256, devices_str128;
44
+ struct spa_strbuf buf;
45
46
info = SPA_DEVICE_INFO_INIT();
47
48
49
ADD_ITEM(SPA_KEY_DEVICE_DESCRIPTION, model);
50
snprintf(name, sizeof(name), "libcamera_device.%s", impl->device_id.c_str());
51
ADD_ITEM(SPA_KEY_DEVICE_NAME, name);
52
+
53
+ auto device_numbers = cameraDevice(impl->camera.get());
54
+
55
+ if (!device_numbers.empty()) {
56
+ spa_strbuf_init(&buf, devices_str, sizeof(devices_str));
57
+
58
+ /* created a space separated string of all the device numbers */
59
+ for (int64_t device_number : device_numbers)
60
+ spa_strbuf_append(&buf, "%" PRId64 " ", device_number);
61
+
62
+ ADD_ITEM(SPA_KEY_DEVICE_DEVIDS, devices_str);
63
+ }
64
+
65
#undef ADD_ITEM
66
67
dict = SPA_DICT_INIT(items, n_items);
68
pipewire-0.3.74.tar.gz/spa/plugins/libcamera/meson.build -> pipewire-0.3.76.tar.gz/spa/plugins/libcamera/meson.build
Changed
9
1
2
3
libcameralib = shared_library('spa-libcamera',
4
libcamera_sources,
5
+ include_directories : configinc ,
6
dependencies : spa_dep, libudev_dep, libcamera_dep, pthread_lib ,
7
install : true,
8
install_dir : spa_plugindir / 'libcamera')
9
pipewire-0.3.74.tar.gz/spa/plugins/support/dbus.c -> pipewire-0.3.76.tar.gz/spa/plugins/support/dbus.c
Changed
25
1
2
struct connection *conn = userdata;
3
struct impl *impl = conn->impl;
4
5
+ spa_log_debug(impl->log, "impl:%p", impl);
6
if (dbus_connection_dispatch(conn->conn) == DBUS_DISPATCH_COMPLETE)
7
spa_loop_utils_enable_idle(impl->utils, conn->dispatch_event, false);
8
}
9
10
struct connection *c = userdata;
11
struct impl *impl = c->impl;
12
13
+ spa_log_debug(impl->log, "impl:%p %d", impl, status);
14
spa_loop_utils_enable_idle(impl->utils, c->dispatch_event,
15
status == DBUS_DISPATCH_COMPLETE ? false : true);
16
}
17
18
struct connection *this = userdata;
19
struct impl *impl = this->impl;
20
21
+ spa_log_debug(impl->log, "wakeup main impl:%p", impl);
22
spa_loop_utils_enable_idle(impl->utils, this->dispatch_event, true);
23
}
24
25
pipewire-0.3.74.tar.gz/spa/plugins/v4l2/v4l2-device.c -> pipewire-0.3.76.tar.gz/spa/plugins/v4l2/v4l2-device.c
Changed
40
1
2
3
struct props {
4
char device64;
5
+ char devnum32;
6
char product_id6;
7
char vendor_id6;
8
int device_fd;
9
10
static int emit_info(struct impl *this, bool full)
11
{
12
int res;
13
- struct spa_dict_item items12;
14
+ struct spa_dict_item items13;
15
uint32_t n_items = 0;
16
struct spa_device_info info;
17
struct spa_param_info params2;
18
- char path128, version16, capabilities16, device_caps16;
19
+ char path128, version16, capabilities16, device_caps16;
20
21
if ((res = spa_v4l2_open(&this->dev, this->props.device)) < 0)
22
return res;
23
24
if (this->props.vendor_id0)
25
ADD_ITEM(SPA_KEY_DEVICE_VENDOR_ID, this->props.vendor_id);
26
ADD_ITEM(SPA_KEY_API_V4L2_PATH, (char *)this->props.device);
27
+ ADD_ITEM(SPA_KEY_DEVICE_DEVIDS, (char *)this->props.devnum);
28
ADD_ITEM(SPA_KEY_API_V4L2_CAP_DRIVER, (char *)this->dev.cap.driver);
29
ADD_ITEM(SPA_KEY_API_V4L2_CAP_CARD, (char *)this->dev.cap.card);
30
ADD_ITEM(SPA_KEY_API_V4L2_CAP_BUS_INFO, (char *)this->dev.cap.bus_info);
31
32
33
if (info && (str = spa_dict_lookup(info, SPA_KEY_API_V4L2_PATH)))
34
strncpy(this->props.device, str, 63);
35
+ if (info && (str = spa_dict_lookup(info, SPA_KEY_DEVICE_DEVIDS)))
36
+ strncpy(this->props.devnum, str, 31);
37
if (info && (str = spa_dict_lookup(info, SPA_KEY_DEVICE_PRODUCT_ID)))
38
strncpy(this->props.product_id, str, 5);
39
if (info && (str = spa_dict_lookup(info, SPA_KEY_DEVICE_VENDOR_ID)))
40
pipewire-0.3.74.tar.gz/spa/plugins/v4l2/v4l2-udev.c -> pipewire-0.3.76.tar.gz/spa/plugins/v4l2/v4l2-udev.c
Changed
21
1
2
uint32_t id = device->id;
3
struct udev_device *dev = device->dev;
4
const char *str;
5
- struct spa_dict_item items20;
6
+ struct spa_dict_item items21;
7
uint32_t n_items = 0;
8
+ char devnum32;
9
10
info = SPA_DEVICE_OBJECT_INFO_INIT();
11
12
13
itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_MEDIA_CLASS, "Video/Device");
14
15
itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_API_V4L2_PATH, udev_device_get_devnode(dev));
16
+ snprintf(devnum, sizeof(devnum), "%" PRId64, (int64_t)udev_device_get_devnum(dev));
17
+ itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_DEVICE_DEVIDS, devnum);
18
19
if ((str = udev_device_get_property_value(dev, "USEC_INITIALIZED")) && *str)
20
itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_DEVICE_PLUGGED_USEC, str);
21
pipewire-0.3.74.tar.gz/src/modules/meson.build -> pipewire-0.3.76.tar.gz/src/modules/meson.build
Changed
37
1
2
'module-rtp-session.c',
3
'module-rtp-source.c',
4
'module-rtp-sink.c',
5
+ 'module-vban-recv.c',
6
+ 'module-vban-send.c',
7
'module-session-manager.c',
8
'module-zeroconf-discover.c',
9
'module-roc-source.c',
10
11
dependencies : mathlib, dl_lib, rt_lib, pipewire_dep,
12
)
13
14
+pipewire_module_vban_send = shared_library('pipewire-module-vban-send',
15
+ 'module-vban-send.c',
16
+ 'module-vban/stream.c' ,
17
+ include_directories : configinc,
18
+ install : true,
19
+ install_dir : modules_install_dir,
20
+ install_rpath: modules_install_dir,
21
+ dependencies : mathlib, dl_lib, rt_lib, pipewire_dep,
22
+)
23
+
24
+pipewire_module_vban_recv = shared_library('pipewire-module-vban-recv',
25
+ 'module-vban-recv.c',
26
+ 'module-vban/stream.c' ,
27
+ include_directories : configinc,
28
+ install : true,
29
+ install_dir : modules_install_dir,
30
+ install_rpath: modules_install_dir,
31
+ dependencies : mathlib, dl_lib, rt_lib, pipewire_dep,
32
+)
33
+
34
build_module_roc = roc_dep.found()
35
if build_module_roc
36
pipewire_module_roc_sink = shared_library('pipewire-module-roc-sink',
37
pipewire-0.3.74.tar.gz/src/modules/module-client-node/client-node.c -> pipewire-0.3.76.tar.gz/src/modules/module-client-node/client-node.c
Changed
10
1
2
spa_node_call_ready(&impl->callbacks, status);
3
} else {
4
spa_log_trace_fp(impl->log, "%p: got complete", impl);
5
- pw_context_driver_emit_complete(node->context, node);
6
+ pw_impl_node_rt_emit_complete(node);
7
}
8
}
9
}
10
pipewire-0.3.74.tar.gz/src/modules/module-client-node/remote-node.c -> pipewire-0.3.76.tar.gz/src/modules/module-client-node/remote-node.c
Changed
89
1
2
3
struct node_data {
4
struct pw_context *context;
5
- struct spa_hook context_listener;
6
7
struct pw_loop *data_loop;
8
struct spa_system *data_system;
9
10
11
struct pw_impl_node *node;
12
struct spa_hook node_listener;
13
+ struct spa_hook node_rt_listener;
14
unsigned int do_free:1;
15
unsigned int have_transport:1;
16
unsigned int allow_mlock:1;
17
18
{
19
pw_log_debug("port %p: mix clear %d.%d", mix->port, mix->port->port_id, mix->mix.id);
20
21
- spa_node_port_set_io(mix->port->mix, mix->mix.port.direction,
22
- mix->mix.port.port_id, SPA_IO_Buffers, NULL, 0);
23
+ if (mix->mix.id != SPA_ID_INVALID)
24
+ spa_node_port_set_io(mix->port->mix, mix->mix.port.direction,
25
+ mix->mix.port.port_id, SPA_IO_Buffers, NULL, 0);
26
27
spa_list_remove(&mix->link);
28
29
30
spa_hook_remove(&data->proxy_client_node_listener);
31
spa_hook_remove(&data->client_node_listener);
32
33
- pw_context_driver_remove_listener(data->context,
34
- &data->context_listener);
35
-
36
if (data->node) {
37
spa_hook_remove(&data->node_listener);
38
+ pw_impl_node_remove_rt_listener(data->node,
39
+ &data->node_rt_listener);
40
pw_impl_node_set_state(data->node, PW_NODE_STATE_SUSPENDED);
41
42
clean_node(data);
43
44
.bound_props = client_node_bound_props,
45
};
46
47
-static void context_complete(void *data, struct pw_impl_node *node)
48
+static void node_rt_complete(void *data)
49
{
50
struct node_data *d = data;
51
+ struct pw_impl_node *node = d->node;
52
struct spa_system *data_system = d->data_system;
53
54
- if (node != d->node || !node->driving ||
55
+ if (!node->driving ||
56
!SPA_FLAG_IS_SET(node->rt.target.activation->flags, PW_NODE_ACTIVATION_FLAG_PROFILER))
57
return;
58
59
60
pw_log_warn("node %p: write failed %m", node);
61
}
62
63
-static const struct pw_context_driver_events context_events = {
64
- PW_VERSION_CONTEXT_DRIVER_EVENTS,
65
- .complete = context_complete,
66
+static const struct pw_impl_node_rt_events node_rt_events = {
67
+ PW_VERSION_IMPL_NODE_RT_EVENTS,
68
+ .complete = node_rt_complete,
69
};
70
71
static struct pw_proxy *node_export(struct pw_core *core, void *object, bool do_free,
72
73
&proxy_client_node_events, data);
74
75
pw_impl_node_add_listener(node, &data->node_listener, &node_events, data);
76
+ pw_impl_node_add_rt_listener(node, &data->node_rt_listener,
77
+ &node_rt_events, data);
78
79
pw_client_node_add_listener(data->client_node,
80
&data->client_node_listener,
81
&client_node_events,
82
data);
83
- pw_context_driver_add_listener(data->context,
84
- &data->context_listener,
85
- &context_events, data);
86
87
do_node_init(data);
88
89
pipewire-0.3.74.tar.gz/src/modules/module-profiler.c -> pipewire-0.3.76.tar.gz/src/modules/module-profiler.c
Changed
364
1
2
#define PW_LOG_TOPIC_DEFAULT mod_topic
3
4
#define TMP_BUFFER (16 * 1024)
5
-#define MAX_BUFFER (8 * 1024 * 1024)
6
-#define MIN_FLUSH (16 * 1024)
7
-#define DEFAULT_IDLE 5
8
-#define DEFAULT_INTERVAL 1
9
+#define DATA_BUFFER (32 * 1024)
10
+#define FLUSH_BUFFER (8 * 1024 * 1024)
11
12
int pw_protocol_native_ext_profiler_init(struct pw_context *context);
13
14
15
{ PW_KEY_MODULE_VERSION, PACKAGE_VERSION },
16
};
17
18
+struct node {
19
+ struct spa_list link;
20
+ struct impl *impl;
21
+
22
+ struct pw_impl_node *node;
23
+ struct spa_hook node_rt_listener;
24
+
25
+ int64_t count;
26
+ struct spa_ringbuffer buffer;
27
+ uint8_t tmpTMP_BUFFER;
28
+ uint8_t dataDATA_BUFFER;
29
+
30
+ unsigned enabled:1;
31
+};
32
+
33
struct impl {
34
struct pw_context *context;
35
struct pw_properties *properties;
36
37
struct pw_global *global;
38
struct spa_hook global_listener;
39
40
- int64_t count;
41
+ struct spa_list node_list;
42
+
43
uint32_t busy;
44
- uint32_t empty;
45
- struct spa_source *flush_timeout;
46
- unsigned int flushing:1;
47
+ struct spa_source *flush_event;
48
unsigned int listening:1;
49
50
- struct spa_ringbuffer buffer;
51
- uint8_t tmpTMP_BUFFER;
52
- uint8_t dataMAX_BUFFER;
53
-
54
- uint8_t flushMAX_BUFFER + sizeof(struct spa_pod_struct);
55
+ uint8_t flushFLUSH_BUFFER + sizeof(struct spa_pod_struct);
56
};
57
58
struct resource_data {
59
60
struct spa_hook resource_listener;
61
};
62
63
-static void start_flush(struct impl *impl)
64
-{
65
- struct timespec value, interval;
66
-
67
- value.tv_sec = 0;
68
- value.tv_nsec = 1;
69
- interval.tv_sec = DEFAULT_INTERVAL;
70
- interval.tv_nsec = 0;
71
- pw_loop_update_timer(impl->main_loop,
72
- impl->flush_timeout, &value, &interval, false);
73
- impl->flushing = true;
74
-}
75
-
76
-static void stop_flush(struct impl *impl)
77
-{
78
- struct timespec value, interval;
79
-
80
- if (!impl->flushing)
81
- return;
82
-
83
- value.tv_sec = 0;
84
- value.tv_nsec = 0;
85
- interval.tv_sec = 0;
86
- interval.tv_nsec = 0;
87
- pw_loop_update_timer(impl->main_loop,
88
- impl->flush_timeout, &value, &interval, false);
89
- impl->flushing = false;
90
-}
91
-
92
-static void flush_timeout(void *data, uint64_t expirations)
93
+static void do_flush_event(void *data, uint64_t count)
94
{
95
struct impl *impl = data;
96
- int32_t avail;
97
- uint32_t idx;
98
- struct spa_pod_struct *p;
99
struct pw_resource *resource;
100
+ struct node *n;
101
+ uint32_t total = 0;
102
+ struct spa_pod_struct *p;
103
104
- avail = spa_ringbuffer_get_read_index(&impl->buffer, &idx);
105
+ p = (struct spa_pod_struct *)impl->flush;
106
107
- pw_log_trace("%p avail %d", impl, avail);
108
+ spa_list_for_each(n, &impl->node_list, link) {
109
+ int32_t avail;
110
+ uint32_t idx;
111
112
- if (avail <= 0) {
113
- if (++impl->empty == DEFAULT_IDLE)
114
- stop_flush(impl);
115
- return;
116
- }
117
- impl->empty = 0;
118
+ avail = spa_ringbuffer_get_read_index(&n->buffer, &idx);
119
120
- p = (struct spa_pod_struct *)impl->flush;
121
- *p = SPA_POD_INIT_Struct(avail);
122
+ pw_log_trace("%p avail %d", impl, avail);
123
+
124
+ if (avail > 0) {
125
+ spa_ringbuffer_read_data(&n->buffer, n->data, DATA_BUFFER,
126
+ idx % DATA_BUFFER,
127
+ SPA_PTROFF(p, sizeof(struct spa_pod_struct) + total, void),
128
+ avail);
129
+ spa_ringbuffer_read_update(&n->buffer, idx + avail);
130
+ total += avail;
131
+ }
132
+ }
133
134
- spa_ringbuffer_read_data(&impl->buffer, impl->data, MAX_BUFFER,
135
- idx % MAX_BUFFER,
136
- SPA_PTROFF(p, sizeof(struct spa_pod_struct), void), avail);
137
- spa_ringbuffer_read_update(&impl->buffer, idx + avail);
138
+ *p = SPA_POD_INIT_Struct(total);
139
140
spa_list_for_each(resource, &impl->global->resource_list, link)
141
pw_profiler_resource_profile(resource, &p->pod);
142
}
143
144
-static void context_do_profile(void *data, struct pw_impl_node *node)
145
+static void context_do_profile(void *data)
146
{
147
- struct impl *impl = data;
148
+ struct node *n = data;
149
+ struct pw_impl_node *node = n->node;
150
+ struct impl *impl = n->impl;
151
struct spa_pod_builder b;
152
struct spa_pod_frame f2;
153
uint32_t id = node->info.id;
154
155
if (SPA_FLAG_IS_SET(pos->clock.flags, SPA_IO_CLOCK_FLAG_FREEWHEEL))
156
return;
157
158
- spa_pod_builder_init(&b, impl->tmp, sizeof(impl->tmp));
159
+ spa_pod_builder_init(&b, n->tmp, sizeof(n->tmp));
160
spa_pod_builder_push_object(&b, &f0,
161
SPA_TYPE_OBJECT_Profiler, 0);
162
163
spa_pod_builder_prop(&b, SPA_PROFILER_info, 0);
164
spa_pod_builder_add_struct(&b,
165
- SPA_POD_Long(impl->count),
166
+ SPA_POD_Long(n->count),
167
SPA_POD_Float(a->cpu_load0),
168
SPA_POD_Float(a->cpu_load1),
169
SPA_POD_Float(a->cpu_load2),
170
171
}
172
spa_pod_builder_pop(&b, &f0);
173
174
- if (b.state.offset > sizeof(impl->tmp))
175
+ if (b.state.offset > sizeof(n->tmp))
176
goto done;
177
178
- filled = spa_ringbuffer_get_write_index(&impl->buffer, &idx);
179
- if (filled < 0 || filled > MAX_BUFFER) {
180
+ filled = spa_ringbuffer_get_write_index(&n->buffer, &idx);
181
+ if (filled < 0 || filled > DATA_BUFFER) {
182
pw_log_warn("%p: queue xrun %d", impl, filled);
183
goto done;
184
}
185
- avail = MAX_BUFFER - filled;
186
+ avail = DATA_BUFFER - filled;
187
if (avail < b.state.offset) {
188
pw_log_warn("%p: queue full %d < %d", impl, avail, b.state.offset);
189
goto done;
190
}
191
- spa_ringbuffer_write_data(&impl->buffer,
192
- impl->data, MAX_BUFFER,
193
- idx % MAX_BUFFER,
194
+ spa_ringbuffer_write_data(&n->buffer,
195
+ n->data, DATA_BUFFER,
196
+ idx % DATA_BUFFER,
197
b.data, b.state.offset);
198
- spa_ringbuffer_write_update(&impl->buffer, idx + b.state.offset);
199
+ spa_ringbuffer_write_update(&n->buffer, idx + b.state.offset);
200
201
- if (!impl->flushing || filled + b.state.offset > MIN_FLUSH)
202
- start_flush(impl);
203
+ pw_loop_signal_event(impl->main_loop, impl->flush_event);
204
done:
205
- impl->count++;
206
+ n->count++;
207
}
208
209
-static const struct pw_context_driver_events context_events = {
210
- PW_VERSION_CONTEXT_DRIVER_EVENTS,
211
- .incomplete = context_do_profile,
212
+static struct pw_impl_node_rt_events node_rt_events = {
213
+ PW_VERSION_IMPL_NODE_RT_EVENTS,
214
.complete = context_do_profile,
215
+ .incomplete = context_do_profile,
216
+};
217
+
218
+static void enable_node_profiling(struct node *n, bool enabled)
219
+{
220
+ if (enabled && !n->enabled) {
221
+ SPA_FLAG_SET(n->node->rt.target.activation->flags, PW_NODE_ACTIVATION_FLAG_PROFILER);
222
+ pw_impl_node_add_rt_listener(n->node, &n->node_rt_listener, &node_rt_events, n);
223
+ } else if (!enabled && n->enabled) {
224
+ SPA_FLAG_CLEAR(n->node->rt.target.activation->flags, PW_NODE_ACTIVATION_FLAG_PROFILER);
225
+ pw_impl_node_remove_rt_listener(n->node, &n->node_rt_listener);
226
+ }
227
+ n->enabled = enabled;
228
+}
229
+
230
+static void enable_profiling(struct impl *impl, bool enabled)
231
+{
232
+ struct node *n;
233
+ spa_list_for_each(n, &impl->node_list, link)
234
+ enable_node_profiling(n, enabled);
235
+}
236
+
237
+static void context_driver_added(void *data, struct pw_impl_node *node)
238
+{
239
+ struct impl *impl = data;
240
+ struct node *n;
241
+
242
+ n = calloc(1, sizeof(*n));
243
+ if (n == NULL)
244
+ return;
245
+
246
+ n->impl = impl;
247
+ n->node = node;
248
+ spa_list_append(&impl->node_list, &n->link);
249
+ spa_ringbuffer_init(&n->buffer);
250
+
251
+ if (impl->busy > 0)
252
+ enable_node_profiling(n, true);
253
+}
254
+
255
+static struct node *find_node(struct impl *impl, struct pw_impl_node *node)
256
+{
257
+ struct node *n;
258
+ spa_list_for_each(n, &impl->node_list, link) {
259
+ if (n->node == node)
260
+ return n;
261
+ }
262
+ return NULL;
263
+}
264
+
265
+static void context_driver_removed(void *data, struct pw_impl_node *node)
266
+{
267
+ struct impl *impl = data;
268
+ struct node *n;
269
+
270
+ n = find_node(impl, node);
271
+ if (n == NULL)
272
+ return;
273
+
274
+ enable_node_profiling(n, false);
275
+ spa_list_remove(&n->link);
276
+ free(n);
277
+}
278
+
279
+static const struct pw_context_events context_events = {
280
+ PW_VERSION_CONTEXT_EVENTS,
281
+ .driver_added = context_driver_added,
282
+ .driver_removed = context_driver_removed,
283
};
284
285
static void stop_listener(struct impl *impl)
286
{
287
if (impl->listening) {
288
- pw_context_driver_remove_listener(impl->context,
289
- &impl->context_listener);
290
+ enable_profiling(impl, false);
291
impl->listening = false;
292
}
293
}
294
295
296
if (++impl->busy == 1) {
297
pw_log_info("%p: starting profiler", impl);
298
- pw_context_driver_add_listener(impl->context,
299
- &impl->context_listener,
300
- &context_events, impl);
301
+ enable_profiling(impl, true);
302
impl->listening = true;
303
}
304
return 0;
305
306
if (impl->global != NULL)
307
pw_global_destroy(impl->global);
308
309
+ spa_hook_remove(&impl->context_listener);
310
spa_hook_remove(&impl->module_listener);
311
312
pw_properties_free(impl->properties);
313
314
- pw_loop_destroy_source(impl->main_loop, impl->flush_timeout);
315
+ pw_loop_destroy_source(impl->main_loop, impl->flush_event);
316
317
free(impl);
318
}
319
320
struct impl *impl = data;
321
322
stop_listener(impl);
323
- stop_flush(impl);
324
325
spa_hook_remove(&impl->global_listener);
326
impl->global = NULL;
327
328
if (impl == NULL)
329
return -errno;
330
331
+ spa_list_init(&impl->node_list);
332
pw_protocol_native_ext_profiler_init(context);
333
334
pw_log_debug("module %p: new %s", impl, args);
335
336
impl->main_loop = pw_context_get_main_loop(impl->context);
337
impl->data_loop = pw_data_loop_get_loop(pw_context_get_data_loop(impl->context));
338
339
- spa_ringbuffer_init(&impl->buffer);
340
-
341
impl->global = pw_global_new(context,
342
PW_TYPE_INTERFACE_Profiler,
343
PW_VERSION_PROFILER,
344
345
pw_properties_setf(impl->properties, PW_KEY_OBJECT_SERIAL, "%"PRIu64,
346
pw_global_get_serial(impl->global));
347
348
- impl->flush_timeout = pw_loop_add_timer(impl->main_loop, flush_timeout, impl);
349
+ impl->flush_event = pw_loop_add_event(impl->main_loop, do_flush_event, impl);
350
351
pw_global_update_keys(impl->global, &impl->properties->dict, keys);
352
353
354
355
pw_impl_module_update_properties(module, &SPA_DICT_INIT_ARRAY(module_props));
356
357
+ pw_context_add_listener(impl->context,
358
+ &impl->context_listener,
359
+ &context_events, impl);
360
+
361
pw_global_register(impl->global);
362
363
pw_global_add_listener(impl->global, &impl->global_listener, &global_events, impl);
364
pipewire-0.3.74.tar.gz/src/modules/module-protocol-pulse.c -> pipewire-0.3.76.tar.gz/src/modules/module-protocol-pulse.c
Changed
11
1
2
* * `remove-capture-dont-move` Removes the DONT_MOVE flag on capture streams. Some applications
3
* set this flag so that the stream can't be moved anymore with tools such as
4
* pavucontrol.
5
+ * * `block-source-volume` blocks the client from updating any source volumes. This can be used
6
+ * to disable things like automatic gain control.
7
+ * * `block-sink-volume` blocks the client from updating any sink volumes.
8
*
9
* ### update-props
10
*
11
pipewire-0.3.74.tar.gz/src/modules/module-protocol-pulse/manager.c -> pipewire-0.3.76.tar.gz/src/modules/module-protocol-pulse/manager.c
Changed
18
1
2
pw_properties_parse_bool(str);
3
}
4
5
+bool pw_manager_object_is_network(struct pw_manager_object *o)
6
+{
7
+ const char *str;
8
+ struct pw_node_info *info;
9
+ return spa_streq(o->type, PW_TYPE_INTERFACE_Node) &&
10
+ (info = o->info) != NULL && info->props != NULL &&
11
+ (str = spa_dict_lookup(info->props, PW_KEY_NODE_NETWORK)) != NULL &&
12
+ pw_properties_parse_bool(str);
13
+}
14
+
15
bool pw_manager_object_is_source_or_monitor(struct pw_manager_object *o)
16
{
17
return pw_manager_object_is_source(o) || pw_manager_object_is_monitor(o);
18
pipewire-0.3.74.tar.gz/src/modules/module-protocol-pulse/manager.h -> pipewire-0.3.76.tar.gz/src/modules/module-protocol-pulse/manager.h
Changed
9
1
2
bool pw_manager_object_is_source(struct pw_manager_object *o);
3
bool pw_manager_object_is_monitor(struct pw_manager_object *o);
4
bool pw_manager_object_is_virtual(struct pw_manager_object *o);
5
+bool pw_manager_object_is_network(struct pw_manager_object *o);
6
bool pw_manager_object_is_source_or_monitor(struct pw_manager_object *o);
7
bool pw_manager_object_is_sink_input(struct pw_manager_object *o);
8
bool pw_manager_object_is_source_output(struct pw_manager_object *o);
9
pipewire-0.3.74.tar.gz/src/modules/module-protocol-pulse/modules/module-switch-on-connect.c -> pipewire-0.3.76.tar.gz/src/modules/module-protocol-pulse/modules/module-switch-on-connect.c
Changed
10
1
2
return;
3
}
4
5
- if (d->ignore_virtual && spa_dict_lookup(info->props, PW_KEY_DEVICE_API) == NULL) {
6
+ if (d->ignore_virtual && pw_manager_object_is_virtual(o)) {
7
pw_log_debug("not switching to virtual device");
8
return;
9
}
10
pipewire-0.3.74.tar.gz/src/modules/module-protocol-pulse/modules/module-zeroconf-publish.c -> pipewire-0.3.76.tar.gz/src/modules/module-protocol-pulse/modules/module-zeroconf-publish.c
Changed
28
1
2
3
collect_device_info(o, card, &dev_info, false, &impl->defs);
4
5
- if ((str = spa_dict_lookup(info->props, PW_KEY_DEVICE_API)) != NULL) {
6
+ if (!pw_manager_object_is_virtual(o)) {
7
if (is_sink)
8
flags |= SINK_HARDWARE;
9
else if (is_source)
10
11
{
12
struct service *s;
13
struct pw_node_info *info;
14
- const char *str;
15
16
if (!pw_manager_object_is_sink(o) && !pw_manager_object_is_source(o))
17
return;
18
19
if (info == NULL || info->props == NULL)
20
return;
21
22
- if ((str = spa_dict_lookup(info->props, PW_KEY_NODE_NETWORK)) != NULL &&
23
- spa_atob(str))
24
+ if (pw_manager_object_is_network(o))
25
return;
26
27
s = create_service(d, o);
28
pipewire-0.3.74.tar.gz/src/modules/module-protocol-pulse/pulse-server.c -> pipewire-0.3.76.tar.gz/src/modules/module-protocol-pulse/pulse-server.c
Changed
25
1
2
}
3
4
flags = SINK_LATENCY | SINK_DYNAMIC_LATENCY | SINK_DECIBEL_VOLUME;
5
- if ((str = spa_dict_lookup(info->props, PW_KEY_DEVICE_API)) != NULL)
6
+ if (!pw_manager_object_is_virtual(o))
7
flags |= SINK_HARDWARE;
8
- if ((str = spa_dict_lookup(info->props, PW_KEY_NODE_NETWORK)) != NULL)
9
+ if (pw_manager_object_is_network(o))
10
flags |= SINK_NETWORK;
11
if (SPA_FLAG_IS_SET(dev_info.volume_info.flags, VOLUME_HW_VOLUME))
12
flags |= SINK_HW_VOLUME_CTRL;
13
14
}
15
16
flags = SOURCE_LATENCY | SOURCE_DYNAMIC_LATENCY | SOURCE_DECIBEL_VOLUME;
17
- if ((str = spa_dict_lookup(info->props, PW_KEY_DEVICE_API)) != NULL)
18
+ if (!pw_manager_object_is_virtual(o))
19
flags |= SOURCE_HARDWARE;
20
- if ((str = spa_dict_lookup(info->props, PW_KEY_NODE_NETWORK)) != NULL)
21
+ if (pw_manager_object_is_network(o))
22
flags |= SOURCE_NETWORK;
23
if (SPA_FLAG_IS_SET(dev_info.volume_info.flags, VOLUME_HW_VOLUME))
24
flags |= SOURCE_HW_VOLUME_CTRL;
25
pipewire-0.3.74.tar.gz/src/modules/module-raop-discover.c -> pipewire-0.3.76.tar.gz/src/modules/module-raop-discover.c
Changed
27
1
2
value = "tcp";
3
pw_properties_set(props, "raop.transport", value);
4
} else if (spa_streq(key, "et")) {
5
- /* Supported encryption types:
6
+ /* RAOP encryption types:
7
* 0 = none,
8
* 1 = RSA,
9
- * 2 = FairPlay,
10
- * 3 = MFiSAP,
11
- * 4 = FairPlay SAPv2.5. */
12
+ * 3 = FairPlay,
13
+ * 4 = MFiSAP (/auth-setup),
14
+ * 5 = FairPlay SAPv2.5 */
15
if (str_in_list(value, ",", "1"))
16
value = "RSA";
17
else if (str_in_list(value, ",", "4"))
18
19
value = "ALAC";
20
else if (str_in_list(value, ",", "2"))
21
value = "AAC";
22
- else if (str_in_list(value, ",", "2"))
23
+ else if (str_in_list(value, ",", "3"))
24
value = "AAC-ELD";
25
else
26
value = "unknown";
27
pipewire-0.3.74.tar.gz/src/modules/module-raop-sink.c -> pipewire-0.3.76.tar.gz/src/modules/module-raop-sink.c
Changed
171
1
2
#define FRAMES_PER_UDP_PACKET 352
3
4
#define RAOP_LATENCY_MIN 11025u
5
-#define DEFAULT_LATENCY_MS "1000"
6
+#define DEFAULT_LATENCY_MS "1500"
7
8
#define DEFAULT_TCP_AUDIO_PORT 6000
9
#define DEFAULT_UDP_AUDIO_PORT 6000
10
11
pkt0 |= htonl(0x10000000);
12
pkt1 = htonl(rtptime - latency);
13
transmitted = ntp_now();
14
- pkt2 = htonl((transmitted >> 32) & 0x0000ffff);
15
+ pkt2 = htonl(transmitted >> 32);
16
pkt3 = htonl(transmitted & 0xffffffff);
17
pkt4 = htonl(rtptime);
18
19
20
21
static int rtsp_send(struct impl *impl, const char *method,
22
const char *content_type, const char *content,
23
- int (*reply) (void *data, int status, const struct spa_dict *headers))
24
+ int (*reply) (void *data, int status, const struct spa_dict *headers, const struct pw_array *content))
25
{
26
int res;
27
28
29
return res;
30
}
31
32
-static int rtsp_flush_reply(void *data, int status, const struct spa_dict *headers)
33
+static int rtsp_log_reply_status(void *data, int status, const struct spa_dict *headers, const struct pw_array *content)
34
{
35
- pw_log_info("reply %d", status);
36
+ pw_log_info("reply status: %d", status);
37
return 0;
38
}
39
40
41
42
impl->recording = false;
43
44
- res = rtsp_send(impl, "FLUSH", NULL, NULL, rtsp_flush_reply);
45
+ res = rtsp_send(impl, "FLUSH", NULL, NULL, rtsp_log_reply_status);
46
47
pw_properties_set(impl->headers, "Range", NULL);
48
pw_properties_set(impl->headers, "RTP-Info", NULL);
49
50
char header128, volstr64;
51
snprintf(header, sizeof(header), "volume: %s\r\n",
52
spa_dtoa(volstr, sizeof(volstr), impl->volume));
53
- return rtsp_send(impl, "SET_PARAMETER", "text/parameters", header, NULL);
54
+ return rtsp_send(impl, "SET_PARAMETER", "text/parameters", header, rtsp_log_reply_status);
55
}
56
57
-static int rtsp_record_reply(void *data, int status, const struct spa_dict *headers)
58
+static int rtsp_record_reply(void *data, int status, const struct spa_dict *headers, const struct pw_array *content)
59
{
60
struct impl *impl = data;
61
const char *str;
62
63
struct spa_latency_info latency;
64
char progress128;
65
66
- pw_log_info("reply %d", status);
67
+ pw_log_info("record status: %d", status);
68
69
if ((str = spa_dict_lookup(headers, "Audio-Latency")) != NULL) {
70
uint32_t l;
71
72
rtsp_send_volume(impl);
73
74
snprintf(progress, sizeof(progress), "progress: %s/%s/%s\r\n", "0", "0", "0");
75
- return rtsp_send(impl, "SET_PARAMETER", "text/parameters", progress, NULL);
76
+ return rtsp_send(impl, "SET_PARAMETER", "text/parameters", progress, rtsp_log_reply_status);
77
}
78
79
static int rtsp_do_record(struct impl *impl)
80
81
pw_loop_update_io(impl->loop, impl->server_source, 0);
82
}
83
84
-static int rtsp_setup_reply(void *data, int status, const struct spa_dict *headers)
85
+static int rtsp_setup_reply(void *data, int status, const struct spa_dict *headers, const struct pw_array *content)
86
{
87
struct impl *impl = data;
88
const char *str, *state = NULL, *s;
89
90
uint16_t control_port, timing_port;
91
int res;
92
93
- pw_log_info("reply %d", status);
94
+ pw_log_info("setup status: %d", status);
95
96
if ((str = spa_dict_lookup(headers, "Session")) == NULL) {
97
pw_log_error("missing Session header");
98
99
return -EIO;
100
}
101
102
-static int rtsp_announce_reply(void *data, int status, const struct spa_dict *headers)
103
+static int rtsp_announce_reply(void *data, int status, const struct spa_dict *headers, const struct pw_array *content)
104
{
105
struct impl *impl = data;
106
107
- pw_log_info("reply %d", status);
108
+ pw_log_info("announce status: %d", status);
109
110
pw_properties_set(impl->headers, "Apple-Challenge", NULL);
111
112
113
return rtsp_send(impl, "ANNOUNCE", "application/sdp", sdp, rtsp_announce_reply);
114
}
115
116
-static int rtsp_auth_setup_reply(void *data, int status, const struct spa_dict *headers)
117
+static int rtsp_auth_setup_reply(void *data, int status, const struct spa_dict *headers, const struct pw_array *content)
118
{
119
struct impl *impl = data;
120
121
- pw_log_info("reply %d", status);
122
+ pw_log_info("auth-setup status: %d", status);
123
124
return rtsp_do_announce(impl);
125
}
126
127
rtsp_auth_setup_reply, impl);
128
}
129
130
-static int rtsp_auth_reply(void *data, int status, const struct spa_dict *headers)
131
+static int rtsp_auth_reply(void *data, int status, const struct spa_dict *headers, const struct pw_array *content)
132
{
133
struct impl *impl = data;
134
int res = 0;
135
136
- pw_log_info("auth %d", status);
137
+ pw_log_info("auth status: %d", status);
138
139
switch (status) {
140
case 200:
141
142
return rtsp_send(impl, "OPTIONS", NULL, NULL, rtsp_auth_reply);
143
}
144
145
-static int rtsp_options_reply(void *data, int status, const struct spa_dict *headers)
146
+static int rtsp_options_reply(void *data, int status, const struct spa_dict *headers, const struct pw_array *content)
147
{
148
struct impl *impl = data;
149
int res = 0;
150
151
- pw_log_info("options %d", status);
152
+ pw_log_info("options status: %d", status);
153
154
switch (status) {
155
case 401:
156
157
return pw_rtsp_client_connect(impl->rtsp, hostname, atoi(port), impl->session_id);
158
}
159
160
-static int rtsp_teardown_reply(void *data, int status, const struct spa_dict *headers)
161
+static int rtsp_teardown_reply(void *data, int status, const struct spa_dict *headers, const struct pw_array *content)
162
{
163
struct impl *impl = data;
164
const char *str;
165
166
- pw_log_info("reply");
167
+ pw_log_info("teardown status: %d", status);
168
169
connection_cleanup(impl);
170
171
pipewire-0.3.74.tar.gz/src/modules/module-raop/rtsp-client.c -> pipewire-0.3.76.tar.gz/src/modules/module-raop/rtsp-client.c
Changed
89
1
2
size_t len;
3
size_t offset;
4
uint32_t cseq;
5
- int (*reply) (void *user_data, int status, const struct spa_dict *headers);
6
+ int (*reply) (void *user_data, int status, const struct spa_dict *headers, const struct pw_array *content);
7
void *user_data;
8
};
9
10
11
char line_buf1024;
12
size_t line_pos;
13
struct pw_properties *headers;
14
+ struct pw_array content;
15
size_t content_length;
16
17
uint32_t cseq;
18
19
spa_list_init(&client->pending);
20
spa_hook_list_init(&client->listener_list);
21
client->headers = pw_properties_new(NULL, NULL);
22
+ pw_array_init(&client->content, 4096);
23
client->recv_state = CLIENT_RECV_NONE;
24
25
pw_log_info("new client %p", client);
26
27
pw_properties_free(client->headers);
28
pw_properties_free(client->props);
29
spa_hook_list_clean(&client->listener_list);
30
+ pw_array_clear(&client->content);
31
free(client);
32
}
33
34
35
36
msg = find_pending(client, cseq);
37
if (msg) {
38
- res = msg->reply(msg->user_data, client->status, &client->headers->dict);
39
+ res = msg->reply(msg->user_data, client->status, &client->headers->dict, &client->content);
40
spa_list_remove(&msg->link);
41
free(msg);
42
43
44
else {
45
pw_rtsp_client_emit_message(client, client->status, &client->headers->dict);
46
}
47
+
48
+ pw_array_reset(&client->content);
49
}
50
51
static void process_received_message(struct pw_rtsp_client *client)
52
53
54
static int process_content(struct pw_rtsp_client *client)
55
{
56
- char buf1024;
57
+ uint8_t buf4096;
58
59
while (client->content_length > 0) {
60
const size_t max_recv = SPA_MIN(sizeof(buf), client->content_length);
61
62
return res;
63
}
64
65
+ void *p = pw_array_add(&client->content, res);
66
+ memcpy(p, buf, res);
67
+
68
spa_assert((size_t) res <= client->content_length);
69
client->content_length -= res;
70
}
71
72
int pw_rtsp_client_url_send(struct pw_rtsp_client *client, const char *url,
73
const char *cmd, const struct spa_dict *headers,
74
const char *content_type, const void *content, size_t content_length,
75
- int (*reply) (void *user_data, int status, const struct spa_dict *headers),
76
+ int (*reply) (void *user_data, int status, const struct spa_dict *headers, const struct pw_array *content),
77
void *user_data)
78
{
79
FILE *f;
80
81
int pw_rtsp_client_send(struct pw_rtsp_client *client,
82
const char *cmd, const struct spa_dict *headers,
83
const char *content_type, const char *content,
84
- int (*reply) (void *user_data, int status, const struct spa_dict *headers),
85
+ int (*reply) (void *user_data, int status, const struct spa_dict *headers, const struct pw_array *content),
86
void *user_data)
87
{
88
const size_t content_length = content ? strlen(content) : 0;
89
pipewire-0.3.74.tar.gz/src/modules/module-raop/rtsp-client.h -> pipewire-0.3.76.tar.gz/src/modules/module-raop/rtsp-client.h
Changed
17
1
2
int pw_rtsp_client_url_send(struct pw_rtsp_client *client, const char *url,
3
const char *cmd, const struct spa_dict *headers,
4
const char *content_type, const void *content, size_t content_length,
5
- int (*reply) (void *user_data, int status, const struct spa_dict *headers),
6
+ int (*reply) (void *user_data, int status, const struct spa_dict *headers, const struct pw_array *content),
7
void *user_data);
8
9
int pw_rtsp_client_send(struct pw_rtsp_client *client,
10
const char *cmd, const struct spa_dict *headers,
11
const char *content_type, const char *content,
12
- int (*reply) (void *user_data, int status, const struct spa_dict *headers),
13
+ int (*reply) (void *user_data, int status, const struct spa_dict *headers, const struct pw_array *content),
14
void *user_data);
15
16
17
pipewire-0.3.74.tar.gz/src/modules/module-rt.c -> pipewire-0.3.76.tar.gz/src/modules/module-rt.c
Changed
579
1
2
3
struct spa_thread_utils thread_utils;
4
5
+ pid_t main_pid;
6
+ struct rlimit rl;
7
int nice_level;
8
int rt_prio;
9
rlim_t rt_time_soft;
10
11
const char* object_path;
12
const char* interface;
13
struct pw_rtkit_bus *rtkit_bus;
14
+ struct pw_thread_loop *thread_loop;
15
+ int max_rtprio;
16
+ int min_nice_level;
17
+ rlim_t rttime_max;
18
19
/* These are only for the RTKit implementation to fill in the `thread`
20
* struct. Since there's barely any overhead here we'll do this
21
22
return ret;
23
}
24
25
-static int pw_rtkit_get_max_realtime_priority(struct impl *impl)
26
-{
27
- long long retval;
28
- int err;
29
-
30
- err = rtkit_get_int_property(impl, "MaxRealtimePriority", &retval);
31
- return err < 0 ? err : retval;
32
-}
33
-
34
-static int pw_rtkit_get_min_nice_level(struct impl *impl, int *min_nice_level)
35
-{
36
- long long retval;
37
- int err;
38
-
39
- err = rtkit_get_int_property(impl, "MinNiceLevel", &retval);
40
- if (err >= 0)
41
- *min_nice_level = retval;
42
- return err;
43
-}
44
-
45
-static long long pw_rtkit_get_rttime_usec_max(struct impl *impl)
46
-{
47
- long long retval;
48
- int err;
49
-
50
- err = rtkit_get_int_property(impl, "RTTimeUSecMax", &retval);
51
- return err < 0 ? err : retval;
52
-}
53
-
54
static int pw_rtkit_make_realtime(struct impl *impl, pid_t thread, int priority)
55
{
56
- DBusMessage *m = NULL, *r = NULL;
57
+ DBusMessage *m = NULL;
58
dbus_uint64_t pid;
59
dbus_uint64_t u64;
60
- dbus_uint32_t u32;
61
+ dbus_uint32_t u32, serial;
62
DBusError error;
63
int ret;
64
struct pw_rtkit_bus *connection = impl->rtkit_bus;
65
66
goto finish;
67
}
68
69
- if (!(r = dbus_connection_send_with_reply_and_block(connection->bus, m, -1, &error))) {
70
+ if (!dbus_connection_send(connection->bus, m, &serial)) {
71
ret = translate_error(error.name);
72
goto finish;
73
}
74
-
75
-
76
- if (dbus_set_error_from_message(&error, r)) {
77
- ret = translate_error(error.name);
78
- goto finish;
79
- }
80
-
81
ret = 0;
82
83
finish:
84
85
if (m)
86
dbus_message_unref(m);
87
88
- if (r)
89
- dbus_message_unref(r);
90
-
91
- dbus_error_free(&error);
92
-
93
return ret;
94
}
95
96
+
97
static int pw_rtkit_make_high_priority(struct impl *impl, pid_t thread, int nice_level)
98
{
99
- DBusMessage *m = NULL, *r = NULL;
100
+ DBusMessage *m = NULL;
101
dbus_uint64_t pid;
102
dbus_uint64_t u64;
103
dbus_int32_t s32;
104
- DBusError error;
105
+ dbus_uint32_t serial;
106
int ret;
107
struct pw_rtkit_bus *connection = impl->rtkit_bus;
108
109
- dbus_error_init(&error);
110
-
111
if (thread == 0)
112
thread = _gettid();
113
114
115
ret = -ENOMEM;
116
goto finish;
117
}
118
-
119
-
120
-
121
- if (!(r = dbus_connection_send_with_reply_and_block(connection->bus, m, -1, &error))) {
122
- ret = translate_error(error.name);
123
- goto finish;
124
- }
125
-
126
-
127
- if (dbus_set_error_from_message(&error, r)) {
128
- ret = translate_error(error.name);
129
+ if (!dbus_connection_send(connection->bus, m, &serial)) {
130
+ ret = -EIO;
131
goto finish;
132
}
133
-
134
ret = 0;
135
136
finish:
137
138
if (m)
139
dbus_message_unref(m);
140
141
- if (r)
142
- dbus_message_unref(r);
143
-
144
- dbus_error_free(&error);
145
-
146
return ret;
147
}
148
#endif /* HAVE_DBUS */
149
150
spa_hook_remove(&impl->module_listener);
151
152
#ifdef HAVE_DBUS
153
+ if (impl->thread_loop)
154
+ pw_thread_loop_destroy(impl->thread_loop);
155
if (impl->rtkit_bus)
156
pw_rtkit_bus_free(impl->rtkit_bus);
157
#endif
158
159
return false;
160
}
161
162
-static int sched_set_nice(int nice_level)
163
+static int sched_set_nice(pid_t pid, int nice_level)
164
{
165
- if (setpriority(PRIO_PROCESS, _gettid(), nice_level) == 0)
166
+ if (setpriority(PRIO_PROCESS, pid, nice_level) == 0)
167
return 0;
168
else
169
return -errno;
170
171
172
#ifdef HAVE_DBUS
173
if (impl->use_rtkit) {
174
- int min_nice = nice_level;
175
- pw_rtkit_get_min_nice_level(impl, &min_nice);
176
- if (nice_level < min_nice) {
177
+ if (nice_level < impl->min_nice_level) {
178
pw_log_info("clamped nice level %d to %d",
179
- nice_level, min_nice);
180
- nice_level = min_nice;
181
+ nice_level, impl->min_nice_level);
182
+ nice_level = impl->min_nice_level;
183
}
184
- res = pw_rtkit_make_high_priority(impl, 0, nice_level);
185
+ res = pw_rtkit_make_high_priority(impl, impl->main_pid, nice_level);
186
}
187
- else if (impl->rlimits_enabled)
188
- res = sched_set_nice(nice_level);
189
else
190
- res = -ENOTSUP;
191
-#else
192
+#endif
193
if (impl->rlimits_enabled)
194
- res = sched_set_nice(nice_level);
195
+ res = sched_set_nice(impl->main_pid, nice_level);
196
else
197
res = -ENOTSUP;
198
-#endif
199
200
if (res < 0) {
201
if (warn)
202
pw_log_warn("could not set nice-level to %d: %s",
203
nice_level, spa_strerror(res));
204
+ } else if (res > 0) {
205
+ pw_log_info("main thread setting nice level to %d: %s",
206
+ nice_level, spa_strerror(-res));
207
} else {
208
pw_log_info("main thread nice level set to %d",
209
nice_level);
210
211
212
static int set_rlimit(struct impl *impl)
213
{
214
- struct rlimit rl;
215
int res = 0;
216
217
- spa_zero(rl);
218
- rl.rlim_cur = impl->rt_time_soft;
219
- rl.rlim_max = impl->rt_time_hard;
220
-
221
-#ifdef HAVE_DBUS
222
- if (impl->use_rtkit) {
223
- long long rttime;
224
- rttime = pw_rtkit_get_rttime_usec_max(impl);
225
- if (rttime >= 0) {
226
- if ((rlim_t)rttime < rl.rlim_cur) {
227
- pw_log_debug("clamping rt.time.soft from %llu to %lld because of RTKit",
228
- (long long)rl.rlim_cur, rttime);
229
- }
230
-
231
- rl.rlim_cur = SPA_MIN(rl.rlim_cur, (rlim_t)rttime);
232
- rl.rlim_max = SPA_MIN(rl.rlim_max, (rlim_t)rttime);
233
- }
234
- }
235
-#endif
236
-
237
- if (setrlimit(RLIMIT_RTTIME, &rl) < 0)
238
+ if (setrlimit(RLIMIT_RTTIME, &impl->rl) < 0)
239
res = -errno;
240
241
if (res < 0)
242
pw_log_debug("setrlimit() failed: %s", spa_strerror(res));
243
else
244
pw_log_debug("rt.time.soft:%"PRIi64" rt.time.hard:%"PRIi64,
245
- (int64_t)rl.rlim_cur, (int64_t)rl.rlim_max);
246
+ (int64_t)impl->rl.rlim_cur, (int64_t)impl->rl.rlim_max);
247
248
return res;
249
}
250
251
if (min)
252
*min = 1;
253
if (max) {
254
- if ((*max = pw_rtkit_get_max_realtime_priority(impl)) < 0)
255
- return *max;
256
+ *max = impl->max_rtprio;
257
if (*max < 1)
258
*max = 1;
259
}
260
261
return res;
262
}
263
264
-static pid_t impl_gettid(struct impl *impl, pthread_t pt)
265
-{
266
- struct thread *thr;
267
+struct rt_params {
268
pid_t pid;
269
+ int priority;
270
+};
271
272
- pthread_mutex_lock(&impl->lock);
273
- if ((thr = find_thread_by_pt(impl, pt)) != NULL)
274
- pid = thr->pid;
275
- else
276
- pid = _gettid();
277
- pthread_mutex_unlock(&impl->lock);
278
+static int do_make_realtime(struct spa_loop *loop, bool async, uint32_t seq,
279
+ const void *data, size_t size, void *user_data)
280
+{
281
+ struct impl *impl = user_data;
282
+ const struct rt_params *params = data;
283
+ int err, min, max, priority = params->priority;
284
+ pid_t pid = params->pid;
285
+
286
+ pw_log_debug("rtkit realtime");
287
+
288
+ if ((err = get_rtkit_priority_range(impl, &min, &max)) < 0)
289
+ return err;
290
+
291
+ if (priority < min || priority > max) {
292
+ pw_log_info("clamping requested priority %d for thread %d "
293
+ "between %d and %d", priority, pid, min, max);
294
+ priority = SPA_CLAMP(priority, min, max);
295
+ }
296
+
297
+ if ((err = pw_rtkit_make_realtime(impl, pid, priority)) < 0) {
298
+ pw_log_warn("could not make thread %d realtime using RTKit: %s", pid, spa_strerror(err));
299
+ return err;
300
+ }
301
302
- return pid;
303
+ pw_log_info("acquired realtime priority %d for thread %d using RTKit", priority, pid);
304
+ return 0;
305
}
306
307
static int impl_acquire_rt(void *object, struct spa_thread *thread, int priority)
308
{
309
struct impl *impl = object;
310
struct sched_param sp;
311
- int err;
312
pthread_t pt = (pthread_t)thread;
313
- pid_t pid;
314
+ int res;
315
316
/* See the docstring on `spa_thread_utils_methods::acquire_rt` */
317
if (priority == -1) {
318
priority = impl->rt_prio;
319
}
320
-
321
if (impl->use_rtkit) {
322
- int min, max;
323
-
324
- if ((err = get_rtkit_priority_range(impl, &min, &max)) < 0)
325
- return err;
326
-
327
- pid = impl_gettid(impl, pt);
328
-
329
- if (priority < min || priority > max) {
330
- pw_log_info("clamping requested priority %d for thread %d "
331
- "between %d and %d", priority, pid, min, max);
332
- priority = SPA_CLAMP(priority, min, max);
333
- }
334
+ struct rt_params params;
335
+ struct thread *thr;
336
337
spa_zero(sp);
338
- sp.sched_priority = priority;
339
-
340
if (pthread_setschedparam(pt, SCHED_OTHER | PW_SCHED_RESET_ON_FORK, &sp) == 0) {
341
pw_log_debug("SCHED_OTHER|SCHED_RESET_ON_FORK worked.");
342
}
343
344
- if ((err = pw_rtkit_make_realtime(impl, pid, priority)) < 0) {
345
- pw_log_warn("could not make thread %d realtime using RTKit: %s", pid, spa_strerror(err));
346
- return err;
347
- }
348
+ params.priority = priority;
349
350
- pw_log_info("acquired realtime priority %d for thread %d using RTKit", priority, pid);
351
- return 0;
352
+ pthread_mutex_lock(&impl->lock);
353
+ if ((thr = find_thread_by_pt(impl, pt)) != NULL)
354
+ params.pid = thr->pid;
355
+ else
356
+ params.pid = _gettid();
357
+
358
+ res = pw_loop_invoke(pw_thread_loop_get_loop(impl->thread_loop),
359
+ do_make_realtime, 0, ¶ms, sizeof(params), false, impl);
360
+ pthread_mutex_unlock(&impl->lock);
361
+
362
+ return res;
363
} else {
364
return acquire_rt_sched(thread, priority);
365
}
366
367
368
return 0;
369
}
370
+
371
+static int rtkit_get_bus(struct impl *impl)
372
+{
373
+ int res;
374
+
375
+ pw_log_debug("enter rtkit get bus");
376
+
377
+ /* Checking xdg-desktop-portal. It works fine in all situations. */
378
+ if (impl->rtportal_enabled)
379
+ impl->rtkit_bus = pw_rtkit_bus_get_session();
380
+ else
381
+ pw_log_info("Portal Realtime disabled");
382
+
383
+ if (impl->rtkit_bus != NULL) {
384
+ if (pw_rtkit_check_xdg_portal(impl->rtkit_bus)) {
385
+ impl->service_name = XDG_PORTAL_SERVICE_NAME;
386
+ impl->object_path = XDG_PORTAL_OBJECT_PATH;
387
+ impl->interface = XDG_PORTAL_INTERFACE;
388
+ } else {
389
+ pw_log_info("found session bus but no portal, trying RTKit fallback");
390
+ pw_rtkit_bus_free(impl->rtkit_bus);
391
+ impl->rtkit_bus = NULL;
392
+ }
393
+ }
394
+ /* Failed to get xdg-desktop-portal, try to use rtkit. */
395
+ if (impl->rtkit_bus == NULL) {
396
+ if (impl->rtkit_enabled)
397
+ impl->rtkit_bus = pw_rtkit_bus_get_system();
398
+ else
399
+ pw_log_info("RTkit disabled");
400
+
401
+ if (impl->rtkit_bus != NULL) {
402
+ impl->service_name = RTKIT_SERVICE_NAME;
403
+ impl->object_path = RTKIT_OBJECT_PATH;
404
+ impl->interface = RTKIT_INTERFACE;
405
+ } else {
406
+ res = -errno;
407
+ pw_log_warn("Realtime scheduling disabled: unsufficient realtime privileges, "
408
+ "Portal not found on session bus, and no system bus for RTKit: %m");
409
+ return res;
410
+ }
411
+ }
412
+
413
+ return 0;
414
+}
415
+
416
+static int do_rtkit_setup(struct spa_loop *loop, bool async, uint32_t seq,
417
+ const void *data, size_t size, void *user_data)
418
+{
419
+ struct impl *impl = user_data;
420
+ long long retval;
421
+
422
+ pw_log_debug("enter rtkit setup");
423
+
424
+ /* get some properties */
425
+ if (rtkit_get_int_property(impl, "MaxRealtimePriority", &retval) < 0) {
426
+ retval = 1;
427
+ pw_log_warn("RTKit does not give us MaxRealtimePriority, using %lld", retval);
428
+ }
429
+ impl->max_rtprio = retval;
430
+ if (rtkit_get_int_property(impl, "MinNiceLevel", &retval) < 0) {
431
+ retval = 0;
432
+ pw_log_warn("RTKit does not give us MinNiceLevel, using %lld", retval);
433
+ }
434
+ impl->min_nice_level = retval;
435
+ if (rtkit_get_int_property(impl, "RTTimeUSecMax", &retval) < 0) {
436
+ retval = impl->rl.rlim_cur;
437
+ pw_log_warn("RTKit does not give us RTTimeUSecMax, using %lld", retval);
438
+ }
439
+ impl->rttime_max = retval;
440
+
441
+ /* Retry set_nice with rtkit */
442
+ if (IS_VALID_NICE_LEVEL(impl->nice_level))
443
+ set_nice(impl, impl->nice_level, true);
444
+
445
+ /* Set rlimit with rtkit limits */
446
+ if (impl->rttime_max < impl->rl.rlim_cur) {
447
+ pw_log_debug("clamping rt.time.soft from %llu to %lld because of RTKit",
448
+ (long long)impl->rl.rlim_cur, (long long)impl->rttime_max);
449
+ }
450
+ impl->rl.rlim_cur = SPA_MIN(impl->rl.rlim_cur, impl->rttime_max);
451
+ impl->rl.rlim_max = SPA_MIN(impl->rl.rlim_max, impl->rttime_max);
452
+
453
+ set_rlimit(impl);
454
+
455
+ return 0;
456
+}
457
#endif /* HAVE_DBUS */
458
459
SPA_EXPORT
460
461
impl->rtportal_enabled = pw_properties_get_bool(props, "rtportal.enabled", true);
462
impl->rtkit_enabled = pw_properties_get_bool(props, "rtkit.enabled", true);
463
464
+ impl->rl.rlim_cur = impl->rt_time_soft;
465
+ impl->rl.rlim_max = impl->rt_time_hard;
466
+ impl->main_pid = _gettid();
467
+
468
bool can_use_rtkit = false, use_rtkit = false;
469
470
if (!IS_VALID_NICE_LEVEL(impl->nice_level)) {
471
472
473
if ((res = check_rtkit(impl, context, &can_use_rtkit)) < 0)
474
goto error;
475
+
476
#endif
477
/* If the user has permissions to use regular realtime scheduling, as well as
478
* the nice level we want, then we'll use that instead of RTKit */
479
480
if (set_nice(impl, impl->nice_level, !can_use_rtkit) < 0)
481
use_rtkit = can_use_rtkit;
482
}
483
+ if (!use_rtkit)
484
+ set_rlimit(impl);
485
486
#ifdef HAVE_DBUS
487
impl->use_rtkit = use_rtkit;
488
if (impl->use_rtkit) {
489
- /* Checking xdg-desktop-portal. It works fine in all situations. */
490
- if (impl->rtportal_enabled)
491
- impl->rtkit_bus = pw_rtkit_bus_get_session();
492
- else
493
- pw_log_info("Portal Realtime disabled");
494
- if (impl->rtkit_bus != NULL) {
495
- if (pw_rtkit_check_xdg_portal(impl->rtkit_bus)) {
496
- impl->service_name = XDG_PORTAL_SERVICE_NAME;
497
- impl->object_path = XDG_PORTAL_OBJECT_PATH;
498
- impl->interface = XDG_PORTAL_INTERFACE;
499
- } else {
500
- pw_log_info("found session bus but no portal, trying RTKit fallback");
501
- pw_rtkit_bus_free(impl->rtkit_bus);
502
- impl->rtkit_bus = NULL;
503
- }
504
- }
505
- /* Failed to get xdg-desktop-portal, try to use rtkit. */
506
- if (impl->rtkit_bus == NULL) {
507
- if (impl->rtkit_enabled)
508
- impl->rtkit_bus = pw_rtkit_bus_get_system();
509
- else
510
- pw_log_info("RTkit disabled");
511
-
512
- if (impl->rtkit_bus != NULL) {
513
- impl->service_name = RTKIT_SERVICE_NAME;
514
- impl->object_path = RTKIT_OBJECT_PATH;
515
- impl->interface = RTKIT_INTERFACE;
516
- } else {
517
- res = -errno;
518
- pw_log_warn("Realtime scheduling disabled: unsufficient realtime privileges, "
519
- "Portal not found on session bus, and no system bus for RTKit: %m");
520
- goto error;
521
- }
522
+ struct spa_dict_item items = {
523
+ { "thread-loop.start-signal", "true" }
524
+ };
525
+ if ((res = rtkit_get_bus(impl)) < 0)
526
+ goto error;
527
+
528
+ impl->thread_loop = pw_thread_loop_new("module-rt",
529
+ &SPA_DICT_INIT_ARRAY(items));
530
+ if (impl->thread_loop == NULL) {
531
+ res = -errno;
532
+ goto error;
533
}
534
- /* Retry set_nice with rtkit */
535
- if (IS_VALID_NICE_LEVEL(impl->nice_level))
536
- set_nice(impl, impl->nice_level, true);
537
+ pw_thread_loop_lock(impl->thread_loop);
538
+ pw_thread_loop_start(impl->thread_loop);
539
+ pw_thread_loop_wait(impl->thread_loop);
540
+ pw_thread_loop_unlock(impl->thread_loop);
541
+
542
+ pw_loop_invoke(pw_thread_loop_get_loop(impl->thread_loop),
543
+ do_rtkit_setup, 0, NULL, 0, false, impl);
544
+
545
+ pw_log_debug("initialized using RTKit");
546
+ } else {
547
+ pw_log_debug("initialized using regular realtime scheduling");
548
}
549
+#else
550
+ pw_log_debug("initialized using regular realtime scheduling");
551
#endif
552
- set_rlimit(impl);
553
554
impl->thread_utils.iface = SPA_INTERFACE_INIT(
555
SPA_TYPE_INTERFACE_ThreadUtils,
556
557
pw_impl_module_update_properties(module, &SPA_DICT_INIT_ARRAY(module_props));
558
pw_impl_module_update_properties(module, &props->dict);
559
560
-#ifdef HAVE_DBUS
561
- if (impl->use_rtkit) {
562
- pw_log_debug("initialized using RTKit");
563
- } else {
564
- pw_log_debug("initialized using regular realtime scheduling");
565
- }
566
-#else
567
- pw_log_debug("initialized using regular realtime scheduling");
568
-#endif
569
-
570
goto done;
571
572
error:
573
#ifdef HAVE_DBUS
574
+ if (impl->thread_loop)
575
+ pw_thread_loop_destroy(impl->thread_loop);
576
if (impl->rtkit_bus)
577
pw_rtkit_bus_free(impl->rtkit_bus);
578
#endif
579
pipewire-0.3.76.tar.gz/src/modules/module-vban
Added
2
1
+(directory)
2
pipewire-0.3.76.tar.gz/src/modules/module-vban-recv.c
Added
574
1
2
+/* PipeWire */
3
+/* SPDX-FileCopyrightText: Copyright © 2023 Wim Taymans <wim.taymans@gmail.com> */
4
+/* SPDX-License-Identifier: MIT */
5
+
6
+#include "config.h"
7
+
8
+#include <limits.h>
9
+#include <string.h>
10
+#include <unistd.h>
11
+#include <sys/stat.h>
12
+#include <sys/socket.h>
13
+#include <sys/ioctl.h>
14
+#include <arpa/inet.h>
15
+#include <netinet/in.h>
16
+#include <net/if.h>
17
+#include <ctype.h>
18
+
19
+#include <spa/utils/hook.h>
20
+#include <spa/utils/result.h>
21
+#include <spa/utils/ringbuffer.h>
22
+#include <spa/utils/defs.h>
23
+#include <spa/utils/dll.h>
24
+#include <spa/utils/json.h>
25
+#include <spa/param/audio/format-utils.h>
26
+#include <spa/control/control.h>
27
+#include <spa/debug/types.h>
28
+#include <spa/debug/mem.h>
29
+
30
+#include <pipewire/pipewire.h>
31
+#include <pipewire/impl.h>
32
+
33
+#include <module-vban/stream.h>
34
+
35
+#ifdef __FreeBSD__
36
+#define ifr_ifindex ifr_index
37
+#endif
38
+
39
+/** \page page_module_vban_recv PipeWire Module: VBAN receiver
40
+ *
41
+ * The `vban-recv` module creates a PipeWire source that receives audio
42
+ * and midi VBAN(https://vb-audio.com) packets.
43
+ *
44
+ * ## Module Options
45
+ *
46
+ * Options specific to the behavior of this module
47
+ *
48
+ * - `local.ifname = <str>`: interface name to use
49
+ * - `source.ip = <str>`: the source ip address, default 127.0.0.1
50
+ * - `source.port = <int>`: the source port
51
+ * - `node.always-process = <bool>`: true to receive even when not running
52
+ * - `sess.latency.msec = <str>`: target network latency in milliseconds, default 100
53
+ * - `sess.ignore-ssrc = <bool>`: ignore SSRC, default false
54
+ * - `sess.media = <string>`: the media type audio|midi|opus, default audio
55
+ * - `stream.props = {}`: properties to be passed to the stream
56
+ *
57
+ * ## General options
58
+ *
59
+ * Options with well-known behavior:
60
+ *
61
+ * - \ref PW_KEY_REMOTE_NAME
62
+ * - \ref PW_KEY_AUDIO_FORMAT
63
+ * - \ref PW_KEY_AUDIO_RATE
64
+ * - \ref PW_KEY_AUDIO_CHANNELS
65
+ * - \ref SPA_KEY_AUDIO_POSITION
66
+ * - \ref PW_KEY_MEDIA_NAME
67
+ * - \ref PW_KEY_MEDIA_CLASS
68
+ * - \ref PW_KEY_NODE_NAME
69
+ * - \ref PW_KEY_NODE_DESCRIPTION
70
+ * - \ref PW_KEY_NODE_GROUP
71
+ * - \ref PW_KEY_NODE_LATENCY
72
+ * - \ref PW_KEY_NODE_VIRTUAL
73
+ *
74
+ * ## Example configuration
75
+ *\code{.unparsed}
76
+ * context.modules =
77
+ * { name = libpipewire-module-vban-recv
78
+ * args = {
79
+ * #local.ifname = eth0
80
+ * #source.ip = 127.0.0.1
81
+ * #source.port = 6980
82
+ * sess.latency.msec = 100
83
+ * #sess.ignore-ssrc = false
84
+ * #node.always-process = false
85
+ * #sess.media = "audio"
86
+ * #audio.format = "S16LE"
87
+ * #audio.rate = 44100
88
+ * #audio.channels = 2
89
+ * #audio.position = FL FR
90
+ * stream.props = {
91
+ * #media.class = "Audio/Source"
92
+ * node.name = "vban-receiver"
93
+ * }
94
+ * }
95
+ * }
96
+ *
97
+ *\endcode
98
+ *
99
+ * \since 0.3.76
100
+ */
101
+
102
+#define NAME "vban-recv"
103
+
104
+PW_LOG_TOPIC_STATIC(mod_topic, "mod." NAME);
105
+#define PW_LOG_TOPIC_DEFAULT mod_topic
106
+
107
+#define DEFAULT_CLEANUP_SEC 60
108
+#define DEFAULT_SOURCE_IP "127.0.0.1"
109
+#define DEFAULT_SOURCE_PORT 6980
110
+
111
+#define USAGE "( local.ifname=<local interface name to use> ) " \
112
+ "( source.ip=<source IP address, default:"DEFAULT_SOURCE_IP"> ) " \
113
+ "( source.port=<int, source port, default:"SPA_STRINGIFY(DEFAULT_SOURCE_PORT)"> " \
114
+ "( sess.latency.msec=<target network latency, default "SPA_STRINGIFY(DEFAULT_SESS_LATENCY)"> ) "\
115
+ "( sess.media=<string, the media type audio|midi, default audio> ) " \
116
+ "( audio.format=<format, default:"DEFAULT_FORMAT"> ) " \
117
+ "( audio.rate=<sample rate, default:"SPA_STRINGIFY(DEFAULT_RATE)"> ) " \
118
+ "( audio.channels=<number of channels, default:"SPA_STRINGIFY(DEFAULT_CHANNELS)"> ) " \
119
+ "( audio.position=<channel map, default:"DEFAULT_POSITION"> ) " \
120
+ "( stream.props= { key=value ... } ) "
121
+
122
+static const struct spa_dict_item module_info = {
123
+ { PW_KEY_MODULE_AUTHOR, "Wim Taymans <wim.taymans@gmail.com>" },
124
+ { PW_KEY_MODULE_DESCRIPTION, "VBAN Receiver" },
125
+ { PW_KEY_MODULE_USAGE, USAGE },
126
+ { PW_KEY_MODULE_VERSION, PACKAGE_VERSION },
127
+};
128
+
129
+struct impl {
130
+ struct pw_impl_module *module;
131
+ struct spa_hook module_listener;
132
+ struct pw_properties *props;
133
+ struct pw_context *context;
134
+
135
+ struct pw_loop *loop;
136
+ struct pw_loop *data_loop;
137
+
138
+ struct pw_core *core;
139
+ struct spa_hook core_listener;
140
+ struct spa_hook core_proxy_listener;
141
+ unsigned int do_disconnect:1;
142
+
143
+ char *ifname;
144
+ bool always_process;
145
+ uint32_t cleanup_interval;
146
+
147
+ struct spa_source *timer;
148
+
149
+ struct pw_properties *stream_props;
150
+ struct vban_stream *stream;
151
+
152
+ uint16_t src_port;
153
+ struct sockaddr_storage src_addr;
154
+ socklen_t src_len;
155
+ struct spa_source *source;
156
+
157
+ unsigned receiving:1;
158
+};
159
+
160
+static void
161
+on_vban_io(void *data, int fd, uint32_t mask)
162
+{
163
+ struct impl *impl = data;
164
+ ssize_t len;
165
+ uint8_t buffer2048;
166
+
167
+ if (mask & SPA_IO_IN) {
168
+ if ((len = recv(fd, buffer, sizeof(buffer), 0)) < 0)
169
+ goto receive_error;
170
+
171
+ if (len < 12)
172
+ goto short_packet;
173
+
174
+ if (SPA_LIKELY(impl->stream))
175
+ vban_stream_receive_packet(impl->stream, buffer, len);
176
+
177
+ impl->receiving = true;
178
+ }
179
+ return;
180
+
181
+receive_error:
182
+ pw_log_warn("recv error: %m");
183
+ return;
184
+short_packet:
185
+ pw_log_warn("short packet received");
186
+ return;
187
+}
188
+
189
+static int parse_address(const char *address, uint16_t port,
190
+ struct sockaddr_storage *addr, socklen_t *len)
191
+{
192
+ struct sockaddr_in *sa4 = (struct sockaddr_in*)addr;
193
+ struct sockaddr_in6 *sa6 = (struct sockaddr_in6*)addr;
194
+
195
+ if (inet_pton(AF_INET, address, &sa4->sin_addr) > 0) {
196
+ sa4->sin_family = AF_INET;
197
+ sa4->sin_port = htons(port);
198
+ *len = sizeof(*sa4);
199
+ } else if (inet_pton(AF_INET6, address, &sa6->sin6_addr) > 0) {
200
+ sa6->sin6_family = AF_INET6;
201
+ sa6->sin6_port = htons(port);
202
+ *len = sizeof(*sa6);
203
+ } else
204
+ return -EINVAL;
205
+
206
+ return 0;
207
+}
208
+
209
+static int make_socket(const struct sockaddr* sa, socklen_t salen, char *ifname)
210
+{
211
+ int af, fd, val, res;
212
+ struct ifreq req;
213
+
214
+ af = sa->sa_family;
215
+ if ((fd = socket(af, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0)) < 0) {
216
+ pw_log_error("socket failed: %m");
217
+ return -errno;
218
+ }
219
+#ifdef SO_TIMESTAMP
220
+ val = 1;
221
+ if (setsockopt(fd, SOL_SOCKET, SO_TIMESTAMP, &val, sizeof(val)) < 0) {
222
+ res = -errno;
223
+ pw_log_error("setsockopt failed: %m");
224
+ goto error;
225
+ }
226
+#endif
227
+ val = 1;
228
+ if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val)) < 0) {
229
+ res = -errno;
230
+ pw_log_error("setsockopt failed: %m");
231
+ goto error;
232
+ }
233
+
234
+ spa_zero(req);
235
+ if (ifname) {
236
+ snprintf(req.ifr_name, sizeof(req.ifr_name), "%s", ifname);
237
+ res = ioctl(fd, SIOCGIFINDEX, &req);
238
+ if (res < 0)
239
+ pw_log_warn("SIOCGIFINDEX %s failed: %m", ifname);
240
+ }
241
+ res = 0;
242
+ if (af == AF_INET) {
243
+ static const uint32_t ipv4_mcast_mask = 0xe0000000;
244
+ struct sockaddr_in *sa4 = (struct sockaddr_in*)sa;
245
+ if ((ntohl(sa4->sin_addr.s_addr) & ipv4_mcast_mask) == ipv4_mcast_mask) {
246
+ struct ip_mreqn mr4;
247
+ memset(&mr4, 0, sizeof(mr4));
248
+ mr4.imr_multiaddr = sa4->sin_addr;
249
+ mr4.imr_ifindex = req.ifr_ifindex;
250
+ res = setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mr4, sizeof(mr4));
251
+ } else {
252
+ sa4->sin_addr.s_addr = INADDR_ANY;
253
+ }
254
+ } else if (af == AF_INET6) {
255
+ struct sockaddr_in6 *sa6 = (struct sockaddr_in6*)sa;
256
+ if (sa6->sin6_addr.s6_addr0 == 0xff) {
257
+ struct ipv6_mreq mr6;
258
+ memset(&mr6, 0, sizeof(mr6));
259
+ mr6.ipv6mr_multiaddr = sa6->sin6_addr;
260
+ mr6.ipv6mr_interface = req.ifr_ifindex;
261
+ res = setsockopt(fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mr6, sizeof(mr6));
262
+ } else {
263
+ sa6->sin6_addr = in6addr_any;
264
+ }
265
+ } else {
266
+ res = -EINVAL;
267
+ goto error;
268
+ }
269
+
270
+ if (res < 0) {
271
+ res = -errno;
272
+ pw_log_error("join mcast failed: %m");
273
+ goto error;
274
+ }
275
+
276
+ if (bind(fd, sa, salen) < 0) {
277
+ res = -errno;
278
+ pw_log_error("bind() failed: %m");
279
+ goto error;
280
+ }
281
+ return fd;
282
+error:
283
+ close(fd);
284
+ return res;
285
+}
286
+
287
+static int stream_start(struct impl *impl)
288
+{
289
+ int fd;
290
+
291
+ if (impl->source != NULL)
292
+ return 0;
293
+
294
+ pw_log_info("starting VBAN listener");
295
+
296
+ if ((fd = make_socket((const struct sockaddr *)&impl->src_addr,
297
+ impl->src_len, impl->ifname)) < 0) {
298
+ pw_log_error("failed to create socket: %m");
299
+ return fd;
300
+ }
301
+
302
+ impl->source = pw_loop_add_io(impl->data_loop, fd,
303
+ SPA_IO_IN, true, on_vban_io, impl);
304
+ if (impl->source == NULL) {
305
+ pw_log_error("can't create io source: %m");
306
+ close(fd);
307
+ return -errno;
308
+ }
309
+ return 0;
310
+}
311
+
312
+static void stream_stop(struct impl *impl)
313
+{
314
+ if (!impl->source)
315
+ return;
316
+
317
+ pw_log_info("stopping VBAN listener");
318
+
319
+ pw_loop_destroy_source(impl->data_loop, impl->source);
320
+ impl->source = NULL;
321
+}
322
+
323
+static void stream_destroy(void *d)
324
+{
325
+ struct impl *impl = d;
326
+ impl->stream = NULL;
327
+}
328
+
329
+static void stream_state_changed(void *data, bool started, const char *error)
330
+{
331
+ struct impl *impl = data;
332
+
333
+ if (error) {
334
+ pw_log_error("stream error: %s", error);
335
+ pw_impl_module_schedule_destroy(impl->module);
336
+ } else if (started) {
337
+ if ((errno = -stream_start(impl)) < 0)
338
+ pw_log_error("failed to start VBAN stream: %m");
339
+ } else {
340
+ if (!impl->always_process)
341
+ stream_stop(impl);
342
+ }
343
+}
344
+
345
+static const struct vban_stream_events stream_events = {
346
+ VBAN_VERSION_STREAM_EVENTS,
347
+ .destroy = stream_destroy,
348
+ .state_changed = stream_state_changed,
349
+};
350
+
351
+static void on_timer_event(void *data, uint64_t expirations)
352
+{
353
+ struct impl *impl = data;
354
+
355
+ if (!impl->receiving) {
356
+ pw_log_info("timeout, inactive VBAN source");
357
+ //pw_impl_module_schedule_destroy(impl->module);
358
+ } else {
359
+ pw_log_debug("timeout, keeping active VBAN source");
360
+ }
361
+ impl->receiving = false;
362
+}
363
+
364
+static void core_destroy(void *d)
365
+{
366
+ struct impl *impl = d;
367
+ spa_hook_remove(&impl->core_listener);
368
+ impl->core = NULL;
369
+ pw_impl_module_schedule_destroy(impl->module);
370
+}
371
+
372
+static const struct pw_proxy_events core_proxy_events = {
373
+ .destroy = core_destroy,
374
+};
375
+
376
+static void impl_destroy(struct impl *impl)
377
+{
378
+ if (impl->stream)
379
+ vban_stream_destroy(impl->stream);
380
+ if (impl->source)
381
+ pw_loop_destroy_source(impl->data_loop, impl->source);
382
+
383
+ if (impl->core && impl->do_disconnect)
384
+ pw_core_disconnect(impl->core);
385
+
386
+ if (impl->timer)
387
+ pw_loop_destroy_source(impl->loop, impl->timer);
388
+
389
+ pw_properties_free(impl->stream_props);
390
+ pw_properties_free(impl->props);
391
+
392
+ free(impl->ifname);
393
+ free(impl);
394
+}
395
+
396
+static void module_destroy(void *d)
397
+{
398
+ struct impl *impl = d;
399
+ spa_hook_remove(&impl->module_listener);
400
+ impl_destroy(impl);
401
+}
402
+
403
+static const struct pw_impl_module_events module_events = {
404
+ PW_VERSION_IMPL_MODULE_EVENTS,
405
+ .destroy = module_destroy,
406
+};
407
+
408
+static void on_core_error(void *d, uint32_t id, int seq, int res, const char *message)
409
+{
410
+ struct impl *impl = d;
411
+
412
+ pw_log_error("error id:%u seq:%d res:%d (%s): %s",
413
+ id, seq, res, spa_strerror(res), message);
414
+
415
+ if (id == PW_ID_CORE && res == -EPIPE)
416
+ pw_impl_module_schedule_destroy(impl->module);
417
+}
418
+
419
+static const struct pw_core_events core_events = {
420
+ PW_VERSION_CORE_EVENTS,
421
+ .error = on_core_error,
422
+};
423
+
424
+static void copy_props(struct impl *impl, struct pw_properties *props, const char *key)
425
+{
426
+ const char *str;
427
+ if ((str = pw_properties_get(props, key)) != NULL) {
428
+ if (pw_properties_get(impl->stream_props, key) == NULL)
429
+ pw_properties_set(impl->stream_props, key, str);
430
+ }
431
+}
432
+
433
+SPA_EXPORT
434
+int pipewire__module_init(struct pw_impl_module *module, const char *args)
435
+{
436
+ struct pw_context *context = pw_impl_module_get_context(module);
437
+ struct impl *impl;
438
+ const char *str, *sess_name;
439
+ struct timespec value, interval;
440
+ struct pw_properties *props, *stream_props;
441
+ int res = 0;
442
+
443
+ PW_LOG_TOPIC_INIT(mod_topic);
444
+
445
+ impl = calloc(1, sizeof(struct impl));
446
+ if (impl == NULL)
447
+ return -errno;
448
+
449
+ if (args == NULL)
450
+ args = "";
451
+
452
+ props = impl->props = pw_properties_new_string(args);
453
+ stream_props = impl->stream_props = pw_properties_new(NULL, NULL);
454
+ if (props == NULL || stream_props == NULL) {
455
+ res = -errno;
456
+ pw_log_error( "can't create properties: %m");
457
+ goto out;
458
+ }
459
+
460
+ impl->module = module;
461
+ impl->context = context;
462
+ impl->loop = pw_context_get_main_loop(context);
463
+ impl->data_loop = pw_data_loop_get_loop(pw_context_get_data_loop(context));
464
+
465
+ if ((sess_name = pw_properties_get(props, "sess.name")) == NULL)
466
+ sess_name = pw_get_host_name();
467
+
468
+ if (pw_properties_get(props, PW_KEY_NODE_NAME) == NULL)
469
+ pw_properties_setf(props, PW_KEY_NODE_NAME, "vban_session.%s", sess_name);
470
+ if (pw_properties_get(props, PW_KEY_NODE_DESCRIPTION) == NULL)
471
+ pw_properties_setf(props, PW_KEY_NODE_DESCRIPTION, "%s", sess_name);
472
+ if (pw_properties_get(props, PW_KEY_MEDIA_NAME) == NULL)
473
+ pw_properties_setf(props, PW_KEY_MEDIA_NAME, "VBAN Session with %s",
474
+ sess_name);
475
+
476
+ if ((str = pw_properties_get(props, "stream.props")) != NULL)
477
+ pw_properties_update_string(stream_props, str, strlen(str));
478
+
479
+ copy_props(impl, props, PW_KEY_AUDIO_FORMAT);
480
+ copy_props(impl, props, PW_KEY_AUDIO_RATE);
481
+ copy_props(impl, props, PW_KEY_AUDIO_CHANNELS);
482
+ copy_props(impl, props, SPA_KEY_AUDIO_POSITION);
483
+ copy_props(impl, props, PW_KEY_NODE_NAME);
484
+ copy_props(impl, props, PW_KEY_NODE_DESCRIPTION);
485
+ copy_props(impl, props, PW_KEY_NODE_GROUP);
486
+ copy_props(impl, props, PW_KEY_NODE_LATENCY);
487
+ copy_props(impl, props, PW_KEY_NODE_VIRTUAL);
488
+ copy_props(impl, props, PW_KEY_NODE_CHANNELNAMES);
489
+ copy_props(impl, props, PW_KEY_MEDIA_NAME);
490
+ copy_props(impl, props, PW_KEY_MEDIA_CLASS);
491
+ copy_props(impl, props, "net.mtu");
492
+ copy_props(impl, props, "sess.media");
493
+ copy_props(impl, props, "sess.name");
494
+ copy_props(impl, props, "sess.min-ptime");
495
+ copy_props(impl, props, "sess.max-ptime");
496
+ copy_props(impl, props, "sess.latency.msec");
497
+
498
+ str = pw_properties_get(props, "local.ifname");
499
+ impl->ifname = str ? strdup(str) : NULL;
500
+
501
+ impl->src_port = pw_properties_get_uint32(props, "source.port", DEFAULT_SOURCE_PORT);
502
+ if (impl->src_port == 0) {
503
+ pw_log_error("invalid source.port");
504
+ goto out;
505
+ }
506
+ if ((str = pw_properties_get(props, "source.ip")) == NULL)
507
+ str = DEFAULT_SOURCE_IP;
508
+ if ((res = parse_address(str, impl->src_port, &impl->src_addr, &impl->src_len)) < 0) {
509
+ pw_log_error("invalid source.ip %s: %s", str, spa_strerror(res));
510
+ goto out;
511
+ }
512
+
513
+ impl->always_process = pw_properties_get_bool(stream_props,
514
+ PW_KEY_NODE_ALWAYS_PROCESS, true);
515
+
516
+ impl->cleanup_interval = pw_properties_get_uint32(props,
517
+ "cleanup.sec", DEFAULT_CLEANUP_SEC);
518
+
519
+ impl->core = pw_context_get_object(impl->context, PW_TYPE_INTERFACE_Core);
520
+ if (impl->core == NULL) {
521
+ str = pw_properties_get(props, PW_KEY_REMOTE_NAME);
522
+ impl->core = pw_context_connect(impl->context,
523
+ pw_properties_new(
524
+ PW_KEY_REMOTE_NAME, str,
525
+ NULL),
526
+ 0);
527
+ impl->do_disconnect = true;
528
+ }
529
+ if (impl->core == NULL) {
530
+ res = -errno;
531
+ pw_log_error("can't connect: %m");
532
+ goto out;
533
+ }
534
+
535
+ pw_proxy_add_listener((struct pw_proxy*)impl->core,
536
+ &impl->core_proxy_listener,
537
+ &core_proxy_events, impl);
538
+ pw_core_add_listener(impl->core,
539
+ &impl->core_listener,
540
+ &core_events, impl);
541
+
542
+ impl->timer = pw_loop_add_timer(impl->loop, on_timer_event, impl);
543
+ if (impl->timer == NULL) {
544
+ res = -errno;
545
+ pw_log_error("can't create timer source: %m");
546
+ goto out;
547
+ }
548
+ value.tv_sec = impl->cleanup_interval;
549
+ value.tv_nsec = 0;
550
+ interval.tv_sec = impl->cleanup_interval;
551
+ interval.tv_nsec = 0;
552
+ pw_loop_update_timer(impl->loop, impl->timer, &value, &interval, false);
553
+
554
+ impl->stream = vban_stream_new(impl->core,
555
+ PW_DIRECTION_OUTPUT, pw_properties_copy(stream_props),
556
+ &stream_events, impl);
557
+ if (impl->stream == NULL) {
558
+ res = -errno;
559
+ pw_log_error("can't create stream: %m");
560
+ goto out;
561
+ }
562
+
563
+ pw_impl_module_add_listener(module, &impl->module_listener, &module_events, impl);
564
+
565
+ pw_impl_module_update_properties(module, &SPA_DICT_INIT_ARRAY(module_info));
566
+
567
+ pw_log_info("Successfully loaded module-vban-recv");
568
+
569
+ return 0;
570
+out:
571
+ impl_destroy(impl);
572
+ return res;
573
+}
574
pipewire-0.3.76.tar.gz/src/modules/module-vban-send.c
Added
536
1
2
+/* PipeWire */
3
+/* SPDX-FileCopyrightText: Copyright © 2023 Wim Taymans <wim.taymans@gmail.com> */
4
+/* SPDX-License-Identifier: MIT */
5
+
6
+#include "config.h"
7
+
8
+#include <limits.h>
9
+#include <unistd.h>
10
+#include <sys/stat.h>
11
+#include <sys/socket.h>
12
+#include <sys/ioctl.h>
13
+#include <arpa/inet.h>
14
+#include <netinet/ip.h>
15
+#include <netinet/in.h>
16
+#include <net/if.h>
17
+#include <ctype.h>
18
+
19
+#include <spa/utils/hook.h>
20
+#include <spa/utils/result.h>
21
+#include <spa/utils/ringbuffer.h>
22
+#include <spa/utils/json.h>
23
+#include <spa/param/audio/format-utils.h>
24
+#include <spa/debug/types.h>
25
+
26
+#include <pipewire/pipewire.h>
27
+#include <pipewire/impl.h>
28
+
29
+#include <module-vban/stream.h>
30
+
31
+#ifndef IPTOS_DSCP
32
+#define IPTOS_DSCP_MASK 0xfc
33
+#define IPTOS_DSCP(x) ((x) & IPTOS_DSCP_MASK)
34
+#endif
35
+
36
+/** \page page_module_vban_send PipeWire Module: VBAN sender
37
+ *
38
+ * The `vban-send` module creates a PipeWire sink that sends
39
+ * audio and midi VBAN(https://vb-audio.com) packets.
40
+ *
41
+ * ## Module Options
42
+ *
43
+ * Options specific to the behavior of this module
44
+ *
45
+ * - `source.ip =<str>`: source IP address, default "0.0.0.0"
46
+ * - `destination.ip =<str>`: destination IP address, default "127.0.0.1"
47
+ * - `destination.port =<int>`: destination port, default 6980
48
+ * - `local.ifname = <str>`: interface name to use
49
+ * - `net.mtu = <int>`: MTU to use, default 1500
50
+ * - `net.ttl = <int>`: TTL to use, default 1
51
+ * - `net.loop = <bool>`: loopback multicast, default false
52
+ * - `sess.min-ptime = <int>`: minimum packet time in milliseconds, default 2
53
+ * - `sess.max-ptime = <int>`: maximum packet time in milliseconds, default 20
54
+ * - `sess.name = <str>`: a session name
55
+ * - `sess.media = <string>`: the media type audio|midi, default audio
56
+ * - `stream.props = {}`: properties to be passed to the stream
57
+ *
58
+ * ## General options
59
+ *
60
+ * Options with well-known behavior:
61
+ *
62
+ * - \ref PW_KEY_REMOTE_NAME
63
+ * - \ref PW_KEY_AUDIO_FORMAT
64
+ * - \ref PW_KEY_AUDIO_RATE
65
+ * - \ref PW_KEY_AUDIO_CHANNELS
66
+ * - \ref SPA_KEY_AUDIO_POSITION
67
+ * - \ref PW_KEY_NODE_NAME
68
+ * - \ref PW_KEY_NODE_DESCRIPTION
69
+ * - \ref PW_KEY_MEDIA_NAME
70
+ * - \ref PW_KEY_NODE_GROUP
71
+ * - \ref PW_KEY_NODE_LATENCY
72
+ * - \ref PW_KEY_NODE_VIRTUAL
73
+ * - \ref PW_KEY_MEDIA_CLASS
74
+ *
75
+ * ## Example configuration
76
+ *\code{.unparsed}
77
+ * context.modules =
78
+ * { name = libpipewire-module-vban-send
79
+ * args = {
80
+ * #local.ifname = "eth0"
81
+ * #source.ip = "0.0.0.0"
82
+ * #destination.ip = "127.0.0.1"
83
+ * #destination.port = 6980
84
+ * #net.mtu = 1500
85
+ * #net.ttl = 1
86
+ * #net.loop = false
87
+ * #sess.min-ptime = 2
88
+ * #sess.max-ptime = 20
89
+ * #sess.name = "PipeWire VBAN stream"
90
+ * #sess.media = "audio"
91
+ * #audio.format = "S16LE"
92
+ * #audio.rate = 44100
93
+ * #audio.channels = 2
94
+ * #audio.position = FL FR
95
+ * stream.props = {
96
+ * node.name = "vban-sender"
97
+ * }
98
+ * }
99
+ *}
100
+ *
101
+ *\endcode
102
+ *
103
+ * \since 0.3.76
104
+ */
105
+
106
+#define NAME "vban-send"
107
+
108
+PW_LOG_TOPIC_STATIC(mod_topic, "mod." NAME);
109
+#define PW_LOG_TOPIC_DEFAULT mod_topic
110
+
111
+#define DEFAULT_PORT 6980
112
+#define DEFAULT_SOURCE_IP "0.0.0.0"
113
+#define DEFAULT_DESTINATION_IP "127.0.0.1"
114
+#define DEFAULT_TTL 1
115
+#define DEFAULT_LOOP false
116
+#define DEFAULT_DSCP 34 /* Default to AES-67 AF41 (34) */
117
+
118
+#define USAGE "( source.ip=<source IP address, default:"DEFAULT_SOURCE_IP"> ) " \
119
+ "( destination.ip=<destination IP address, default:"DEFAULT_DESTINATION_IP"> ) " \
120
+ "( destination.port=<int, default:"SPA_STRINGIFY(DEFAULT_PORT)"> ) " \
121
+ "( local.ifname=<local interface name to use> ) " \
122
+ "( net.mtu=<desired MTU, default:"SPA_STRINGIFY(DEFAULT_MTU)"> ) " \
123
+ "( net.ttl=<desired TTL, default:"SPA_STRINGIFY(DEFAULT_TTL)"> ) " \
124
+ "( net.loop=<desired loopback, default:"SPA_STRINGIFY(DEFAULT_LOOP)"> ) " \
125
+ "( net.dscp=<desired DSCP, default:"SPA_STRINGIFY(DEFAULT_DSCP)"> ) " \
126
+ "( sess.name=<a name for the session> ) " \
127
+ "( sess.min-ptime=<minimum packet time in milliseconds, default:2> ) " \
128
+ "( sess.max-ptime=<maximum packet time in milliseconds, default:20> ) " \
129
+ "( sess.media=<string, the media type audio|midi, default audio> ) " \
130
+ "( audio.format=<format, default:"DEFAULT_FORMAT"> ) " \
131
+ "( audio.rate=<sample rate, default:"SPA_STRINGIFY(DEFAULT_RATE)"> ) " \
132
+ "( audio.channels=<number of channels, default:"SPA_STRINGIFY(DEFAULT_CHANNELS)"> ) " \
133
+ "( audio.position=<channel map, default:"DEFAULT_POSITION"> ) " \
134
+ "( stream.props= { key=value ... } ) "
135
+
136
+static const struct spa_dict_item module_info = {
137
+ { PW_KEY_MODULE_AUTHOR, "Wim Taymans <wim.taymans@gmail.com>" },
138
+ { PW_KEY_MODULE_DESCRIPTION, "VBAN Sender" },
139
+ { PW_KEY_MODULE_USAGE, USAGE },
140
+ { PW_KEY_MODULE_VERSION, PACKAGE_VERSION },
141
+};
142
+
143
+struct impl {
144
+ struct pw_context *context;
145
+
146
+ struct pw_impl_module *module;
147
+ struct spa_hook module_listener;
148
+ struct pw_properties *props;
149
+
150
+ struct pw_loop *loop;
151
+
152
+ struct pw_core *core;
153
+ struct spa_hook core_listener;
154
+ struct spa_hook core_proxy_listener;
155
+
156
+ struct pw_properties *stream_props;
157
+ struct vban_stream *stream;
158
+
159
+ unsigned int do_disconnect:1;
160
+
161
+ char *ifname;
162
+ char *session_name;
163
+ uint32_t ttl;
164
+ bool mcast_loop;
165
+ uint32_t dscp;
166
+
167
+ struct sockaddr_storage src_addr;
168
+ socklen_t src_len;
169
+
170
+ uint16_t dst_port;
171
+ struct sockaddr_storage dst_addr;
172
+ socklen_t dst_len;
173
+
174
+ int vban_fd;
175
+};
176
+
177
+static void stream_destroy(void *d)
178
+{
179
+ struct impl *impl = d;
180
+ impl->stream = NULL;
181
+}
182
+
183
+static void stream_send_packet(void *data, struct iovec *iov, size_t iovlen)
184
+{
185
+ struct impl *impl = data;
186
+ struct msghdr msg;
187
+ ssize_t n;
188
+
189
+ spa_zero(msg);
190
+ msg.msg_iov = iov;
191
+ msg.msg_iovlen = iovlen;
192
+ msg.msg_control = NULL;
193
+ msg.msg_controllen = 0;
194
+ msg.msg_flags = 0;
195
+
196
+ n = sendmsg(impl->vban_fd, &msg, MSG_NOSIGNAL);
197
+ if (n < 0)
198
+ pw_log_debug("sendmsg() failed: %m");
199
+}
200
+
201
+static void stream_state_changed(void *data, bool started, const char *error)
202
+{
203
+ struct impl *impl = data;
204
+
205
+ if (error) {
206
+ pw_log_error("stream error: %s", error);
207
+ pw_impl_module_schedule_destroy(impl->module);
208
+ }
209
+}
210
+
211
+static const struct vban_stream_events stream_events = {
212
+ VBAN_VERSION_STREAM_EVENTS,
213
+ .destroy = stream_destroy,
214
+ .state_changed = stream_state_changed,
215
+ .send_packet = stream_send_packet,
216
+};
217
+
218
+static int parse_address(const char *address, uint16_t port,
219
+ struct sockaddr_storage *addr, socklen_t *len)
220
+{
221
+ struct sockaddr_in *sa4 = (struct sockaddr_in*)addr;
222
+ struct sockaddr_in6 *sa6 = (struct sockaddr_in6*)addr;
223
+
224
+ if (inet_pton(AF_INET, address, &sa4->sin_addr) > 0) {
225
+ sa4->sin_family = AF_INET;
226
+ sa4->sin_port = htons(port);
227
+ *len = sizeof(*sa4);
228
+ } else if (inet_pton(AF_INET6, address, &sa6->sin6_addr) > 0) {
229
+ sa6->sin6_family = AF_INET6;
230
+ sa6->sin6_port = htons(port);
231
+ *len = sizeof(*sa6);
232
+ } else
233
+ return -EINVAL;
234
+
235
+ return 0;
236
+}
237
+
238
+static bool is_multicast(struct sockaddr *sa, socklen_t salen)
239
+{
240
+ if (sa->sa_family == AF_INET) {
241
+ static const uint32_t ipv4_mcast_mask = 0xe0000000;
242
+ struct sockaddr_in *sa4 = (struct sockaddr_in*)sa;
243
+ return (ntohl(sa4->sin_addr.s_addr) & ipv4_mcast_mask) == ipv4_mcast_mask;
244
+ } else if (sa->sa_family == AF_INET6) {
245
+ struct sockaddr_in6 *sa6 = (struct sockaddr_in6*)sa;
246
+ return sa6->sin6_addr.s6_addr0 == 0xff;
247
+ }
248
+ return false;
249
+}
250
+
251
+static int make_socket(struct sockaddr_storage *src, socklen_t src_len,
252
+ struct sockaddr_storage *dst, socklen_t dst_len,
253
+ bool loop, int ttl, int dscp)
254
+{
255
+ int af, fd, val, res;
256
+
257
+ af = src->ss_family;
258
+ if ((fd = socket(af, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0)) < 0) {
259
+ pw_log_error("socket failed: %m");
260
+ return -errno;
261
+ }
262
+ if (bind(fd, (struct sockaddr*)src, src_len) < 0) {
263
+ res = -errno;
264
+ pw_log_error("bind() failed: %m");
265
+ goto error;
266
+ }
267
+ if (connect(fd, (struct sockaddr*)dst, dst_len) < 0) {
268
+ res = -errno;
269
+ pw_log_error("connect() failed: %m");
270
+ goto error;
271
+ }
272
+ if (is_multicast((struct sockaddr*)dst, dst_len)) {
273
+ val = loop;
274
+ if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP, &val, sizeof(val)) < 0)
275
+ pw_log_warn("setsockopt(IP_MULTICAST_LOOP) failed: %m");
276
+
277
+ val = ttl;
278
+ if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL, &val, sizeof(val)) < 0)
279
+ pw_log_warn("setsockopt(IP_MULTICAST_TTL) failed: %m");
280
+ }
281
+#ifdef SO_PRIORITY
282
+ val = 6;
283
+ if (setsockopt(fd, SOL_SOCKET, SO_PRIORITY, &val, sizeof(val)) < 0)
284
+ pw_log_warn("setsockopt(SO_PRIORITY) failed: %m");
285
+#endif
286
+ if (dscp > 0) {
287
+ val = IPTOS_DSCP(dscp << 2);
288
+ if (setsockopt(fd, IPPROTO_IP, IP_TOS, &val, sizeof(val)) < 0)
289
+ pw_log_warn("setsockopt(IP_TOS) failed: %m");
290
+ }
291
+
292
+
293
+ return fd;
294
+error:
295
+ close(fd);
296
+ return res;
297
+}
298
+
299
+static int get_ip(const struct sockaddr_storage *sa, char *ip, size_t len)
300
+{
301
+ if (sa->ss_family == AF_INET) {
302
+ struct sockaddr_in *in = (struct sockaddr_in*)sa;
303
+ inet_ntop(sa->ss_family, &in->sin_addr, ip, len);
304
+ } else if (sa->ss_family == AF_INET6) {
305
+ struct sockaddr_in6 *in = (struct sockaddr_in6*)sa;
306
+ inet_ntop(sa->ss_family, &in->sin6_addr, ip, len);
307
+ } else
308
+ return -EIO;
309
+ return 0;
310
+}
311
+
312
+static void core_destroy(void *d)
313
+{
314
+ struct impl *impl = d;
315
+ spa_hook_remove(&impl->core_listener);
316
+ impl->core = NULL;
317
+ pw_impl_module_schedule_destroy(impl->module);
318
+}
319
+
320
+static const struct pw_proxy_events core_proxy_events = {
321
+ .destroy = core_destroy,
322
+};
323
+
324
+static void impl_destroy(struct impl *impl)
325
+{
326
+ if (impl->stream)
327
+ vban_stream_destroy(impl->stream);
328
+
329
+ if (impl->core && impl->do_disconnect)
330
+ pw_core_disconnect(impl->core);
331
+
332
+ if (impl->vban_fd != -1)
333
+ close(impl->vban_fd);
334
+
335
+ pw_properties_free(impl->stream_props);
336
+ pw_properties_free(impl->props);
337
+
338
+ free(impl->ifname);
339
+ free(impl->session_name);
340
+ free(impl);
341
+}
342
+
343
+static void module_destroy(void *d)
344
+{
345
+ struct impl *impl = d;
346
+ spa_hook_remove(&impl->module_listener);
347
+ impl_destroy(impl);
348
+}
349
+
350
+static const struct pw_impl_module_events module_events = {
351
+ PW_VERSION_IMPL_MODULE_EVENTS,
352
+ .destroy = module_destroy,
353
+};
354
+
355
+static void on_core_error(void *d, uint32_t id, int seq, int res, const char *message)
356
+{
357
+ struct impl *impl = d;
358
+
359
+ pw_log_error("error id:%u seq:%d res:%d (%s): %s",
360
+ id, seq, res, spa_strerror(res), message);
361
+
362
+ if (id == PW_ID_CORE && res == -EPIPE)
363
+ pw_impl_module_schedule_destroy(impl->module);
364
+}
365
+
366
+static const struct pw_core_events core_events = {
367
+ PW_VERSION_CORE_EVENTS,
368
+ .error = on_core_error,
369
+};
370
+
371
+static void copy_props(struct impl *impl, struct pw_properties *props, const char *key)
372
+{
373
+ const char *str;
374
+ if ((str = pw_properties_get(props, key)) != NULL) {
375
+ if (pw_properties_get(impl->stream_props, key) == NULL)
376
+ pw_properties_set(impl->stream_props, key, str);
377
+ }
378
+}
379
+
380
+SPA_EXPORT
381
+int pipewire__module_init(struct pw_impl_module *module, const char *args)
382
+{
383
+ struct pw_context *context = pw_impl_module_get_context(module);
384
+ struct impl *impl;
385
+ struct pw_properties *props = NULL, *stream_props = NULL;
386
+ char addr64;
387
+ const char *str, *sess_name;
388
+ int res = 0;
389
+
390
+ PW_LOG_TOPIC_INIT(mod_topic);
391
+
392
+ impl = calloc(1, sizeof(struct impl));
393
+ if (impl == NULL)
394
+ return -errno;
395
+
396
+ impl->vban_fd = -1;
397
+
398
+ if (args == NULL)
399
+ args = "";
400
+
401
+ props = pw_properties_new_string(args);
402
+ if (props == NULL) {
403
+ res = -errno;
404
+ pw_log_error( "can't create properties: %m");
405
+ goto out;
406
+ }
407
+ impl->props = props;
408
+
409
+ stream_props = pw_properties_new(NULL, NULL);
410
+ if (stream_props == NULL) {
411
+ res = -errno;
412
+ pw_log_error( "can't create properties: %m");
413
+ goto out;
414
+ }
415
+ impl->stream_props = stream_props;
416
+
417
+ impl->module = module;
418
+ impl->context = context;
419
+ impl->loop = pw_context_get_main_loop(context);
420
+
421
+ if ((sess_name = pw_properties_get(props, "sess.name")) == NULL)
422
+ sess_name = pw_get_host_name();
423
+
424
+ if (pw_properties_get(props, PW_KEY_NODE_NAME) == NULL)
425
+ pw_properties_setf(props, PW_KEY_NODE_NAME, "vban_session.%s", sess_name);
426
+ if (pw_properties_get(props, PW_KEY_NODE_DESCRIPTION) == NULL)
427
+ pw_properties_setf(props, PW_KEY_NODE_DESCRIPTION, "%s", sess_name);
428
+ if (pw_properties_get(props, PW_KEY_MEDIA_NAME) == NULL)
429
+ pw_properties_setf(props, PW_KEY_MEDIA_NAME, "VBAN Session with %s",
430
+ sess_name);
431
+
432
+ if ((str = pw_properties_get(props, "stream.props")) != NULL)
433
+ pw_properties_update_string(stream_props, str, strlen(str));
434
+
435
+ copy_props(impl, props, PW_KEY_AUDIO_FORMAT);
436
+ copy_props(impl, props, PW_KEY_AUDIO_RATE);
437
+ copy_props(impl, props, PW_KEY_AUDIO_CHANNELS);
438
+ copy_props(impl, props, SPA_KEY_AUDIO_POSITION);
439
+ copy_props(impl, props, PW_KEY_NODE_NAME);
440
+ copy_props(impl, props, PW_KEY_NODE_DESCRIPTION);
441
+ copy_props(impl, props, PW_KEY_NODE_GROUP);
442
+ copy_props(impl, props, PW_KEY_NODE_LATENCY);
443
+ copy_props(impl, props, PW_KEY_NODE_VIRTUAL);
444
+ copy_props(impl, props, PW_KEY_NODE_CHANNELNAMES);
445
+ copy_props(impl, props, PW_KEY_MEDIA_NAME);
446
+ copy_props(impl, props, PW_KEY_MEDIA_CLASS);
447
+ copy_props(impl, props, "net.mtu");
448
+ copy_props(impl, props, "sess.media");
449
+ copy_props(impl, props, "sess.name");
450
+ copy_props(impl, props, "sess.min-ptime");
451
+ copy_props(impl, props, "sess.max-ptime");
452
+ copy_props(impl, props, "sess.latency.msec");
453
+ copy_props(impl, props, "sess.ts-refclk");
454
+
455
+ str = pw_properties_get(props, "local.ifname");
456
+ impl->ifname = str ? strdup(str) : NULL;
457
+
458
+ if ((str = pw_properties_get(props, "source.ip")) == NULL)
459
+ str = DEFAULT_SOURCE_IP;
460
+ if ((res = parse_address(str, 0, &impl->src_addr, &impl->src_len)) < 0) {
461
+ pw_log_error("invalid source.ip %s: %s", str, spa_strerror(res));
462
+ goto out;
463
+ }
464
+
465
+ impl->dst_port = pw_properties_get_uint32(props, "destination.port", DEFAULT_PORT);
466
+ if ((str = pw_properties_get(props, "destination.ip")) == NULL)
467
+ str = DEFAULT_DESTINATION_IP;
468
+ if ((res = parse_address(str, impl->dst_port, &impl->dst_addr, &impl->dst_len)) < 0) {
469
+ pw_log_error("invalid destination.ip %s: %s", str, spa_strerror(res));
470
+ goto out;
471
+ }
472
+
473
+ impl->ttl = pw_properties_get_uint32(props, "net.ttl", DEFAULT_TTL);
474
+ impl->mcast_loop = pw_properties_get_bool(props, "net.loop", DEFAULT_LOOP);
475
+ impl->dscp = pw_properties_get_uint32(props, "net.dscp", DEFAULT_DSCP);
476
+
477
+ get_ip(&impl->src_addr, addr, sizeof(addr));
478
+ pw_properties_set(stream_props, "vban.source.ip", addr);
479
+ get_ip(&impl->dst_addr, addr, sizeof(addr));
480
+ pw_properties_set(stream_props, "vban.destination.ip", addr);
481
+ pw_properties_setf(stream_props, "vban.destination.port", "%u", impl->dst_port);
482
+ pw_properties_setf(stream_props, "vban.ttl", "%u", impl->ttl);
483
+ pw_properties_setf(stream_props, "vban.dscp", "%u", impl->dscp);
484
+
485
+ impl->core = pw_context_get_object(impl->context, PW_TYPE_INTERFACE_Core);
486
+ if (impl->core == NULL) {
487
+ str = pw_properties_get(props, PW_KEY_REMOTE_NAME);
488
+ impl->core = pw_context_connect(impl->context,
489
+ pw_properties_new(
490
+ PW_KEY_REMOTE_NAME, str,
491
+ NULL),
492
+ 0);
493
+ impl->do_disconnect = true;
494
+ }
495
+ if (impl->core == NULL) {
496
+ res = -errno;
497
+ pw_log_error("can't connect: %m");
498
+ goto out;
499
+ }
500
+
501
+ pw_proxy_add_listener((struct pw_proxy*)impl->core,
502
+ &impl->core_proxy_listener,
503
+ &core_proxy_events, impl);
504
+ pw_core_add_listener(impl->core,
505
+ &impl->core_listener,
506
+ &core_events, impl);
507
+
508
+ if ((res = make_socket(&impl->src_addr, impl->src_len,
509
+ &impl->dst_addr, impl->dst_len,
510
+ impl->mcast_loop, impl->ttl, impl->dscp)) < 0) {
511
+ pw_log_error("can't make socket: %s", spa_strerror(res));
512
+ goto out;
513
+ }
514
+ impl->vban_fd = res;
515
+
516
+ impl->stream = vban_stream_new(impl->core,
517
+ PW_DIRECTION_INPUT, pw_properties_copy(stream_props),
518
+ &stream_events, impl);
519
+ if (impl->stream == NULL) {
520
+ res = -errno;
521
+ pw_log_error("can't create stream: %m");
522
+ goto out;
523
+ }
524
+
525
+ pw_impl_module_add_listener(module, &impl->module_listener, &module_events, impl);
526
+
527
+ pw_impl_module_update_properties(module, &SPA_DICT_INIT_ARRAY(module_info));
528
+
529
+ pw_log_info("Successfully loaded module-vban-send");
530
+
531
+ return 0;
532
+out:
533
+ impl_destroy(impl);
534
+ return res;
535
+}
536
pipewire-0.3.76.tar.gz/src/modules/module-vban/audio.c
Added
283
1
2
+/* PipeWire */
3
+/* SPDX-FileCopyrightText: Copyright © 2022 Wim Taymans <wim.taymans@gmail.com> */
4
+/* SPDX-License-Identifier: MIT */
5
+
6
+static void vban_audio_process_playback(void *data)
7
+{
8
+ struct impl *impl = data;
9
+ struct pw_buffer *buf;
10
+ struct spa_data *d;
11
+ uint32_t wanted, timestamp, target_buffer, stride, maxsize;
12
+ int32_t avail;
13
+
14
+ if ((buf = pw_stream_dequeue_buffer(impl->stream)) == NULL) {
15
+ pw_log_debug("Out of stream buffers: %m");
16
+ return;
17
+ }
18
+ d = buf->buffer->datas;
19
+
20
+ stride = impl->stride;
21
+
22
+ maxsize = d0.maxsize / stride;
23
+ wanted = buf->requested ? SPA_MIN(buf->requested, maxsize) : maxsize;
24
+
25
+ avail = spa_ringbuffer_get_read_index(&impl->ring, ×tamp);
26
+
27
+ target_buffer = impl->target_buffer;
28
+
29
+ if (avail < (int32_t)wanted) {
30
+ enum spa_log_level level;
31
+ memset(d0.data, 0, wanted * stride);
32
+ if (impl->have_sync) {
33
+ impl->have_sync = false;
34
+ level = SPA_LOG_LEVEL_WARN;
35
+ } else {
36
+ level = SPA_LOG_LEVEL_DEBUG;
37
+ }
38
+ pw_log(level, "underrun %d/%u < %u",
39
+ avail, target_buffer, wanted);
40
+ } else {
41
+ float error, corr;
42
+ if (impl->first) {
43
+ if ((uint32_t)avail > target_buffer) {
44
+ uint32_t skip = avail - target_buffer;
45
+ pw_log_debug("first: avail:%d skip:%u target:%u",
46
+ avail, skip, target_buffer);
47
+ timestamp += skip;
48
+ avail = target_buffer;
49
+ }
50
+ impl->first = false;
51
+ } else if (avail > (int32_t)SPA_MIN(target_buffer * 8, BUFFER_SIZE / stride)) {
52
+ pw_log_warn("overrun %u > %u", avail, target_buffer * 8);
53
+ timestamp += avail - target_buffer;
54
+ avail = target_buffer;
55
+ }
56
+ /* try to adjust our playback rate to keep the
57
+ * requested target_buffer bytes in the ringbuffer */
58
+ error = (float)target_buffer - (float)avail;
59
+ error = SPA_CLAMP(error, -impl->max_error, impl->max_error);
60
+
61
+ corr = spa_dll_update(&impl->dll, error);
62
+
63
+ pw_log_debug("avail:%u target:%u error:%f corr:%f", avail,
64
+ target_buffer, error, corr);
65
+
66
+ if (impl->io_rate_match) {
67
+ SPA_FLAG_SET(impl->io_rate_match->flags,
68
+ SPA_IO_RATE_MATCH_FLAG_ACTIVE);
69
+ impl->io_rate_match->rate = 1.0f / corr;
70
+ }
71
+ spa_ringbuffer_read_data(&impl->ring,
72
+ impl->buffer,
73
+ BUFFER_SIZE,
74
+ (timestamp * stride) & BUFFER_MASK,
75
+ d0.data, wanted * stride);
76
+
77
+ timestamp += wanted;
78
+ spa_ringbuffer_read_update(&impl->ring, timestamp);
79
+ }
80
+ d0.chunk->size = wanted * stride;
81
+ d0.chunk->stride = stride;
82
+ d0.chunk->offset = 0;
83
+ buf->size = wanted;
84
+
85
+ pw_stream_queue_buffer(impl->stream, buf);
86
+}
87
+
88
+static int vban_audio_receive(struct impl *impl, uint8_t *buffer, ssize_t len)
89
+{
90
+ struct vban_header *hdr;
91
+ ssize_t hlen, plen;
92
+ uint32_t n_frames, timestamp, samples, write, expected_write;
93
+ uint32_t stride = impl->stride;
94
+ int32_t filled;
95
+
96
+ if (len < VBAN_HEADER_SIZE)
97
+ goto short_packet;
98
+
99
+ hdr = (struct vban_header*)buffer;
100
+ if (strncmp(hdr->vban, "VBAN", 3))
101
+ goto invalid_version;
102
+
103
+ impl->receiving = true;
104
+
105
+ hlen = VBAN_HEADER_SIZE;
106
+ plen = len - hlen;
107
+ samples = SPA_MIN(hdr->format_nbs, plen / stride);
108
+
109
+ n_frames = hdr->n_frames;
110
+ if (impl->have_sync && impl->n_frames != n_frames) {
111
+ pw_log_info("unexpected frame (%d != %d)",
112
+ n_frames, impl->n_frames);
113
+ impl->have_sync = false;
114
+ }
115
+ impl->n_frames = n_frames + 1;
116
+
117
+ timestamp = impl->timestamp;
118
+ impl->timestamp += samples;
119
+
120
+ filled = spa_ringbuffer_get_write_index(&impl->ring, &expected_write);
121
+
122
+ /* we always write to timestamp + delay */
123
+ write = timestamp + impl->target_buffer;
124
+
125
+ if (!impl->have_sync) {
126
+ pw_log_info("sync to timestamp:%u target:%u",
127
+ timestamp, impl->target_buffer);
128
+
129
+ /* we read from timestamp, keeping target_buffer of data
130
+ * in the ringbuffer. */
131
+ impl->ring.readindex = timestamp;
132
+ impl->ring.writeindex = write;
133
+ filled = impl->target_buffer;
134
+
135
+ spa_dll_init(&impl->dll);
136
+ spa_dll_set_bw(&impl->dll, SPA_DLL_BW_MIN, 128, impl->rate);
137
+ memset(impl->buffer, 0, BUFFER_SIZE);
138
+ impl->have_sync = true;
139
+ } else if (expected_write != write) {
140
+ pw_log_debug("unexpected write (%u != %u)",
141
+ write, expected_write);
142
+ }
143
+
144
+ if (filled + samples > BUFFER_SIZE / stride) {
145
+ pw_log_debug("capture overrun %u + %u > %u", filled, samples,
146
+ BUFFER_SIZE / stride);
147
+ impl->have_sync = false;
148
+ } else {
149
+ pw_log_debug("got samples:%u", samples);
150
+ spa_ringbuffer_write_data(&impl->ring,
151
+ impl->buffer,
152
+ BUFFER_SIZE,
153
+ (write * stride) & BUFFER_MASK,
154
+ &bufferhlen, (samples * stride));
155
+ write += samples;
156
+ spa_ringbuffer_write_update(&impl->ring, write);
157
+ }
158
+ return 0;
159
+
160
+short_packet:
161
+ pw_log_warn("short packet received");
162
+ return -EINVAL;
163
+invalid_version:
164
+ pw_log_warn("invalid VBAN version");
165
+ spa_debug_mem(0, buffer, len);
166
+ return -EPROTO;
167
+}
168
+
169
+static inline void
170
+set_iovec(struct spa_ringbuffer *rbuf, void *buffer, uint32_t size,
171
+ uint32_t offset, struct iovec *iov, uint32_t len)
172
+{
173
+ iov0.iov_len = SPA_MIN(len, size - offset);
174
+ iov0.iov_base = SPA_PTROFF(buffer, offset, void);
175
+ iov1.iov_len = len - iov0.iov_len;
176
+ iov1.iov_base = buffer;
177
+}
178
+
179
+static void vban_audio_flush_packets(struct impl *impl)
180
+{
181
+ int32_t avail, tosend;
182
+ uint32_t stride, timestamp;
183
+ struct iovec iov3;
184
+ struct vban_header header;
185
+
186
+ avail = spa_ringbuffer_get_read_index(&impl->ring, ×tamp);
187
+ tosend = impl->psamples;
188
+
189
+ if (avail < tosend)
190
+ return;
191
+
192
+ stride = impl->stride;
193
+
194
+ header = impl->header;
195
+ header.format_nbs = tosend - 1;
196
+ header.format_nbc = impl->stream_info.info.raw.channels - 1;
197
+
198
+ iov0.iov_base = &header;
199
+ iov0.iov_len = sizeof(header);
200
+
201
+ while (avail >= tosend) {
202
+ set_iovec(&impl->ring,
203
+ impl->buffer, BUFFER_SIZE,
204
+ (timestamp * stride) & BUFFER_MASK,
205
+ &iov1, tosend * stride);
206
+
207
+ pw_log_trace("sending %d timestamp:%08x", tosend, timestamp);
208
+
209
+ vban_stream_emit_send_packet(impl, iov, 3);
210
+
211
+ timestamp += tosend;
212
+ avail -= tosend;
213
+ impl->header.n_frames++;
214
+ }
215
+ spa_ringbuffer_read_update(&impl->ring, timestamp);
216
+}
217
+
218
+static void vban_audio_process_capture(void *data)
219
+{
220
+ struct impl *impl = data;
221
+ struct pw_buffer *buf;
222
+ struct spa_data *d;
223
+ uint32_t offs, size, timestamp, expected_timestamp, stride;
224
+ int32_t filled, wanted;
225
+
226
+ if ((buf = pw_stream_dequeue_buffer(impl->stream)) == NULL) {
227
+ pw_log_debug("Out of stream buffers: %m");
228
+ return;
229
+ }
230
+ d = buf->buffer->datas;
231
+
232
+ offs = SPA_MIN(d0.chunk->offset, d0.maxsize);
233
+ size = SPA_MIN(d0.chunk->size, d0.maxsize - offs);
234
+ stride = impl->stride;
235
+ wanted = size / stride;
236
+
237
+ filled = spa_ringbuffer_get_write_index(&impl->ring, &expected_timestamp);
238
+
239
+ if (SPA_LIKELY(impl->io_position)) {
240
+ uint32_t rate = impl->io_position->clock.rate.denom;
241
+ timestamp = impl->io_position->clock.position * impl->rate / rate;
242
+ } else
243
+ timestamp = expected_timestamp;
244
+
245
+ if (!impl->have_sync) {
246
+ pw_log_info("sync to timestamp:%u", timestamp);
247
+ impl->ring.readindex = impl->ring.writeindex = timestamp;
248
+ memset(impl->buffer, 0, BUFFER_SIZE);
249
+ impl->have_sync = true;
250
+ expected_timestamp = timestamp;
251
+ } else {
252
+ if (SPA_ABS((int32_t)expected_timestamp - (int32_t)timestamp) > 32) {
253
+ pw_log_warn("expected %u != timestamp %u", expected_timestamp, timestamp);
254
+ impl->have_sync = false;
255
+ } else if (filled + wanted > (int32_t)(BUFFER_SIZE / stride)) {
256
+ pw_log_warn("overrun %u + %u > %u", filled, wanted, BUFFER_SIZE / stride);
257
+ impl->have_sync = false;
258
+ }
259
+ }
260
+
261
+ spa_ringbuffer_write_data(&impl->ring,
262
+ impl->buffer,
263
+ BUFFER_SIZE,
264
+ (expected_timestamp * stride) & BUFFER_MASK,
265
+ SPA_PTROFF(d0.data, offs, void), wanted * stride);
266
+ expected_timestamp += wanted;
267
+ spa_ringbuffer_write_update(&impl->ring, expected_timestamp);
268
+
269
+ pw_stream_queue_buffer(impl->stream, buf);
270
+
271
+ vban_audio_flush_packets(impl);
272
+}
273
+
274
+static int vban_audio_init(struct impl *impl, enum spa_direction direction)
275
+{
276
+ if (direction == SPA_DIRECTION_INPUT)
277
+ impl->stream_events.process = vban_audio_process_capture;
278
+ else
279
+ impl->stream_events.process = vban_audio_process_playback;
280
+ impl->receive_vban = vban_audio_receive;
281
+ return 0;
282
+}
283
pipewire-0.3.76.tar.gz/src/modules/module-vban/stream.c
Added
481
1
2
+/* PipeWire */
3
+/* SPDX-FileCopyrightText: Copyright © 2023 Wim Taymans <wim.taymans@gmail.com> */
4
+/* SPDX-License-Identifier: MIT */
5
+
6
+#include <sys/socket.h>
7
+#include <arpa/inet.h>
8
+
9
+#include <spa/utils/result.h>
10
+#include <spa/utils/json.h>
11
+#include <spa/utils/ringbuffer.h>
12
+#include <spa/utils/dll.h>
13
+#include <spa/param/audio/format-utils.h>
14
+#include <spa/control/control.h>
15
+#include <spa/debug/types.h>
16
+#include <spa/debug/mem.h>
17
+
18
+#include "config.h"
19
+
20
+#include <pipewire/pipewire.h>
21
+#include <pipewire/impl.h>
22
+
23
+#include <module-vban/vban.h>
24
+#include <module-vban/stream.h>
25
+
26
+#define BUFFER_SIZE (1u<<22)
27
+#define BUFFER_MASK (BUFFER_SIZE-1)
28
+#define BUFFER_SIZE2 (BUFFER_SIZE>>1)
29
+#define BUFFER_MASK2 (BUFFER_SIZE2-1)
30
+
31
+#define vban_stream_emit(s,m,v,...) spa_hook_list_call(&s->listener_list, \
32
+ struct vban_stream_events, m, v, ##__VA_ARGS__)
33
+#define vban_stream_emit_destroy(s) vban_stream_emit(s, destroy, 0)
34
+#define vban_stream_emit_state_changed(s,n,e) vban_stream_emit(s, state_changed,0,n,e)
35
+#define vban_stream_emit_send_packet(s,i,l) vban_stream_emit(s, send_packet,0,i,l)
36
+#define vban_stream_emit_send_feedback(s,seq) vban_stream_emit(s, send_feedback,0,seq)
37
+
38
+struct impl {
39
+ struct spa_audio_info info;
40
+ struct spa_audio_info stream_info;
41
+
42
+ struct pw_stream *stream;
43
+ struct spa_hook stream_listener;
44
+ struct pw_stream_events stream_events;
45
+
46
+ struct spa_hook_list listener_list;
47
+ struct spa_hook listener;
48
+
49
+ const struct format_info *format_info;
50
+
51
+ void *stream_data;
52
+
53
+ uint32_t rate;
54
+ uint32_t stride;
55
+ uint32_t psamples;
56
+ uint32_t mtu;
57
+
58
+ struct vban_header header;
59
+ uint32_t timestamp;
60
+ uint32_t n_frames;
61
+
62
+ struct spa_ringbuffer ring;
63
+ uint8_t bufferBUFFER_SIZE;
64
+
65
+ struct spa_io_rate_match *io_rate_match;
66
+ struct spa_io_position *io_position;
67
+ struct spa_dll dll;
68
+ double corr;
69
+ uint32_t target_buffer;
70
+ float max_error;
71
+
72
+ float last_timestamp;
73
+ float last_time;
74
+
75
+ unsigned always_process:1;
76
+ unsigned started:1;
77
+ unsigned have_sync:1;
78
+ unsigned receiving:1;
79
+ unsigned first:1;
80
+
81
+ int (*receive_vban)(struct impl *impl, uint8_t *buffer, ssize_t len);
82
+};
83
+
84
+#include "module-vban/audio.c"
85
+//#include "module-vban/midi.c"
86
+
87
+struct format_info {
88
+ uint32_t media_subtype;
89
+ uint32_t format;
90
+ uint32_t size;
91
+ uint8_t format_bit;
92
+};
93
+
94
+static const struct format_info audio_format_info = {
95
+ { SPA_MEDIA_SUBTYPE_raw, SPA_AUDIO_FORMAT_U8, 1, VBAN_DATATYPE_U8, },
96
+ { SPA_MEDIA_SUBTYPE_raw, SPA_AUDIO_FORMAT_S16_LE, 2, VBAN_DATATYPE_INT16, },
97
+ { SPA_MEDIA_SUBTYPE_raw, SPA_AUDIO_FORMAT_S24_LE, 3, VBAN_DATATYPE_INT24, },
98
+ { SPA_MEDIA_SUBTYPE_raw, SPA_AUDIO_FORMAT_S32_LE, 4, VBAN_DATATYPE_INT32, },
99
+ { SPA_MEDIA_SUBTYPE_raw, SPA_AUDIO_FORMAT_F32_LE, 4, VBAN_DATATYPE_FLOAT32, },
100
+ { SPA_MEDIA_SUBTYPE_raw, SPA_AUDIO_FORMAT_F64_LE, 8, VBAN_DATATYPE_FLOAT64, },
101
+ { SPA_MEDIA_SUBTYPE_control, 0, 1, },
102
+};
103
+
104
+static void stream_io_changed(void *data, uint32_t id, void *area, uint32_t size)
105
+{
106
+ struct impl *impl = data;
107
+ switch (id) {
108
+ case SPA_IO_RateMatch:
109
+ impl->io_rate_match = area;
110
+ break;
111
+ case SPA_IO_Position:
112
+ impl->io_position = area;
113
+ break;
114
+ }
115
+}
116
+
117
+static void stream_destroy(void *d)
118
+{
119
+ struct impl *impl = d;
120
+ spa_hook_remove(&impl->stream_listener);
121
+ impl->stream = NULL;
122
+}
123
+
124
+static int stream_start(struct impl *impl)
125
+{
126
+ if (impl->started)
127
+ return 0;
128
+
129
+ vban_stream_emit_state_changed(impl, true, NULL);
130
+
131
+ impl->started = true;
132
+ return 0;
133
+}
134
+
135
+static int stream_stop(struct impl *impl)
136
+{
137
+ if (!impl->started)
138
+ return 0;
139
+
140
+ vban_stream_emit_state_changed(impl, false, NULL);
141
+
142
+ impl->started = false;
143
+ return 0;
144
+}
145
+
146
+static void on_stream_state_changed(void *d, enum pw_stream_state old,
147
+ enum pw_stream_state state, const char *error)
148
+{
149
+ struct impl *impl = d;
150
+
151
+ switch (state) {
152
+ case PW_STREAM_STATE_UNCONNECTED:
153
+ pw_log_info("stream disconnected");
154
+ break;
155
+ case PW_STREAM_STATE_ERROR:
156
+ pw_log_error("stream error: %s", error);
157
+ vban_stream_emit_state_changed(impl, false, error);
158
+ break;
159
+ case PW_STREAM_STATE_STREAMING:
160
+ if ((errno = -stream_start(impl)) < 0)
161
+ pw_log_error("failed to start RTP stream: %m");
162
+ break;
163
+ case PW_STREAM_STATE_PAUSED:
164
+ if (!impl->always_process)
165
+ stream_stop(impl);
166
+ impl->have_sync = false;
167
+ break;
168
+ default:
169
+ break;
170
+ }
171
+}
172
+
173
+static const struct pw_stream_events stream_events = {
174
+ PW_VERSION_STREAM_EVENTS,
175
+ .destroy = stream_destroy,
176
+ .state_changed = on_stream_state_changed,
177
+ .io_changed = stream_io_changed,
178
+};
179
+
180
+static const struct format_info *find_audio_format_info(const struct spa_audio_info *info)
181
+{
182
+ SPA_FOR_EACH_ELEMENT_VAR(audio_format_info, f)
183
+ if (f->media_subtype == info->media_subtype &&
184
+ (f->format == 0 || f->format == info->info.raw.format))
185
+ return f;
186
+ return NULL;
187
+}
188
+
189
+static inline uint32_t format_from_name(const char *name, size_t len)
190
+{
191
+ int i;
192
+ for (i = 0; spa_type_audio_formati.name; i++) {
193
+ if (strncmp(name, spa_debug_type_short_name(spa_type_audio_formati.name), len) == 0)
194
+ return spa_type_audio_formati.type;
195
+ }
196
+ return SPA_AUDIO_FORMAT_UNKNOWN;
197
+}
198
+
199
+static uint32_t channel_from_name(const char *name)
200
+{
201
+ int i;
202
+ for (i = 0; spa_type_audio_channeli.name; i++) {
203
+ if (spa_streq(name, spa_debug_type_short_name(spa_type_audio_channeli.name)))
204
+ return spa_type_audio_channeli.type;
205
+ }
206
+ return SPA_AUDIO_CHANNEL_UNKNOWN;
207
+}
208
+
209
+static void parse_position(struct spa_audio_info_raw *info, const char *val, size_t len)
210
+{
211
+ struct spa_json it2;
212
+ char v256;
213
+
214
+ spa_json_init(&it0, val, len);
215
+ if (spa_json_enter_array(&it0, &it1) <= 0)
216
+ spa_json_init(&it1, val, len);
217
+
218
+ info->channels = 0;
219
+ while (spa_json_get_string(&it1, v, sizeof(v)) > 0 &&
220
+ info->channels < SPA_AUDIO_MAX_CHANNELS) {
221
+ info->positioninfo->channels++ = channel_from_name(v);
222
+ }
223
+}
224
+
225
+static void parse_audio_info(const struct pw_properties *props, struct spa_audio_info_raw *info)
226
+{
227
+ const char *str;
228
+
229
+ spa_zero(*info);
230
+ if ((str = pw_properties_get(props, PW_KEY_AUDIO_FORMAT)) == NULL)
231
+ str = DEFAULT_FORMAT;
232
+ info->format = format_from_name(str, strlen(str));
233
+
234
+ info->rate = pw_properties_get_uint32(props, PW_KEY_AUDIO_RATE, info->rate);
235
+ if (info->rate == 0)
236
+ info->rate = DEFAULT_RATE;
237
+
238
+ info->channels = pw_properties_get_uint32(props, PW_KEY_AUDIO_CHANNELS, info->channels);
239
+ info->channels = SPA_MIN(info->channels, SPA_AUDIO_MAX_CHANNELS);
240
+ if ((str = pw_properties_get(props, SPA_KEY_AUDIO_POSITION)) != NULL)
241
+ parse_position(info, str, strlen(str));
242
+ if (info->channels == 0)
243
+ parse_position(info, DEFAULT_POSITION, strlen(DEFAULT_POSITION));
244
+}
245
+
246
+static uint32_t msec_to_samples(struct impl *impl, uint32_t msec)
247
+{
248
+ return msec * impl->rate / 1000;
249
+}
250
+
251
+struct vban_stream *vban_stream_new(struct pw_core *core,
252
+ enum pw_direction direction, struct pw_properties *props,
253
+ const struct vban_stream_events *events, void *data)
254
+{
255
+ struct impl *impl;
256
+ const char *str;
257
+ uint8_t buffer1024;
258
+ struct spa_pod_builder b;
259
+ uint32_t n_params, min_samples, max_samples;
260
+ float min_ptime, max_ptime;
261
+ const struct spa_pod *params1;
262
+ enum pw_stream_flags flags;
263
+ int latency_msec;
264
+ int res;
265
+
266
+ impl = calloc(1, sizeof(*impl));
267
+ if (impl == NULL) {
268
+ res = -errno;
269
+ goto out;
270
+ return NULL;
271
+ }
272
+ impl->first = true;
273
+ spa_hook_list_init(&impl->listener_list);
274
+ impl->stream_events = stream_events;
275
+
276
+ if ((str = pw_properties_get(props, "sess.media")) == NULL)
277
+ str = "audio";
278
+
279
+ if (spa_streq(str, "audio")) {
280
+ impl->info.media_type = SPA_MEDIA_TYPE_audio;
281
+ impl->info.media_subtype = SPA_MEDIA_SUBTYPE_raw;
282
+ }
283
+ else if (spa_streq(str, "midi")) {
284
+ impl->info.media_type = SPA_MEDIA_TYPE_application;
285
+ impl->info.media_subtype = SPA_MEDIA_SUBTYPE_control;
286
+ }
287
+ else {
288
+ pw_log_error("unsupported media type:%s", str);
289
+ res = -EINVAL;
290
+ goto out;
291
+ }
292
+ memcpy(impl->header.vban, "VBAN", 4);
293
+ if ((str = pw_properties_get(props, "sess.name")) == NULL)
294
+ str = "Stream1";
295
+ strcpy(impl->header.stream_name, str);
296
+
297
+ switch (impl->info.media_subtype) {
298
+ case SPA_MEDIA_SUBTYPE_raw:
299
+ parse_audio_info(props, &impl->info.info.raw);
300
+ impl->stream_info = impl->info;
301
+ impl->format_info = find_audio_format_info(&impl->info);
302
+ if (impl->format_info == NULL) {
303
+ pw_log_error("unsupported audio format:%d channels:%d",
304
+ impl->stream_info.info.raw.format,
305
+ impl->stream_info.info.raw.channels);
306
+ res = -EINVAL;
307
+ goto out;
308
+ }
309
+ impl->stride = impl->format_info->size * impl->stream_info.info.raw.channels;
310
+ impl->rate = impl->stream_info.info.raw.rate;
311
+ impl->header.format_SR = vban_sr_index(impl->rate);
312
+ if (impl->header.format_SR == VBAN_SR_MAXNUMBER) {
313
+ pw_log_error("unsupported audio rate:%u", impl->rate);
314
+ res = -EINVAL;
315
+ goto out;
316
+ }
317
+ impl->header.format_bit = impl->format_info->format_bit;
318
+ break;
319
+ case SPA_MEDIA_SUBTYPE_control:
320
+ impl->stream_info = impl->info;
321
+ impl->format_info = find_audio_format_info(&impl->info);
322
+ if (impl->format_info == NULL) {
323
+ res = -EINVAL;
324
+ goto out;
325
+ }
326
+ pw_properties_set(props, PW_KEY_FORMAT_DSP, "8 bit raw midi");
327
+ impl->stride = impl->format_info->size;
328
+ impl->rate = pw_properties_get_uint32(props, "midi.rate", 10000);
329
+ if (impl->rate == 0)
330
+ impl->rate = 10000;
331
+ break;
332
+ default:
333
+ spa_assert_not_reached();
334
+ break;
335
+ }
336
+
337
+ if (pw_properties_get(props, PW_KEY_NODE_VIRTUAL) == NULL)
338
+ pw_properties_set(props, PW_KEY_NODE_VIRTUAL, "true");
339
+ if (pw_properties_get(props, PW_KEY_NODE_NETWORK) == NULL)
340
+ pw_properties_set(props, PW_KEY_NODE_NETWORK, "true");
341
+
342
+ impl->mtu = pw_properties_get_uint32(props, "net.mtu", DEFAULT_MTU);
343
+
344
+ str = pw_properties_get(props, "sess.min-ptime");
345
+ if (!spa_atof(str, &min_ptime))
346
+ min_ptime = DEFAULT_MIN_PTIME;
347
+ str = pw_properties_get(props, "sess.max-ptime");
348
+ if (!spa_atof(str, &max_ptime))
349
+ max_ptime = DEFAULT_MAX_PTIME;
350
+
351
+ min_samples = min_ptime * impl->rate / 1000;
352
+ max_samples = SPA_MIN(256, max_ptime * impl->rate / 1000);
353
+
354
+ float ptime = 0;
355
+ if ((str = pw_properties_get(props, "vban.ptime")) != NULL)
356
+ if (!spa_atof(str, &ptime))
357
+ ptime = 0.0;
358
+
359
+ if (ptime) {
360
+ impl->psamples = ptime * impl->rate / 1000;
361
+ } else {
362
+ impl->psamples = impl->mtu / impl->stride;
363
+ impl->psamples = SPA_CLAMP(impl->psamples, min_samples, max_samples);
364
+ if (direction == PW_DIRECTION_OUTPUT)
365
+ pw_properties_setf(props, "vban.ptime", "%f",
366
+ impl->psamples * 1000.0 / impl->rate);
367
+ }
368
+ latency_msec = pw_properties_get_uint32(props,
369
+ "sess.latency.msec", DEFAULT_SESS_LATENCY);
370
+ impl->target_buffer = msec_to_samples(impl, latency_msec);
371
+ impl->max_error = msec_to_samples(impl, ERROR_MSEC);
372
+
373
+ pw_properties_setf(props, PW_KEY_NODE_RATE, "1/%d", impl->rate);
374
+ if (direction == PW_DIRECTION_INPUT) {
375
+ pw_properties_setf(props, PW_KEY_NODE_LATENCY, "%d/%d",
376
+ impl->psamples, impl->rate);
377
+ } else {
378
+ pw_properties_setf(props, PW_KEY_NODE_LATENCY, "%d/%d",
379
+ impl->target_buffer / 2, impl->rate);
380
+ }
381
+
382
+ pw_properties_setf(props, "net.mtu", "%u", impl->mtu);
383
+ pw_properties_setf(props, "vban.rate", "%u", impl->rate);
384
+ if (impl->info.info.raw.channels > 0)
385
+ pw_properties_setf(props, "vban.channels", "%u", impl->info.info.raw.channels);
386
+
387
+ spa_dll_init(&impl->dll);
388
+ spa_dll_set_bw(&impl->dll, SPA_DLL_BW_MIN, 128, impl->rate);
389
+ impl->corr = 1.0;
390
+
391
+ impl->stream = pw_stream_new(core, "vban-session", props);
392
+ props = NULL;
393
+ if (impl->stream == NULL) {
394
+ res = -errno;
395
+ pw_log_error("can't create stream: %m");
396
+ goto out;
397
+ }
398
+
399
+ n_params = 0;
400
+ spa_pod_builder_init(&b, buffer, sizeof(buffer));
401
+
402
+ flags = PW_STREAM_FLAG_MAP_BUFFERS | PW_STREAM_FLAG_RT_PROCESS;
403
+
404
+
405
+ switch (impl->info.media_subtype) {
406
+ case SPA_MEDIA_SUBTYPE_raw:
407
+ paramsn_params++ = spa_format_audio_build(&b,
408
+ SPA_PARAM_EnumFormat, &impl->stream_info);
409
+ flags |= PW_STREAM_FLAG_AUTOCONNECT;
410
+ vban_audio_init(impl, direction);
411
+ break;
412
+ case SPA_MEDIA_SUBTYPE_control:
413
+ paramsn_params++ = spa_pod_builder_add_object(&b,
414
+ SPA_TYPE_OBJECT_Format, SPA_PARAM_EnumFormat,
415
+ SPA_FORMAT_mediaType, SPA_POD_Id(SPA_MEDIA_TYPE_application),
416
+ SPA_FORMAT_mediaSubtype, SPA_POD_Id(SPA_MEDIA_SUBTYPE_control));
417
+// vban_midi_init(impl, direction);
418
+ break;
419
+ default:
420
+ res = -EINVAL;
421
+ goto out;
422
+ }
423
+
424
+ pw_stream_add_listener(impl->stream,
425
+ &impl->stream_listener,
426
+ &impl->stream_events, impl);
427
+
428
+ if ((res = pw_stream_connect(impl->stream,
429
+ direction,
430
+ PW_ID_ANY,
431
+ flags,
432
+ params, n_params)) < 0) {
433
+ pw_log_error("can't connect stream: %s", spa_strerror(res));
434
+ goto out;
435
+ }
436
+
437
+ if (impl->always_process &&
438
+ (res = stream_start(impl)) < 0)
439
+ goto out;
440
+
441
+ spa_hook_list_append(&impl->listener_list, &impl->listener, events, data);
442
+
443
+ return (struct vban_stream*)impl;
444
+out:
445
+ pw_properties_free(props);
446
+ errno = -res;
447
+ return NULL;
448
+}
449
+
450
+void vban_stream_destroy(struct vban_stream *s)
451
+{
452
+ struct impl *impl = (struct impl*)s;
453
+
454
+ vban_stream_emit_destroy(impl);
455
+
456
+ if (impl->stream)
457
+ pw_stream_destroy(impl->stream);
458
+
459
+ spa_hook_list_clean(&impl->listener_list);
460
+ free(impl);
461
+}
462
+
463
+int vban_stream_receive_packet(struct vban_stream *s, uint8_t *buffer, size_t len)
464
+{
465
+ struct impl *impl = (struct impl*)s;
466
+ return impl->receive_vban(impl, buffer, len);
467
+}
468
+
469
+uint64_t vban_stream_get_time(struct vban_stream *s, uint64_t *rate)
470
+{
471
+ struct impl *impl = (struct impl*)s;
472
+ struct spa_io_position *pos = impl->io_position;
473
+
474
+ if (pos == NULL)
475
+ return -EIO;
476
+
477
+ *rate = impl->rate;
478
+ return pos->clock.position * impl->rate *
479
+ pos->clock.rate.num / pos->clock.rate.denom;
480
+}
481
pipewire-0.3.76.tar.gz/src/modules/module-vban/stream.h
Added
56
1
2
+/* PipeWire */
3
+/* SPDX-FileCopyrightText: Copyright © 2023 Wim Taymans <wim.taymans@gmail.com> */
4
+/* SPDX-License-Identifier: MIT */
5
+
6
+#ifndef PIPEWIRE_VBAN_STREAM_H
7
+#define PIPEWIRE_VBAN_STREAM_H
8
+
9
+#ifdef __cplusplus
10
+extern "C" {
11
+#endif
12
+
13
+struct vban_stream;
14
+
15
+#define DEFAULT_FORMAT "S16LE"
16
+#define DEFAULT_RATE 44100
17
+#define DEFAULT_CHANNELS 2
18
+#define DEFAULT_POSITION " FL FR "
19
+
20
+#define ERROR_MSEC 2
21
+#define DEFAULT_SESS_LATENCY 100
22
+
23
+#define DEFAULT_MTU VBAN_PROTOCOL_MAX_SIZE
24
+#define DEFAULT_MIN_PTIME 2
25
+#define DEFAULT_MAX_PTIME 20
26
+
27
+struct vban_stream_events {
28
+#define VBAN_VERSION_STREAM_EVENTS 0
29
+ uint32_t version;
30
+
31
+ void (*destroy) (void *data);
32
+
33
+ void (*state_changed) (void *data, bool started, const char *error);
34
+
35
+ void (*send_packet) (void *data, struct iovec *iov, size_t iovlen);
36
+
37
+ void (*send_feedback) (void *data, uint32_t senum);
38
+};
39
+
40
+struct vban_stream *vban_stream_new(struct pw_core *core,
41
+ enum pw_direction direction, struct pw_properties *props,
42
+ const struct vban_stream_events *events, void *data);
43
+
44
+void vban_stream_destroy(struct vban_stream *s);
45
+
46
+int vban_stream_receive_packet(struct vban_stream *s, uint8_t *buffer, size_t len);
47
+
48
+uint64_t vban_stream_get_time(struct vban_stream *s, uint64_t *rate);
49
+
50
+
51
+#ifdef __cplusplus
52
+}
53
+#endif
54
+
55
+#endif /* PIPEWIRE_VBAN_STREAM_H */
56
pipewire-0.3.76.tar.gz/src/modules/module-vban/vban.h
Added
62
1
2
+/* PipeWire */
3
+/* SPDX-FileCopyrightText: Copyright © 2023 Wim Taymans <wim.taymans@gmail.com> */
4
+/* SPDX-License-Identifier: MIT */
5
+
6
+#ifndef PIPEWIRE_VBAN_H
7
+#define PIPEWIRE_VBAN_H
8
+
9
+#ifdef __cplusplus
10
+extern "C" {
11
+#endif
12
+
13
+#define VBAN_HEADER_SIZE (4 + 4 + 16 + 4)
14
+#define VBAN_STREAM_NAME_SIZE 16
15
+#define VBAN_PROTOCOL_MAX_SIZE 1464
16
+#define VBAN_DATA_MAX_SIZE (VBAN_PROTOCOL_MAX_SIZE - VBAN_HEADER_SIZE)
17
+#define VBAN_CHANNELS_MAX_NB 256
18
+#define VBAN_SAMPLES_MAX_NB 256
19
+
20
+struct vban_header {
21
+ char vban4; /* contains 'V' 'B', 'A', 'N' */
22
+ uint8_t format_SR; /* SR index */
23
+ uint8_t format_nbs; /* nb sample per frame (1 to 256) */
24
+ uint8_t format_nbc; /* nb channel (1 to 256) */
25
+ uint8_t format_bit; /* bit format */
26
+ char stream_nameVBAN_STREAM_NAME_SIZE; /* stream name */
27
+ uint32_t n_frames; /* growing frame number. */
28
+} __attribute__ ((packed));
29
+
30
+#define VBAN_SR_MAXNUMBER 21
31
+
32
+static uint32_t const vban_SRVBAN_SR_MAXNUMBER = {
33
+ 6000, 12000, 24000, 48000, 96000, 192000, 384000,
34
+ 8000, 16000, 32000, 64000, 128000, 256000, 512000,
35
+ 11025, 22050, 44100, 88200, 176400, 352800, 705600
36
+};
37
+
38
+static inline uint8_t vban_sr_index(uint32_t rate)
39
+{
40
+ uint8_t i;
41
+ for (i = 0; i < SPA_N_ELEMENTS(vban_SR); i++) {
42
+ if (vban_SRi == rate)
43
+ return i;
44
+ }
45
+ return VBAN_SR_MAXNUMBER;
46
+}
47
+
48
+#define VBAN_DATATYPE_U8 0x00
49
+#define VBAN_DATATYPE_INT16 0x01
50
+#define VBAN_DATATYPE_INT24 0x02
51
+#define VBAN_DATATYPE_INT32 0x03
52
+#define VBAN_DATATYPE_FLOAT32 0x04
53
+#define VBAN_DATATYPE_FLOAT64 0x05
54
+#define VBAN_DATATYPE_12BITS 0x06
55
+#define VBAN_DATATYPE_10BITS 0x07
56
+
57
+#ifdef __cplusplus
58
+}
59
+#endif
60
+
61
+#endif /* PIPEWIRE_VBAN_H */
62
pipewire-0.3.74.tar.gz/src/pipewire/context.c -> pipewire-0.3.76.tar.gz/src/pipewire/context.c
Changed
84
1
2
#include <spa/support/plugin.h>
3
#include <spa/support/plugin-loader.h>
4
#include <spa/node/utils.h>
5
+#include <spa/utils/atomic.h>
6
#include <spa/utils/names.h>
7
#include <spa/utils/string.h>
8
#include <spa/debug/types.h>
9
10
spa_hook_list_append(&context->listener_list, listener, events, data);
11
}
12
13
-struct listener_data {
14
- struct spa_hook *listener;
15
- const struct pw_context_driver_events *events;
16
- void *data;
17
-};
18
-
19
-static int
20
-do_add_listener(struct spa_loop *loop,
21
- bool async, uint32_t seq, const void *data, size_t size, void *user_data)
22
-{
23
- struct pw_context *context = user_data;
24
- const struct listener_data *d = data;
25
- spa_hook_list_append(&context->driver_listener_list,
26
- d->listener, d->events, d->data);
27
- return 0;
28
-}
29
-
30
-SPA_EXPORT
31
-void pw_context_driver_add_listener(struct pw_context *context,
32
- struct spa_hook *listener,
33
- const struct pw_context_driver_events *events,
34
- void *data)
35
-{
36
- struct listener_data d = {
37
- .listener = listener,
38
- .events = events,
39
- .data = data };
40
- struct pw_impl_node *n;
41
- spa_list_for_each(n, &context->driver_list, driver_link) {
42
- SPA_FLAG_SET(n->rt.target.activation->flags, PW_NODE_ACTIVATION_FLAG_PROFILER);
43
- }
44
- pw_loop_invoke(context->data_loop,
45
- do_add_listener, SPA_ID_INVALID, &d, sizeof(d), false, context);
46
-}
47
-
48
-static int do_remove_listener(struct spa_loop *loop,
49
- bool async, uint32_t seq, const void *data, size_t size, void *user_data)
50
-{
51
- struct spa_hook *listener = user_data;
52
- spa_hook_remove(listener);
53
- return 0;
54
-}
55
-
56
-SPA_EXPORT
57
-void pw_context_driver_remove_listener(struct pw_context *context,
58
- struct spa_hook *listener)
59
-{
60
- struct pw_impl_node *n;
61
- spa_list_for_each(n, &context->driver_list, driver_link) {
62
- SPA_FLAG_CLEAR(n->rt.target.activation->flags, PW_NODE_ACTIVATION_FLAG_PROFILER);
63
- }
64
- pw_loop_invoke(context->data_loop,
65
- do_remove_listener, SPA_ID_INVALID, NULL, 0, true, listener);
66
-}
67
-
68
SPA_EXPORT
69
const struct spa_support *pw_context_get_support(struct pw_context *context, uint32_t *n_support)
70
{
71
72
pw_log_debug("%p: apply duration:%"PRIu64" rate:%u/%u", context,
73
n->target_quantum, n->target_rate.num,
74
n->target_rate.denom);
75
- SEQ_WRITE(n->rt.position->clock.target_seq);
76
+ SPA_SEQ_WRITE(n->rt.position->clock.target_seq);
77
n->rt.position->clock.target_duration = n->target_quantum;
78
n->rt.position->clock.target_rate = n->target_rate;
79
- SEQ_WRITE(n->rt.position->clock.target_seq);
80
+ SPA_SEQ_WRITE(n->rt.position->clock.target_seq);
81
82
if (n->info.state < PW_NODE_STATE_RUNNING) {
83
n->rt.position->clock.duration = n->target_quantum;
84
pipewire-0.3.74.tar.gz/src/pipewire/context.h -> pipewire-0.3.76.tar.gz/src/pipewire/context.h
Changed
30
1
2
3
struct pw_global;
4
struct pw_impl_client;
5
+struct pw_impl_node;
6
7
#include <pipewire/core.h>
8
#include <pipewire/loop.h>
9
10
11
/** context events emitted by the context object added with \ref pw_context_add_listener */
12
struct pw_context_events {
13
-#define PW_VERSION_CONTEXT_EVENTS 0
14
+#define PW_VERSION_CONTEXT_EVENTS 1
15
uint32_t version;
16
17
/** The context is being destroyed */
18
19
void (*global_added) (void *data, struct pw_global *global);
20
/** a global object was removed */
21
void (*global_removed) (void *data, struct pw_global *global);
22
+
23
+ /** a driver was added, since 0.3.75 version:1 */
24
+ void (*driver_added) (void *data, struct pw_impl_node *node);
25
+ /** a driver was removed, since 0.3.75 version:1 */
26
+ void (*driver_removed) (void *data, struct pw_impl_node *node);
27
};
28
29
/** Make a new context object for a given main_loop. Ownership of the properties is taken */
30
pipewire-0.3.74.tar.gz/src/pipewire/filter.c -> pipewire-0.3.76.tar.gz/src/pipewire/filter.c
Changed
93
1
2
struct filter *impl;
3
struct pw_filter *this;
4
const char *str;
5
- struct match match;
6
int res;
7
8
ensure_loop(context->main_loop, return NULL);
9
10
spa_hook_list_init(&impl->hooks);
11
this->properties = props;
12
13
- pw_context_conf_update_props(context, "filter.properties", props);
14
-
15
- match = MATCH_INIT(this);
16
- pw_context_conf_section_match_rules(context, "filter.rules",
17
- &this->properties->dict, execute_match, &match);
18
-
19
- if ((str = getenv("PIPEWIRE_PROPS")) != NULL)
20
- pw_properties_update_string(props, str, strlen(str));
21
- if ((str = getenv("PIPEWIRE_QUANTUM")) != NULL) {
22
- struct spa_fraction q;
23
- if (sscanf(str, "%u/%u", &q.num, &q.denom) == 2 && q.denom != 0) {
24
- pw_properties_setf(props, PW_KEY_NODE_RATE,
25
- "1/%u", q.denom);
26
- pw_properties_setf(props, PW_KEY_NODE_LATENCY,
27
- "%u/%u", q.num, q.denom);
28
- }
29
- }
30
- if ((str = getenv("PIPEWIRE_LATENCY")) != NULL)
31
- pw_properties_set(props, PW_KEY_NODE_LATENCY, str);
32
- if ((str = getenv("PIPEWIRE_RATE")) != NULL)
33
- pw_properties_set(props, PW_KEY_NODE_RATE, str);
34
-
35
if (pw_properties_get(props, PW_KEY_NODE_NAME) == NULL && extra) {
36
str = pw_properties_get(extra, PW_KEY_APP_NAME);
37
if (str == NULL)
38
39
pw_properties_set(props, PW_KEY_NODE_NAME, str);
40
}
41
42
+ if ((pw_properties_get(props, PW_KEY_NODE_WANT_DRIVER) == NULL))
43
+ pw_properties_set(props, PW_KEY_NODE_WANT_DRIVER, "true");
44
+
45
+ pw_context_conf_update_props(context, "filter.properties", props);
46
+
47
this->name = name ? strdup(name) : NULL;
48
this->node_id = SPA_ID_INVALID;
49
50
51
{
52
struct filter *impl = SPA_CONTAINER_OF(filter, struct filter, this);
53
struct pw_properties *props = NULL;
54
+ const char *str;
55
+ struct match match;
56
int res;
57
uint32_t i;
58
59
60
61
if (flags & PW_FILTER_FLAG_DRIVER)
62
pw_properties_set(filter->properties, PW_KEY_NODE_DRIVER, "true");
63
- if ((pw_properties_get(filter->properties, PW_KEY_NODE_WANT_DRIVER) == NULL))
64
- pw_properties_set(filter->properties, PW_KEY_NODE_WANT_DRIVER, "true");
65
if (flags & PW_FILTER_FLAG_TRIGGER) {
66
pw_properties_set(filter->properties, PW_KEY_NODE_TRIGGER, "true");
67
impl->trigger = true;
68
}
69
70
+ match = MATCH_INIT(filter);
71
+ pw_context_conf_section_match_rules(impl->context, "filter.rules",
72
+ &filter->properties->dict, execute_match, &match);
73
+
74
+ if ((str = getenv("PIPEWIRE_PROPS")) != NULL)
75
+ pw_properties_update_string(filter->properties, str, strlen(str));
76
+ if ((str = getenv("PIPEWIRE_QUANTUM")) != NULL) {
77
+ struct spa_fraction q;
78
+ if (sscanf(str, "%u/%u", &q.num, &q.denom) == 2 && q.denom != 0) {
79
+ pw_properties_setf(filter->properties, PW_KEY_NODE_RATE,
80
+ "1/%u", q.denom);
81
+ pw_properties_setf(filter->properties, PW_KEY_NODE_LATENCY,
82
+ "%u/%u", q.num, q.denom);
83
+ }
84
+ }
85
+ if ((str = getenv("PIPEWIRE_LATENCY")) != NULL)
86
+ pw_properties_set(filter->properties, PW_KEY_NODE_LATENCY, str);
87
+ if ((str = getenv("PIPEWIRE_RATE")) != NULL)
88
+ pw_properties_set(filter->properties, PW_KEY_NODE_RATE, str);
89
+
90
if (filter->core == NULL) {
91
filter->core = pw_context_connect(impl->context,
92
pw_properties_copy(filter->properties), 0);
93
pipewire-0.3.74.tar.gz/src/pipewire/impl-client.c -> pipewire-0.3.76.tar.gz/src/pipewire/impl-client.c
Changed
19
1
2
if (context->current_client == client)
3
new_perm &= old_perm;
4
5
- pw_log_debug("%p: set default permissions %08x -> %08x",
6
+ pw_log_info("%p: set default permissions %08x -> %08x",
7
client, old_perm, new_perm);
8
9
def->permissions = new_perm;
10
11
if (context->current_client == client)
12
new_perm &= old_perm;
13
14
- pw_log_debug("%p: set global %d permissions %08x -> %08x",
15
+ pw_log_info("%p: set global %d permissions %08x -> %08x",
16
client, global->id, old_perm, new_perm);
17
18
p->permissions = new_perm;
19
pipewire-0.3.74.tar.gz/src/pipewire/impl-link.c -> pipewire-0.3.76.tar.gz/src/pipewire/impl-link.c
Changed
113
1
2
}
3
}
4
5
+static int check_owner_permissions(struct pw_context *context,
6
+ struct pw_impl_node *node, uint32_t id, uint32_t permissions)
7
+{
8
+ const char *str;
9
+ struct pw_impl_client *client;
10
+ struct pw_global *global;
11
+ uint32_t perms;
12
+ uint32_t client_id;
13
+
14
+ str = pw_properties_get(node->properties, PW_KEY_CLIENT_ID);
15
+ if (str == NULL)
16
+ /* node not owned by client */
17
+ return 0;
18
+
19
+ if (!spa_atou32(str, &client_id, 0))
20
+ /* invalid client_id, something is wrong */
21
+ return -EIO;
22
+ if ((global = pw_context_find_global(context, client_id)) == NULL)
23
+ /* current client can't see the owner client */
24
+ return -errno;
25
+ if (!pw_global_is_type(global, PW_TYPE_INTERFACE_Client) ||
26
+ (client = global->object) == NULL)
27
+ /* not the right object, something wrong */
28
+ return -EIO;
29
+
30
+ if ((global = pw_context_find_global(context, id)) == NULL)
31
+ /* current client can't see node id */
32
+ return -errno;
33
+
34
+ perms = pw_global_get_permissions(global, client);
35
+ if ((perms & permissions) != permissions)
36
+ /* owner client can't see other node */
37
+ return -EPERM;
38
+
39
+ return 0;
40
+}
41
+
42
static int
43
check_permission(struct pw_context *context,
44
struct pw_impl_port *output,
45
struct pw_impl_port *input,
46
struct pw_properties *properties)
47
{
48
+ int res;
49
+ if ((res = check_owner_permissions(context, output->node,
50
+ input->node->info.id, PW_PERM_R)) < 0)
51
+ return res;
52
+ if ((res = check_owner_permissions(context, input->node,
53
+ output->node->info.id, PW_PERM_R)) < 0)
54
+ return res;
55
return 0;
56
}
57
58
static void permissions_changed(struct pw_impl_link *this, struct pw_impl_port *other,
59
struct pw_impl_client *client, uint32_t old, uint32_t new)
60
{
61
+ int res;
62
uint32_t perm;
63
64
perm = pw_global_get_permissions(other->global, client);
65
66
new &= perm;
67
pw_log_debug("%p: permissions changed %08x -> %08x", this, old, new);
68
69
- if (check_permission(this->context, this->output, this->input, this->properties) < 0) {
70
+ if ((res = check_permission(this->context, this->output, this->input, this->properties)) < 0) {
71
+ pw_log_info("%p: link permissions removed: %s", this, spa_strerror(res));
72
pw_impl_link_destroy(this);
73
} else if (this->global != NULL) {
74
pw_global_update_permissions(this->global, client, old, new);
75
}
76
}
77
78
+static bool is_port_owner(struct pw_impl_client *client, struct pw_impl_port *port)
79
+{
80
+ const char *str;
81
+ uint32_t client_id;
82
+
83
+ str = pw_properties_get(port->node->properties, PW_KEY_CLIENT_ID);
84
+ if (str == NULL)
85
+ return false;
86
+
87
+ if (!spa_atou32(str, &client_id, 0))
88
+ return false;
89
+
90
+ return client_id == client->info.id;
91
+}
92
+
93
static void output_permissions_changed(void *data,
94
struct pw_impl_client *client, uint32_t old, uint32_t new)
95
{
96
struct pw_impl_link *this = data;
97
+ if (!is_port_owner(client, this->output) &&
98
+ !is_port_owner(client, this->input))
99
+ return;
100
permissions_changed(this, this->input, client, old, new);
101
}
102
103
104
struct pw_impl_client *client, uint32_t old, uint32_t new)
105
{
106
struct pw_impl_link *this = data;
107
+ if (!is_port_owner(client, this->output) &&
108
+ !is_port_owner(client, this->input))
109
+ return;
110
permissions_changed(this, this->output, client, old, new);
111
}
112
113
pipewire-0.3.74.tar.gz/src/pipewire/impl-node.c -> pipewire-0.3.76.tar.gz/src/pipewire/impl-node.c
Changed
197
1
2
break;
3
}
4
spa_list_append(&n->driver_link, &node->driver_link);
5
+ pw_context_emit_driver_added(context, node);
6
+}
7
+
8
+static inline void remove_driver(struct pw_context *context, struct pw_impl_node *node)
9
+{
10
+ spa_list_remove(&node->driver_link);
11
+ pw_context_emit_driver_removed(context, node);
12
}
13
14
static void update_io(struct pw_impl_node *node)
15
16
static void remove_segment_owner(struct pw_impl_node *driver, uint32_t node_id)
17
{
18
struct pw_node_activation *a = driver->rt.target.activation;
19
- ATOMIC_CAS(a->segment_owner0, node_id, 0);
20
- ATOMIC_CAS(a->segment_owner1, node_id, 0);
21
+ SPA_ATOMIC_CAS(a->segment_owner0, node_id, 0);
22
+ SPA_ATOMIC_CAS(a->segment_owner1, node_id, 0);
23
}
24
25
SPA_EXPORT
26
27
if (node->registered) {
28
if (driver)
29
insert_driver(context, node);
30
- else
31
- spa_list_remove(&node->driver_link);
32
+ else {
33
+ remove_driver(context, node);
34
+ }
35
}
36
if (driver && node->driver_node == node)
37
node->driving = true;
38
39
pw_log_trace_fp("%p: (%s-%u) state:%p pending:%d/%d", t->node,
40
t->name, t->id, state, state->pending, state->required);
41
42
- if (pw_node_activation_state_dec(state, 1)) {
43
+ if (pw_node_activation_state_dec(state)) {
44
a->status = PW_NODE_ACTIVATION_TRIGGERED;
45
a->signal_time = nsec;
46
if (SPA_UNLIKELY(spa_system_eventfd_write(t->system, t->fd, 1) < 0))
47
48
/* calculate CPU time when finished */
49
a->signal_time = this->driver_start;
50
calculate_stats(this, a);
51
- pw_context_driver_emit_complete(this->context, this);
52
+ pw_impl_node_rt_emit_complete(this);
53
+// pw_context_driver_emit_complete(this->context, this);
54
}
55
56
if (SPA_UNLIKELY(status & SPA_STATUS_DRAINED))
57
- pw_context_driver_emit_drained(this->context, this);
58
+ pw_impl_node_rt_emit_drained(this);
59
60
return status;
61
}
62
63
struct pw_node_activation *a = node->rt.target.activation;
64
struct pw_node_activation_state *state = &a->state0;
65
66
- if (pw_node_activation_state_dec(state, 1)) {
67
+ if (pw_node_activation_state_dec(state)) {
68
uint64_t nsec = get_time_ns(node->data_system);
69
a->status = PW_NODE_ACTIVATION_TRIGGERED;
70
a->signal_time = nsec;
71
72
spa_list_init(&this->peer_list);
73
74
spa_hook_list_init(&this->listener_list);
75
+ spa_hook_list_init(&this->rt_listener_list);
76
77
this->info.state = PW_NODE_STATE_CREATING;
78
this->info.props = &this->properties->dict;
79
80
if (SPA_UNLIKELY(a->position.offset == INT64_MIN))
81
a->position.offset = a->position.clock.position;
82
83
- command = ATOMIC_XCHG(a->command, PW_NODE_ACTIVATION_COMMAND_NONE);
84
- *reposition_owner = ATOMIC_XCHG(a->reposition_owner, 0);
85
+ command = SPA_ATOMIC_XCHG(a->command, PW_NODE_ACTIVATION_COMMAND_NONE);
86
+ *reposition_owner = SPA_ATOMIC_XCHG(a->reposition_owner, 0);
87
88
if (SPA_UNLIKELY(command != PW_NODE_ACTIVATION_COMMAND_NONE)) {
89
pw_log_debug("%p: update command:%u", node, command);
90
91
pw_log_warn("(%s-%u) sync timeout, going to RUNNING",
92
node->name, node->info.id);
93
check_states(node, nsec);
94
- pw_context_driver_emit_timeout(node->context, node);
95
+ pw_impl_node_rt_emit_timeout(node);
96
all_ready = true;
97
}
98
if (all_ready)
99
100
state, a->position.clock.duration,
101
state->pending, state->required);
102
check_states(node, nsec);
103
- pw_context_driver_emit_incomplete(node->context, node);
104
+ pw_impl_node_rt_emit_incomplete(node);
105
}
106
107
/* This update is done too late, the driver should do this
108
109
}
110
111
sync_type = check_updates(node, &reposition_owner);
112
- owner0 = ATOMIC_LOAD(a->segment_owner0);
113
- owner1 = ATOMIC_LOAD(a->segment_owner1);
114
+ owner0 = SPA_ATOMIC_LOAD(a->segment_owner0);
115
+ owner1 = SPA_ATOMIC_LOAD(a->segment_owner1);
116
again:
117
all_ready = sync_type == SYNC_CHECK;
118
update_sync = !all_ready;
119
120
121
update_position(node, all_ready, nsec);
122
123
- pw_context_driver_emit_start(node->context, node);
124
+ pw_impl_node_rt_emit_start(node);
125
}
126
/* this should not happen, driver nodes that are not currently driving
127
* should not emit the ready callback */
128
129
missed);
130
}
131
132
- pw_context_driver_emit_xrun(this->context, this);
133
+ pw_impl_node_rt_emit_xrun(this);
134
135
return 0;
136
}
137
138
spa_hook_list_append(&node->listener_list, listener, events, data);
139
}
140
141
+struct listener_data {
142
+ struct spa_hook *listener;
143
+ const struct pw_impl_node_rt_events *events;
144
+ void *data;
145
+};
146
+
147
+static int
148
+do_add_rt_listener(struct spa_loop *loop,
149
+ bool async, uint32_t seq, const void *data, size_t size, void *user_data)
150
+{
151
+ struct pw_impl_node *node = user_data;
152
+ const struct listener_data *d = data;
153
+ spa_hook_list_append(&node->rt_listener_list,
154
+ d->listener, d->events, d->data);
155
+ return 0;
156
+}
157
+
158
+SPA_EXPORT
159
+void pw_impl_node_add_rt_listener(struct pw_impl_node *node,
160
+ struct spa_hook *listener,
161
+ const struct pw_impl_node_rt_events *events,
162
+ void *data)
163
+{
164
+ struct listener_data d = { .listener = listener, .events = events, .data = data };
165
+ pw_loop_invoke(node->data_loop,
166
+ do_add_rt_listener, SPA_ID_INVALID, &d, sizeof(d), false, node);
167
+}
168
+
169
+static int do_remove_listener(struct spa_loop *loop,
170
+ bool async, uint32_t seq, const void *data, size_t size, void *user_data)
171
+{
172
+ struct spa_hook *listener = user_data;
173
+ spa_hook_remove(listener);
174
+ return 0;
175
+}
176
+
177
+SPA_EXPORT
178
+void pw_impl_node_remove_rt_listener(struct pw_impl_node *node,
179
+ struct spa_hook *listener)
180
+{
181
+ pw_loop_invoke(node->data_loop,
182
+ do_remove_listener, SPA_ID_INVALID, NULL, 0, true, listener);
183
+}
184
+
185
/** Destroy a node
186
* \param node a node to destroy
187
*
188
189
if (node->registered) {
190
spa_list_remove(&node->link);
191
if (node->driver)
192
- spa_list_remove(&node->driver_link);
193
+ remove_driver(context, node);
194
}
195
196
if (node->node) {
197
pipewire-0.3.74.tar.gz/src/pipewire/impl-node.h -> pipewire-0.3.76.tar.gz/src/pipewire/impl-node.h
Changed
40
1
2
void (*peer_removed) (void *data, struct pw_impl_node *peer);
3
};
4
5
+struct pw_impl_node_rt_events {
6
+#define PW_VERSION_IMPL_NODE_RT_EVENTS 0
7
+ uint32_t version;
8
+ /** the node is drained */
9
+ void (*drained) (void *data);
10
+ /** the node had an xrun */
11
+ void (*xrun) (void *data);
12
+ /** the driver node starts processing */
13
+ void (*start) (void *data);
14
+ /** the driver node completed processing */
15
+ void (*complete) (void *data);
16
+ /** the driver node did not complete processing */
17
+ void (*incomplete) (void *data);
18
+ /** the node had */
19
+ void (*timeout) (void *data);
20
+};
21
+
22
/** Create a new node */
23
struct pw_impl_node *
24
pw_context_create_node(struct pw_context *context, /**< the context */
25
26
const struct pw_impl_node_events *events,
27
void *data);
28
29
+/** Add an rt_event listener */
30
+void pw_impl_node_add_rt_listener(struct pw_impl_node *node,
31
+ struct spa_hook *listener,
32
+ const struct pw_impl_node_rt_events *events,
33
+ void *data);
34
+void pw_impl_node_remove_rt_listener(struct pw_impl_node *node,
35
+ struct spa_hook *listener);
36
+
37
/** Iterate the ports in the given direction. The callback should return
38
* 0 to fetch the next item, any other value stops the iteration and returns
39
* the value. When all callbacks return 0, this function returns 0 when all
40
pipewire-0.3.74.tar.gz/src/pipewire/pipewire.c -> pipewire-0.3.76.tar.gz/src/pipewire/pipewire.c
Changed
14
1
2
return pw_get_headers_version();
3
}
4
5
+SPA_EXPORT
6
+bool pw_check_library_version(int major, int minor, int micro)
7
+{
8
+ return PW_CHECK_VERSION(major, minor, micro);
9
+}
10
+
11
static const struct spa_type_info type_info = {
12
{ SPA_ID_INVALID, SPA_ID_INVALID, "spa_types", spa_types },
13
{ 0, 0, NULL, NULL },
14
pipewire-0.3.74.tar.gz/src/pipewire/private.h -> pipewire-0.3.76.tar.gz/src/pipewire/private.h
Changed
133
1
2
#include <spa/support/plugin.h>
3
#include <spa/pod/builder.h>
4
#include <spa/param/latency-utils.h>
5
+#include <spa/utils/atomic.h>
6
#include <spa/utils/ratelimit.h>
7
#include <spa/utils/result.h>
8
#include <spa/utils/type-info.h>
9
10
} \
11
})
12
13
-#define pw_context_driver_emit(c,m,v,...) spa_hook_list_call_simple(&c->driver_listener_list, struct pw_context_driver_events, m, v, ##__VA_ARGS__)
14
-#define pw_context_driver_emit_start(c,n) pw_context_driver_emit(c, start, 0, n)
15
-#define pw_context_driver_emit_xrun(c,n) pw_context_driver_emit(c, xrun, 0, n)
16
-#define pw_context_driver_emit_incomplete(c,n) pw_context_driver_emit(c, incomplete, 0, n)
17
-#define pw_context_driver_emit_timeout(c,n) pw_context_driver_emit(c, timeout, 0, n)
18
-#define pw_context_driver_emit_drained(c,n) pw_context_driver_emit(c, drained, 0, n)
19
-#define pw_context_driver_emit_complete(c,n) pw_context_driver_emit(c, complete, 0, n)
20
-
21
-struct pw_context_driver_events {
22
-#define PW_VERSION_CONTEXT_DRIVER_EVENTS 0
23
- uint32_t version;
24
-
25
- /** The driver graph is started */
26
- void (*start) (void *data, struct pw_impl_node *node);
27
- /** The driver under/overruns */
28
- void (*xrun) (void *data, struct pw_impl_node *node);
29
- /** The driver could not complete the graph */
30
- void (*incomplete) (void *data, struct pw_impl_node *node);
31
- /** The driver got a sync timeout */
32
- void (*timeout) (void *data, struct pw_impl_node *node);
33
- /** a node drained */
34
- void (*drained) (void *data, struct pw_impl_node *node);
35
- /** The driver completed the graph */
36
- void (*complete) (void *data, struct pw_impl_node *node);
37
-};
38
-
39
-void pw_context_driver_add_listener(struct pw_context *context,
40
- struct spa_hook *listener,
41
- const struct pw_context_driver_events *events,
42
- void *data);
43
-void pw_context_driver_remove_listener(struct pw_context *context,
44
- struct spa_hook *listener);
45
-
46
#define pw_registry_resource(r,m,v,...) pw_resource_call(r, struct pw_registry_events,m,v,##__VA_ARGS__)
47
#define pw_registry_resource_global(r,...) pw_registry_resource(r,global,0,__VA_ARGS__)
48
#define pw_registry_resource_global_remove(r,...) pw_registry_resource(r,global_remove,0,__VA_ARGS__)
49
50
#define pw_context_emit_check_access(c,cl) pw_context_emit(c, check_access, 0, cl)
51
#define pw_context_emit_global_added(c,g) pw_context_emit(c, global_added, 0, g)
52
#define pw_context_emit_global_removed(c,g) pw_context_emit(c, global_removed, 0, g)
53
+#define pw_context_emit_driver_added(c,n) pw_context_emit(c, driver_added, 1, n)
54
+#define pw_context_emit_driver_removed(c,n) pw_context_emit(c, driver_removed, 1, n)
55
56
struct pw_context {
57
struct pw_impl_core *core; /**< core object */
58
59
state->pending = state->required;
60
}
61
62
-#define pw_node_activation_state_dec(s,c) (__atomic_sub_fetch(&(s)->pending, c, __ATOMIC_SEQ_CST) == 0)
63
+#define pw_node_activation_state_dec(s) (SPA_ATOMIC_DEC(s->pending) == 0)
64
65
struct pw_node_target {
66
struct spa_list link;
67
68
* to update wins */
69
};
70
71
-#define ATOMIC_CAS(v,ov,nv) \
72
-({ \
73
- __typeof__(v) __ov = (ov); \
74
- __atomic_compare_exchange_n(&(v), &__ov, (nv), \
75
- 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); \
76
-})
77
-
78
-#define ATOMIC_DEC(s) __atomic_sub_fetch(&(s), 1, __ATOMIC_SEQ_CST)
79
-#define ATOMIC_INC(s) __atomic_add_fetch(&(s), 1, __ATOMIC_SEQ_CST)
80
-#define ATOMIC_LOAD(s) __atomic_load_n(&(s), __ATOMIC_SEQ_CST)
81
-#define ATOMIC_STORE(s,v) __atomic_store_n(&(s), (v), __ATOMIC_SEQ_CST)
82
-#define ATOMIC_XCHG(s,v) __atomic_exchange_n(&(s), (v), __ATOMIC_SEQ_CST)
83
-
84
-#define SEQ_WRITE(s) ATOMIC_INC(s)
85
-#define SEQ_WRITE_SUCCESS(s1,s2) ((s1) + 1 == (s2) && ((s2) & 1) == 0)
86
-
87
-#define SEQ_READ(s) ATOMIC_LOAD(s)
88
-#define SEQ_READ_SUCCESS(s1,s2) ((s1) == (s2) && ((s2) & 1) == 0)
89
-
90
#define pw_impl_node_emit(o,m,v,...) spa_hook_list_call(&o->listener_list, struct pw_impl_node_events, m, v, ##__VA_ARGS__)
91
#define pw_impl_node_emit_destroy(n) pw_impl_node_emit(n, destroy, 0)
92
#define pw_impl_node_emit_free(n) pw_impl_node_emit(n, free, 0)
93
94
#define pw_impl_node_emit_peer_added(n,p) pw_impl_node_emit(n, peer_added, 0, p)
95
#define pw_impl_node_emit_peer_removed(n,p) pw_impl_node_emit(n, peer_removed, 0, p)
96
97
+#define pw_impl_node_rt_emit(o,m,v,...) spa_hook_list_call(&o->rt_listener_list, struct pw_impl_node_rt_events, m, v, ##__VA_ARGS__)
98
+#define pw_impl_node_rt_emit_drained(n) pw_impl_node_rt_emit(n, drained, 0)
99
+#define pw_impl_node_rt_emit_xrun(n) pw_impl_node_rt_emit(n, xrun, 0)
100
+#define pw_impl_node_rt_emit_start(n) pw_impl_node_rt_emit(n, start, 0)
101
+#define pw_impl_node_rt_emit_complete(n) pw_impl_node_rt_emit(n, complete, 0)
102
+#define pw_impl_node_rt_emit_incomplete(n) pw_impl_node_rt_emit(n, incomplete, 0)
103
+#define pw_impl_node_rt_emit_timeout(n) pw_impl_node_rt_emit(n, timeout, 0)
104
+
105
struct pw_impl_node {
106
struct pw_context *context; /**< context object */
107
struct spa_list link; /**< link in context node_list */
108
109
struct pw_map output_port_map; /**< map from port_id to port */
110
111
struct spa_hook_list listener_list;
112
+ struct spa_hook_list rt_listener_list;
113
114
struct pw_loop *data_loop; /**< the data loop for this node */
115
struct spa_system *data_system;
116
117
118
struct pw_impl_node *node;
119
struct spa_hook node_listener;
120
+ struct spa_hook node_rt_listener;
121
122
struct spa_list controls;
123
};
124
125
int pw_settings_expose(struct pw_context *context);
126
void pw_settings_clean(struct pw_context *context);
127
128
-pthread_attr_t *pw_thread_fill_attr(const struct spa_dict *props, pthread_attr_t *attr);
129
-
130
/** \endcond */
131
132
#ifdef __cplusplus
133
pipewire-0.3.74.tar.gz/src/pipewire/stream.c -> pipewire-0.3.76.tar.gz/src/pipewire/stream.c
Changed
327
1
2
#include <spa/pod/filter.h>
3
#include <spa/pod/dynamic.h>
4
#include <spa/debug/types.h>
5
-#include <spa/debug/dict.h>
6
7
#define PW_ENABLE_DEPRECATED
8
9
10
const char *path;
11
12
struct pw_context *context;
13
- struct spa_hook context_listener;
14
15
struct pw_loop *main_loop;
16
struct pw_loop *data_loop;
17
18
buffer->this.requested = impl->quantum;
19
res = 1;
20
}
21
- pw_log_trace_fp("%p: update buffer:%u size:%"PRIu64, impl, id, buffer->this.requested);
22
+ pw_log_trace_fp("%p: update buffer:%u req:%"PRIu64, impl, id, buffer->this.requested);
23
return res;
24
}
25
26
27
28
static void call_drained(struct stream *impl)
29
{
30
+ pw_log_info("%p: drained", impl);
31
pw_loop_invoke(impl->main_loop,
32
do_call_drained, 1, NULL, 0, false, impl);
33
}
34
35
{
36
struct spa_io_position *p = impl->rt.position;
37
38
- SEQ_WRITE(impl->seq);
39
+ SPA_SEQ_WRITE(impl->seq);
40
if (SPA_LIKELY(p != NULL)) {
41
impl->time.now = p->clock.nsec;
42
impl->time.rate = p->clock.rate;
43
44
}
45
if (SPA_LIKELY(impl->rate_match != NULL))
46
impl->rate_queued = impl->rate_match->delay;
47
- SEQ_WRITE(impl->seq);
48
+ SPA_SEQ_WRITE(impl->seq);
49
}
50
51
static int impl_send_command(void *object, const struct spa_command *command)
52
53
54
while ((b = queue_pop(impl, &impl->dequeued))) {
55
if (b->busy)
56
- ATOMIC_DEC(b->busy->count);
57
+ SPA_ATOMIC_DEC(b->busy->count);
58
}
59
} else
60
clear_queue(impl, &impl->dequeued);
61
62
pw_log_trace_fp("%p: push %d %p", stream, b->id, io);
63
if (queue_push(impl, &impl->dequeued, b) == 0) {
64
if (b->busy)
65
- ATOMIC_INC(b->busy->count);
66
+ SPA_ATOMIC_INC(b->busy->count);
67
}
68
}
69
if (!queue_is_empty(impl, &impl->dequeued)) {
70
71
struct pw_stream *stream = data;
72
struct stream *impl = SPA_CONTAINER_OF(stream, struct stream, this);
73
spa_hook_remove(&stream->node_listener);
74
+ pw_impl_node_remove_rt_listener(stream->node, &stream->node_rt_listener);
75
stream->node = NULL;
76
impl->data_loop = NULL;
77
}
78
79
.error = on_core_error,
80
};
81
82
-static void context_drained(void *data, struct pw_impl_node *node)
83
+static void node_drained(void *data)
84
{
85
struct stream *impl = data;
86
- if (impl->this.node != node)
87
- return;
88
if (impl->draining && impl->drained) {
89
impl->draining = false;
90
if (impl->io != NULL)
91
92
}
93
}
94
95
-static const struct pw_context_driver_events context_events = {
96
- PW_VERSION_CONTEXT_DRIVER_EVENTS,
97
- .drained = context_drained,
98
+static const struct pw_impl_node_rt_events node_rt_events = {
99
+ PW_VERSION_IMPL_NODE_RT_EVENTS,
100
+ .drained = node_drained,
101
};
102
103
struct match {
104
105
struct stream *impl;
106
struct pw_stream *this;
107
const char *str;
108
- struct match match;
109
int res;
110
111
ensure_loop(context->main_loop, return NULL);
112
113
spa_hook_list_init(&impl->hooks);
114
this->properties = props;
115
116
- pw_context_conf_update_props(context, "stream.properties", props);
117
-
118
- match = MATCH_INIT(this);
119
- pw_context_conf_section_match_rules(context, "stream.rules",
120
- &this->properties->dict, execute_match, &match);
121
-
122
- if ((str = getenv("PIPEWIRE_PROPS")) != NULL)
123
- pw_properties_update_string(props, str, strlen(str));
124
- if ((str = getenv("PIPEWIRE_QUANTUM")) != NULL) {
125
- struct spa_fraction q;
126
- if (sscanf(str, "%u/%u", &q.num, &q.denom) == 2 && q.denom != 0) {
127
- pw_properties_setf(props, PW_KEY_NODE_RATE,
128
- "1/%u", q.denom);
129
- pw_properties_setf(props, PW_KEY_NODE_LATENCY,
130
- "%u/%u", q.num, q.denom);
131
- }
132
- }
133
- if ((str = getenv("PIPEWIRE_LATENCY")) != NULL)
134
- pw_properties_set(props, PW_KEY_NODE_LATENCY, str);
135
- if ((str = getenv("PIPEWIRE_RATE")) != NULL)
136
- pw_properties_set(props, PW_KEY_NODE_RATE, str);
137
-
138
if (pw_properties_get(props, PW_KEY_STREAM_IS_LIVE) == NULL)
139
pw_properties_set(props, PW_KEY_STREAM_IS_LIVE, "true");
140
if (pw_properties_get(props, PW_KEY_NODE_NAME) == NULL && extra) {
141
142
str = name;
143
pw_properties_set(props, PW_KEY_NODE_NAME, str);
144
}
145
+ if ((pw_properties_get(props, PW_KEY_NODE_WANT_DRIVER) == NULL))
146
+ pw_properties_set(props, PW_KEY_NODE_WANT_DRIVER, "true");
147
+
148
+ pw_context_conf_update_props(context, "stream.properties", props);
149
150
this->name = name ? strdup(name) : NULL;
151
this->node_id = SPA_ID_INVALID;
152
153
impl->allow_mlock = context->settings.mem_allow_mlock;
154
impl->warn_mlock = context->settings.mem_warn_mlock;
155
156
- pw_context_driver_add_listener(impl->context,
157
- &impl->context_listener,
158
- &context_events, impl);
159
return impl;
160
161
error_properties:
162
163
spa_hook_list_clean(&impl->hooks);
164
spa_hook_list_clean(&stream->listener_list);
165
166
- pw_context_driver_remove_listener(impl->context,
167
- &impl->context_listener);
168
-
169
if (impl->data.context)
170
pw_context_destroy(impl->data.context);
171
172
173
struct pw_impl_factory *factory;
174
struct pw_properties *props = NULL;
175
const char *str;
176
+ struct match match;
177
uint32_t i;
178
int res;
179
180
181
impl->using_trigger = false;
182
stream_set_state(stream, PW_STREAM_STATE_CONNECTING, 0, NULL);
183
184
- if ((str = getenv("PIPEWIRE_NODE")) != NULL)
185
- pw_properties_set(stream->properties, PW_KEY_TARGET_OBJECT, str);
186
- else if (target_id != PW_ID_ANY)
187
+ if (target_id != PW_ID_ANY)
188
/* XXX this is deprecated but still used by the portal and its apps */
189
- pw_properties_setf(stream->properties, PW_KEY_NODE_TARGET, "%d", target_id);
190
+ if (pw_properties_get(stream->properties, PW_KEY_NODE_TARGET) == NULL)
191
+ pw_properties_setf(stream->properties, PW_KEY_NODE_TARGET, "%d", target_id);
192
+ if (flags & PW_STREAM_FLAG_AUTOCONNECT)
193
+ if (pw_properties_get(stream->properties, PW_KEY_NODE_AUTOCONNECT) == NULL)
194
+ pw_properties_set(stream->properties, PW_KEY_NODE_AUTOCONNECT, "true");
195
+ if (flags & PW_STREAM_FLAG_EXCLUSIVE)
196
+ if (pw_properties_get(stream->properties, PW_KEY_NODE_EXCLUSIVE) == NULL)
197
+ pw_properties_set(stream->properties, PW_KEY_NODE_EXCLUSIVE, "true");
198
+ if (flags & PW_STREAM_FLAG_DONT_RECONNECT)
199
+ if (pw_properties_get(stream->properties, PW_KEY_NODE_DONT_RECONNECT) == NULL)
200
+ pw_properties_set(stream->properties, PW_KEY_NODE_DONT_RECONNECT, "true");
201
202
- if ((str = getenv("PIPEWIRE_AUTOCONNECT")) != NULL)
203
- pw_properties_set(stream->properties,
204
- PW_KEY_NODE_AUTOCONNECT, spa_atob(str) ? "true" : "false");
205
- else if ((flags & PW_STREAM_FLAG_AUTOCONNECT) &&
206
- pw_properties_get(stream->properties, PW_KEY_NODE_AUTOCONNECT) == NULL) {
207
- pw_properties_set(stream->properties, PW_KEY_NODE_AUTOCONNECT, "true");
208
- }
209
if (flags & PW_STREAM_FLAG_DRIVER)
210
pw_properties_set(stream->properties, PW_KEY_NODE_DRIVER, "true");
211
- if ((pw_properties_get(stream->properties, PW_KEY_NODE_WANT_DRIVER) == NULL))
212
- pw_properties_set(stream->properties, PW_KEY_NODE_WANT_DRIVER, "true");
213
-
214
- if (flags & PW_STREAM_FLAG_EXCLUSIVE)
215
- pw_properties_set(stream->properties, PW_KEY_NODE_EXCLUSIVE, "true");
216
- if (flags & PW_STREAM_FLAG_DONT_RECONNECT)
217
- pw_properties_set(stream->properties, PW_KEY_NODE_DONT_RECONNECT, "true");
218
if (flags & PW_STREAM_FLAG_TRIGGER) {
219
pw_properties_set(stream->properties, PW_KEY_NODE_TRIGGER, "true");
220
impl->trigger = true;
221
}
222
-
223
- if ((str = pw_properties_get(stream->properties, "mem.warn-mlock")) != NULL)
224
- impl->warn_mlock = pw_properties_parse_bool(str);
225
-
226
if ((pw_properties_get(stream->properties, PW_KEY_MEDIA_CLASS) == NULL)) {
227
const char *media_type = pw_properties_get(stream->properties, PW_KEY_MEDIA_TYPE);
228
pw_properties_setf(stream->properties, PW_KEY_MEDIA_CLASS, "Stream/%s/%s",
229
direction == PW_DIRECTION_INPUT ? "Input" : "Output",
230
media_type ? media_type : get_media_class(impl));
231
}
232
-
233
if ((str = pw_properties_get(stream->properties, PW_KEY_FORMAT_DSP)) != NULL)
234
pw_properties_set(impl->port_props, PW_KEY_FORMAT_DSP, str);
235
else if (impl->media_type == SPA_MEDIA_TYPE_application &&
236
impl->media_subtype == SPA_MEDIA_SUBTYPE_control)
237
pw_properties_set(impl->port_props, PW_KEY_FORMAT_DSP, "8 bit raw midi");
238
239
+ match = MATCH_INIT(stream);
240
+ pw_context_conf_section_match_rules(impl->context, "stream.rules",
241
+ &stream->properties->dict, execute_match, &match);
242
+
243
+ if ((str = getenv("PIPEWIRE_NODE")) != NULL)
244
+ pw_properties_set(stream->properties, PW_KEY_TARGET_OBJECT, str);
245
+ if ((str = getenv("PIPEWIRE_AUTOCONNECT")) != NULL)
246
+ pw_properties_set(stream->properties,
247
+ PW_KEY_NODE_AUTOCONNECT, spa_atob(str) ? "true" : "false");
248
+
249
+ if ((str = getenv("PIPEWIRE_PROPS")) != NULL)
250
+ pw_properties_update_string(stream->properties, str, strlen(str));
251
+ if ((str = getenv("PIPEWIRE_QUANTUM")) != NULL) {
252
+ struct spa_fraction q;
253
+ if (sscanf(str, "%u/%u", &q.num, &q.denom) == 2 && q.denom != 0) {
254
+ pw_properties_setf(stream->properties, PW_KEY_NODE_RATE,
255
+ "1/%u", q.denom);
256
+ pw_properties_setf(stream->properties, PW_KEY_NODE_LATENCY,
257
+ "%u/%u", q.num, q.denom);
258
+ }
259
+ }
260
+ if ((str = getenv("PIPEWIRE_LATENCY")) != NULL)
261
+ pw_properties_set(stream->properties, PW_KEY_NODE_LATENCY, str);
262
+ if ((str = getenv("PIPEWIRE_RATE")) != NULL)
263
+ pw_properties_set(stream->properties, PW_KEY_NODE_RATE, str);
264
+
265
+ if ((str = pw_properties_get(stream->properties, "mem.warn-mlock")) != NULL)
266
+ impl->warn_mlock = pw_properties_parse_bool(str);
267
+ if ((str = pw_properties_get(stream->properties, "mem.allow-mlock")) != NULL)
268
+ impl->allow_mlock = pw_properties_parse_bool(str);
269
+
270
impl->port_info.props = &impl->port_props->dict;
271
272
if (stream->core == NULL) {
273
274
pw_proxy_add_listener(stream->proxy, &stream->proxy_listener, &proxy_events, stream);
275
276
pw_impl_node_add_listener(stream->node, &stream->node_listener, &node_events, stream);
277
+ pw_impl_node_add_rt_listener(stream->node, &stream->node_rt_listener,
278
+ &node_rt_events, stream);
279
280
return 0;
281
282
283
uint32_t buffered, quantum, index;
284
285
do {
286
- seq1 = SEQ_READ(impl->seq);
287
+ seq1 = SPA_SEQ_READ(impl->seq);
288
memcpy(time, &impl->time, SPA_MIN(size, sizeof(struct pw_time)));
289
buffered = impl->rate_queued;
290
quantum = impl->quantum;
291
- seq2 = SEQ_READ(impl->seq);
292
- } while (!SEQ_READ_SUCCESS(seq1, seq2));
293
+ seq2 = SPA_SEQ_READ(impl->seq);
294
+ } while (!SPA_SEQ_READ_SUCCESS(seq1, seq2));
295
296
if (impl->direction == SPA_DIRECTION_INPUT)
297
time->queued = (int64_t)(time->queued - impl->dequeued.outcount);
298
299
errno = -res;
300
return NULL;
301
}
302
- pw_log_trace_fp("%p: dequeue buffer %d size:%"PRIu64, stream, b->id, b->this.size);
303
+ pw_log_trace_fp("%p: dequeue buffer %d size:%"PRIu64" req:%"PRIu64,
304
+ stream, b->id, b->this.size, b->this.requested);
305
306
if (b->busy && impl->direction == SPA_DIRECTION_OUTPUT) {
307
- if (ATOMIC_INC(b->busy->count) > 1) {
308
- ATOMIC_DEC(b->busy->count);
309
+ if (SPA_ATOMIC_INC(b->busy->count) > 1) {
310
+ SPA_ATOMIC_DEC(b->busy->count);
311
queue_push(impl, &impl->dequeued, b);
312
pw_log_trace_fp("%p: buffer busy", stream);
313
errno = EBUSY;
314
315
int res;
316
317
if (b->busy)
318
- ATOMIC_DEC(b->busy->count);
319
+ SPA_ATOMIC_DEC(b->busy->count);
320
321
- pw_log_trace_fp("%p: queue buffer %d", stream, b->id);
322
+ pw_log_trace_fp("%p: queue buffer %d size:%"PRIu64, stream, b->id,
323
+ b->this.size);
324
if ((res = queue_push(impl, &impl->queued, b)) < 0)
325
return res;
326
327
pipewire-0.3.74.tar.gz/src/pipewire/thread-loop.c -> pipewire-0.3.76.tar.gz/src/pipewire/thread-loop.c
Changed
31
1
2
int n_waiting_for_accept;
3
unsigned int created:1;
4
unsigned int running:1;
5
+ unsigned int start_signal:1;
6
};
7
/** \endcond */
8
9
10
return NULL;
11
12
pw_log_debug("%p: new name:%s", this, name);
13
+ if (props != NULL) {
14
+ const char *str = spa_dict_lookup(props, "thread-loop.start-signal");
15
+ if (str != NULL)
16
+ this->start_signal = spa_atob(str);
17
+ }
18
19
if (loop == NULL) {
20
loop = pw_loop_new(props);
21
22
pw_log_debug("%p: enter thread", this);
23
pw_loop_enter(this->loop);
24
25
+ if (this->start_signal)
26
+ pw_thread_loop_signal(this, false);
27
+
28
while (this->running) {
29
if ((res = pw_loop_iterate(this->loop, -1)) < 0) {
30
if (res == -EINTR)
31
pipewire-0.3.74.tar.gz/src/pipewire/thread.c -> pipewire-0.3.76.tar.gz/src/pipewire/thread.c
Changed
12
1
2
} while(false);
3
4
SPA_EXPORT
5
-pthread_attr_t *pw_thread_fill_attr(const struct spa_dict *props, pthread_attr_t *attr)
6
+void *pw_thread_fill_attr(const struct spa_dict *props, void *_attr)
7
{
8
+ pthread_attr_t *attr = _attr;
9
const char *str;
10
int res;
11
12
pipewire-0.3.74.tar.gz/src/pipewire/thread.h -> pipewire-0.3.76.tar.gz/src/pipewire/thread.h
Changed
9
1
2
SPA_DEPRECATED
3
void pw_thread_utils_set(struct spa_thread_utils *impl);
4
struct spa_thread_utils *pw_thread_utils_get(void);
5
+void *pw_thread_fill_attr(const struct spa_dict *props, void *attr);
6
7
#define pw_thread_utils_create(...) spa_thread_utils_create(pw_thread_utils_get(), ##__VA_ARGS__)
8
#define pw_thread_utils_join(...) spa_thread_utils_join(pw_thread_utils_get(), ##__VA_ARGS__)
9
pipewire-0.3.74.tar.gz/src/pipewire/version.h.in -> pipewire-0.3.76.tar.gz/src/pipewire/version.h.in
Changed
21
1
2
extern "C" {
3
#endif
4
5
+#include <stdbool.h>
6
+
7
/** Return the version of the header files. Keep in mind that this is
8
a macro and not a function, so it is impossible to get the pointer of
9
it. */
10
11
* linked to. */
12
const char* pw_get_library_version(void);
13
14
+/** Return TRUE if the currently linked PipeWire library version is equal
15
+ * or newer than the specified version. Since 0.3.75 */
16
+bool pw_check_library_version(int major, int minor, int micro);
17
+
18
/** The current API version. Versions prior to 0.2.0 have
19
* PW_API_VERSION undefined. Please note that this is only ever
20
* increased on incompatible API changes! */
21
pipewire-0.3.74.tar.gz/src/tools/pw-cat.c -> pipewire-0.3.76.tar.gz/src/tools/pw-cat.c
Changed
13
1
2
error);
3
pw_main_loop_quit(data->loop);
4
break;
5
+ case PW_STREAM_STATE_UNCONNECTED:
6
+ printf("stream node %"PRIu32" unconnected\n",
7
+ pw_stream_get_node_id(data->stream));
8
+ pw_main_loop_quit(data->loop);
9
+ break;
10
default:
11
break;
12
}
13
pipewire-0.3.74.tar.gz/test/test-context.c -> pipewire-0.3.76.tar.gz/test/test-context.c
Changed
47
1
2
void (*check_access) (void *data, struct pw_impl_client *client);
3
void (*global_added) (void *data, struct pw_global *global);
4
void (*global_removed) (void *data, struct pw_global *global);
5
+ void (*driver_added) (void *data, struct pw_impl_node *node);
6
+ void (*driver_removed) (void *data, struct pw_impl_node *node);
7
} test = { PW_VERSION_CONTEXT_EVENTS, NULL };
8
9
pw_init(0, NULL);
10
11
TEST_FUNC(ev, test, check_access);
12
TEST_FUNC(ev, test, global_added);
13
TEST_FUNC(ev, test, global_removed);
14
+ TEST_FUNC(ev, test, driver_added);
15
+ TEST_FUNC(ev, test, driver_removed);
16
17
- pwtest_int_eq(PW_VERSION_CONTEXT_EVENTS, 0);
18
+ pwtest_int_eq(PW_VERSION_CONTEXT_EVENTS, 1);
19
pwtest_int_eq(sizeof(ev), sizeof(test));
20
21
pw_deinit();
22
23
{
24
pwtest_fail_if_reached();
25
}
26
-
27
+static void context_driver_added_error(void *data, struct pw_impl_node *node)
28
+{
29
+ pwtest_fail_if_reached();
30
+}
31
+static void context_driver_removed_error(void *data, struct pw_impl_node *node)
32
+{
33
+ pwtest_fail_if_reached();
34
+}
35
static const struct pw_context_events context_events_error =
36
{
37
PW_VERSION_CONTEXT_EVENTS,
38
39
.check_access = context_check_access_error,
40
.global_added = context_global_added_error,
41
.global_removed = context_global_removed_error,
42
+ .driver_added = context_driver_added_error,
43
+ .driver_removed = context_driver_removed_error,
44
};
45
46
static int destroy_count = 0;
47
Refresh
No build results available
Refresh
No rpmlint results available
Login required, please
login
or
signup
in order to comment
Request History
zaitor created request over 1 year ago
New upstream release
zaitor accepted request over 1 year ago
Xin