Overview

Request 5813 (accepted)

New upstream release

Submit package home:zaitor:...s:Essentials / pipewire-aptx to package Essentials / pipewire-aptx

pipewire-aptx.changes Changed
x
 
1
@@ -1,4 +1,9 @@
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
@@ -7,7 +7,7 @@
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
@@ -1,3 +1,94 @@
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
@@ -33,9 +124,6 @@
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
@@ -86,6 +86,8 @@
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
@@ -24,7 +24,9 @@
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
@@ -35,7 +37,7 @@
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
@@ -1,5 +1,5 @@
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
@@ -127,7 +127,7 @@
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
@@ -19,20 +19,12 @@
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
@@ -223,7 +215,7 @@
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
@@ -232,8 +224,8 @@
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
@@ -437,7 +429,7 @@
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
@@ -455,7 +447,7 @@
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
@@ -1737,7 +1737,7 @@
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
@@ -1898,18 +1898,18 @@
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
@@ -3536,6 +3536,7 @@
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
@@ -3607,14 +3608,16 @@
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
@@ -6192,7 +6195,7 @@
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
@@ -6364,7 +6367,7 @@
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
@@ -6374,7 +6377,7 @@
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
@@ -19,6 +19,7 @@
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
@@ -248,8 +249,6 @@
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
@@ -301,7 +300,7 @@
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
@@ -314,7 +313,7 @@
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
@@ -349,7 +348,7 @@
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
@@ -384,7 +383,7 @@
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
@@ -18,6 +18,7 @@
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
@@ -53,7 +54,7 @@
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
@@ -62,7 +63,7 @@
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
@@ -275,7 +275,8 @@
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
@@ -59,13 +59,16 @@
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
@@ -74,9 +77,11 @@
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
@@ -23,8 +23,11 @@
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
@@ -23,8 +23,11 @@
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
@@ -134,11 +134,12 @@
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
@@ -0,0 +1,35 @@
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
@@ -80,7 +80,6 @@
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
@@ -94,6 +93,7 @@
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
@@ -503,7 +503,7 @@
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
@@ -89,6 +89,7 @@
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
@@ -109,6 +110,7 @@
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
@@ -695,6 +697,14 @@
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
@@ -773,6 +783,8 @@
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
@@ -854,6 +866,8 @@
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
@@ -1049,13 +1063,15 @@
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
@@ -1124,7 +1140,8 @@
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
@@ -1139,13 +1156,15 @@
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
@@ -1187,7 +1206,7 @@
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
@@ -2609,7 +2628,8 @@
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
@@ -709,9 +709,10 @@
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
@@ -18,6 +18,7 @@
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
@@ -764,7 +765,6 @@
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
@@ -777,7 +777,7 @@
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
@@ -847,7 +847,7 @@
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
@@ -890,7 +890,6 @@
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
@@ -906,7 +905,6 @@
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
@@ -927,8 +925,6 @@
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
@@ -1183,7 +1179,7 @@
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
@@ -1216,7 +1212,6 @@
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
@@ -1303,9 +1298,6 @@
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
@@ -33,6 +33,7 @@
2
 #include <libusb.h>
3
 #endif
4
 
5
+#include "dbus-helpers.h"
6
 #include "modemmanager.h"
7
 #include "upower.h"
8
 
9
@@ -1163,10 +1164,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
@@ -2196,7 +2196,7 @@
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
@@ -2216,8 +2216,8 @@
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
@@ -2226,8 +2226,8 @@
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
@@ -2414,7 +2414,7 @@
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
@@ -2432,9 +2432,8 @@
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
@@ -22,6 +22,7 @@
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
@@ -633,7 +634,7 @@
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
@@ -665,7 +666,6 @@
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
@@ -35,6 +35,7 @@
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
@@ -225,12 +226,7 @@
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
@@ -355,10 +351,8 @@
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
@@ -422,9 +416,7 @@
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
@@ -2340,7 +2332,7 @@
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
@@ -2634,17 +2626,8 @@
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
@@ -2669,6 +2652,7 @@
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
@@ -3242,11 +3226,8 @@
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
@@ -3270,11 +3251,7 @@
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
@@ -3400,11 +3377,8 @@
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
@@ -3613,11 +3587,7 @@
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
@@ -3742,10 +3712,7 @@
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
@@ -3992,11 +3959,8 @@
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
@@ -4475,9 +4439,7 @@
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
@@ -4776,9 +4738,7 @@
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
@@ -4809,9 +4769,7 @@
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
@@ -5225,12 +5183,8 @@
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
@@ -5625,10 +5579,7 @@
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
@@ -0,0 +1,32 @@
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
@@ -7,6 +7,7 @@
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
@@ -56,8 +57,7 @@
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
@@ -125,10 +125,7 @@
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
@@ -424,10 +421,7 @@
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
@@ -455,13 +449,11 @@
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
@@ -477,10 +469,7 @@
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
@@ -787,10 +776,7 @@
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
@@ -820,10 +806,7 @@
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
@@ -1158,10 +1141,7 @@
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
@@ -5,6 +5,7 @@
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
@@ -17,6 +18,8 @@
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
@@ -43,8 +46,8 @@
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
@@ -66,6 +69,31 @@
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
@@ -99,20 +127,8 @@
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
@@ -210,26 +226,12 @@
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
@@ -241,6 +243,8 @@
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
@@ -4,6 +4,8 @@
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
@@ -29,6 +31,7 @@
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
@@ -55,6 +58,19 @@
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
@@ -90,7 +106,8 @@
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
@@ -111,6 +128,19 @@
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
@@ -7,6 +7,7 @@
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
@@ -73,6 +73,7 @@
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
@@ -82,6 +83,7 @@
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
@@ -268,6 +270,7 @@
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
@@ -29,6 +29,7 @@
2
 
3
 struct props {
4
    char device64;
5
+   char devnum32;
6
    char product_id6;
7
    char vendor_id6;
8
    int device_fd;
9
@@ -55,11 +56,11 @@
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
@@ -78,6 +79,7 @@
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
@@ -233,6 +235,8 @@
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
@@ -220,8 +220,9 @@
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
@@ -236,6 +237,8 @@
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
@@ -38,6 +38,8 @@
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
@@ -659,6 +661,26 @@
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
@@ -1092,7 +1092,7 @@
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
@@ -45,7 +45,6 @@
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
@@ -61,6 +60,7 @@
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
@@ -889,8 +889,9 @@
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
@@ -1129,11 +1130,10 @@
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
@@ -1168,12 +1168,13 @@
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
@@ -1181,9 +1182,9 @@
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
@@ -1238,14 +1239,13 @@
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
@@ -52,10 +52,8 @@
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
@@ -71,6 +69,21 @@
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
@@ -84,18 +97,13 @@
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
@@ -105,69 +113,45 @@
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
@@ -180,13 +164,13 @@
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
@@ -253,42 +237,107 @@
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
@@ -331,9 +380,7 @@
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
@@ -346,11 +393,12 @@
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
@@ -365,7 +413,6 @@
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
@@ -393,6 +440,7 @@
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
@@ -407,8 +455,6 @@
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
@@ -422,7 +468,7 @@
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
@@ -430,6 +476,10 @@
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
@@ -281,6 +281,9 @@
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
@@ -974,6 +974,16 @@
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
@@ -112,6 +112,7 @@
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
@@ -107,7 +107,7 @@
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
@@ -215,7 +215,7 @@
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
@@ -574,7 +574,6 @@
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
@@ -583,8 +582,7 @@
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
@@ -3715,9 +3715,9 @@
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
@@ -3927,9 +3927,9 @@
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
@@ -229,12 +229,12 @@
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
@@ -254,7 +254,7 @@
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
@@ -124,7 +124,7 @@
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
@@ -318,7 +318,7 @@
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
@@ -827,7 +827,7 @@
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
@@ -838,9 +838,9 @@
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
@@ -857,7 +857,7 @@
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
@@ -873,10 +873,10 @@
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
@@ -887,7 +887,7 @@
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
@@ -913,7 +913,7 @@
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
@@ -966,7 +966,7 @@
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
@@ -975,7 +975,7 @@
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
@@ -1105,11 +1105,11 @@
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
@@ -1294,11 +1294,11 @@
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
@@ -1315,12 +1315,12 @@
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
@@ -1385,12 +1385,12 @@
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
@@ -1547,12 +1547,12 @@
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
@@ -25,7 +25,7 @@
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
@@ -60,6 +60,7 @@
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
@@ -89,6 +90,7 @@
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
@@ -105,6 +107,7 @@
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
@@ -277,7 +280,7 @@
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
@@ -288,6 +291,8 @@
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
@@ -328,7 +333,7 @@
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
@@ -345,6 +350,9 @@
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
@@ -556,7 +564,7 @@
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
@@ -610,7 +618,7 @@
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
@@ -55,13 +55,13 @@
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
@@ -173,6 +173,8 @@
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
@@ -191,6 +193,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
@@ -383,41 +389,12 @@
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
@@ -446,17 +423,10 @@
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
@@ -464,26 +434,20 @@
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
@@ -505,20 +469,10 @@
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
@@ -526,11 +480,6 @@
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
@@ -542,6 +491,8 @@
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
@@ -637,9 +588,9 @@
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
@@ -651,30 +602,27 @@
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
@@ -684,37 +632,16 @@
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
@@ -840,8 +767,7 @@
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
@@ -860,62 +786,72 @@
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, &params, 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
@@ -986,6 +922,93 @@
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
@@ -1019,6 +1042,10 @@
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
@@ -1034,6 +1061,7 @@
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
@@ -1051,50 +1079,39 @@
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
@@ -1109,20 +1126,12 @@
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
@@ -0,0 +1,572 @@
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
@@ -0,0 +1,534 @@
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
@@ -0,0 +1,281 @@
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, &timestamp);
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, &timestamp);
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
@@ -0,0 +1,479 @@
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
@@ -0,0 +1,54 @@
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
@@ -0,0 +1,60 @@
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
@@ -17,6 +17,7 @@
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
@@ -478,61 +479,6 @@
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
@@ -1534,10 +1480,10 @@
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
@@ -43,6 +43,7 @@
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
@@ -50,7 +51,7 @@
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
@@ -63,6 +64,11 @@
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
@@ -1222,7 +1222,6 @@
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
@@ -1250,28 +1249,6 @@
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
@@ -1281,6 +1258,11 @@
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
@@ -1605,6 +1587,8 @@
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
@@ -1658,13 +1642,31 @@
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
@@ -713,7 +713,7 @@
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
@@ -748,7 +748,7 @@
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
@@ -1130,18 +1130,63 @@
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
@@ -1149,17 +1194,36 @@
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
@@ -1172,6 +1236,9 @@
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
@@ -698,6 +698,13 @@
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
@@ -846,8 +853,8 @@
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
@@ -941,8 +948,9 @@
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
@@ -1158,7 +1166,7 @@
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
@@ -1249,11 +1257,12 @@
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
@@ -1263,7 +1272,7 @@
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
@@ -1394,6 +1403,7 @@
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
@@ -1661,8 +1671,8 @@
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
@@ -1726,7 +1736,7 @@
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
@@ -1775,7 +1785,7 @@
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
@@ -1790,8 +1800,8 @@
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
@@ -1841,7 +1851,7 @@
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
@@ -1904,7 +1914,7 @@
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
@@ -1954,6 +1964,50 @@
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
@@ -1998,7 +2052,7 @@
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
@@ -75,6 +75,23 @@
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
@@ -118,6 +135,14 @@
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
@@ -838,6 +838,12 @@
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
@@ -17,6 +17,7 @@
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
@@ -359,39 +360,6 @@
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
@@ -403,6 +371,8 @@
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
@@ -549,7 +519,7 @@
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
@@ -631,25 +601,6 @@
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
@@ -669,6 +620,14 @@
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
@@ -737,6 +696,7 @@
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
@@ -1085,6 +1045,7 @@
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
@@ -1321,8 +1282,6 @@
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
@@ -17,7 +17,6 @@
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
@@ -84,7 +83,6 @@
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
@@ -436,7 +434,7 @@
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
@@ -480,6 +478,7 @@
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
@@ -621,7 +620,7 @@
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
@@ -636,7 +635,7 @@
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
@@ -859,7 +858,7 @@
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
@@ -1039,7 +1038,7 @@
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
@@ -1389,6 +1388,7 @@
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
@@ -1440,11 +1440,9 @@
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
@@ -1453,9 +1451,9 @@
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
@@ -1481,7 +1479,6 @@
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
@@ -1513,28 +1510,6 @@
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
@@ -1545,6 +1520,10 @@
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
@@ -1562,9 +1541,6 @@
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
@@ -1730,9 +1706,6 @@
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
@@ -1914,6 +1887,7 @@
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
@@ -2007,49 +1981,69 @@
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
@@ -2125,6 +2119,8 @@
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
@@ -2340,12 +2336,12 @@
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
@@ -2394,11 +2390,12 @@
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
@@ -2416,9 +2413,10 @@
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
@@ -43,6 +43,7 @@
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
@@ -143,6 +144,11 @@
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
@@ -282,6 +288,9 @@
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
@@ -25,8 +25,9 @@
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
@@ -27,6 +27,7 @@
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
@@ -11,6 +11,8 @@
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
@@ -20,6 +22,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
@@ -745,6 +745,11 @@
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
@@ -27,6 +27,8 @@
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
@@ -36,8 +38,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
@@ -65,7 +69,14 @@
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
@@ -74,6 +85,8 @@
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

Request History
Bjørn Lie's avatar

zaitor created request over 1 year ago

New upstream release


Bjørn Lie's avatar

zaitor accepted request over 1 year ago

Xin