Changes of Revision 34

pipewire-aptx.changes Changed
x
 
1
@@ -1,4 +1,9 @@
2
 -------------------------------------------------------------------
3
+Fri Sep 22 17:21:32 UTC 2023 - Bjørn Lie <zaitor@opensuse.org>
4
+
5
+- Update to version 0.3.80
6
+
7
+-------------------------------------------------------------------
8
 Wed Aug 30 13:49:39 UTC 2023 - Bjørn Lie <zaitor@opensuse.org>
9
 
10
 - Update to version 0.3.79
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.79
6
+Version:        0.3.80
7
 Release:        0
8
 Summary:        PipeWire Bluetooth aptX codec plugin
9
 License:        MIT
10
pipewire-0.3.79.tar.gz/.gitignore -> pipewire-0.3.80.tar.gz/.gitignore Changed
9
 
1
@@ -18,6 +18,7 @@
2
 subprojects/libyaml.wrap
3
 subprojects/libyaml
4
 subprojects/libcamera
5
+subprojects/webrtc-audio-processing
6
 
7
 # Created by https://www.gitignore.io/api/vim
8
 
9
pipewire-0.3.79.tar.gz/NEWS -> pipewire-0.3.80.tar.gz/NEWS Changed
83
 
1
@@ -1,3 +1,71 @@
2
+# PipeWire 0.3.80 (2023-09-14)
3
+
4
+This is a bugfix release that is API and ABI compatible with previous
5
+0.3.x releases.
6
+
7
+## Highlights
8
+  - A new Tag param was added that allows arbitrary metadata to be transported
9
+    out-of-band in the graph.
10
+  - Vulkan DMA buf support was merged.
11
+  - The echo-canceller was ported to webrtc-audio-processing-1.
12
+  - Fix a regression in locating monitor sources by id in pulse-server.
13
+  - Mixer io areas updates are now synchronized correctly with the data
14
+    thread to avoid potential crashes.
15
+  - Many more bugfixes and improvements.
16
+
17
+
18
+## PipeWire
19
+  - Handle driver nodes that refuse to change the quantum or rate.
20
+  - A new Tag param was added that allows arbitrary metadata to be transported
21
+    out-of-band in the graph.
22
+
23
+## Modules
24
+  - The pipe-tunnel source has been reworked to use a ringbuffer and rate
25
+    adaption to keep the latency constant. It can now also function as a
26
+    driver to reduce resampling. (#3478)
27
+
28
+## Tools
29
+  - pw-cat will now place media properties in Tag params.
30
+  - pw-mon can now filter props and params.
31
+
32
+## SPA
33
+  - ALSA refuses to change quantum and rate when in IRQ mode.
34
+  - ALSA will now be smarter in selecting the period size for batch devices
35
+    and will make it depend on the samplerate. (#3444)
36
+  - Vulkan DMA buf support was merged.
37
+  - ALSA latency will now be reported in the time domain of the graph.
38
+  - Add udev based autodetection for compress-offload devices.
39
+  - The echo-canceller was ported to webrtc-audio-processing-1.
40
+  - The v4l2 inotify code was rewritten to avoid a use-after-free and by
41
+    using a separate watch (but same fd) for each device. (#3439)
42
+  - The tag and latency handling was improved in audioadpter.
43
+  - Don't use -Ofast on alpha because it can crash on denormalized
44
+    values. (#3489)
45
+  - The mixers now synchronize spa_io_buffers updates with the data
46
+    thread to avoid crashes.
47
+  - Handle NULL param updates. (#3504)
48
+
49
+## Pulse-server
50
+  - Fix a regression in locating monitor sources by id. (#3476)
51
+  - Add support for use_system_clock_for_timing in module-pipe-sink.
52
+  - Add support for checking module arguments.
53
+  - Avoid some useless change events.
54
+
55
+## Bluetooth
56
+  - Ports are now marked as physical, which makes the bluetooth devices show
57
+    up as hardware devices in Ardour and other JACK apps. (#3418)
58
+  - Some fixes for LE audio support (#3479)
59
+
60
+## JACK
61
+  - Also emit unregister notify even when supressed when creating the
62
+    client.
63
+  - The notify callbacks now match JACK2 behaviour more.
64
+  - The mixer io areas are updated and handled safely now to avoid
65
+    crashes. (#3506)
66
+
67
+Older versions:
68
+
69
+
70
 # PipeWire 0.3.79 (2023-08-29)
71
 
72
 This is a quick bugfix release that is API and ABI compatible with previous
73
@@ -53,9 +121,6 @@
74
   - Improve property handling, support lists and ranges in addition to
75
     fixed values. (#3451)
76
 
77
-Older versions:
78
-
79
-
80
 # PipeWire 0.3.78 (2023-08-22)
81
 
82
 This is a small bugfix release that is API and ABI compatible with previous
83
pipewire-0.3.79.tar.gz/man/pipewire-pulse.1.rst.in -> pipewire-0.3.80.tar.gz/man/pipewire-pulse.1.rst.in Changed
12
 
1
@@ -17,8 +17,8 @@
2
 ===========
3
 
4
 **pipewire-pulse** starts a PulseAudio-compatible daemon that integrates with
5
-the PipeWire media server. This daemon is a drop-in replacement for the
6
-PulseAudio daemon.
7
+the PipeWire media server, by running a pipewire process through a systemd
8
+service. This daemon is a drop-in replacement for the PulseAudio daemon.
9
 
10
 OPTIONS
11
 =======
12
pipewire-0.3.79.tar.gz/man/pipewire.conf.5.rst.in -> pipewire-0.3.80.tar.gz/man/pipewire.conf.5.rst.in Changed
10
 
1
@@ -69,7 +69,7 @@
2
 of use, a relaxed format may be used where:
3
 
4
  * ``:`` to delimit keys and values can be substuted by ``=`` or a space.
5
- * ``"`` around keys and string can be omited as long as no special characters are used in the strings.
6
+ * ``"`` around keys and string can be omitted as long as no special characters are used in the strings.
7
  * ``,`` to separate objects can be replaced with a whitespace character.
8
  * ``#`` can be used to start a comment until the line end
9
 
10
pipewire-0.3.79.tar.gz/man/pw-top.1.rst.in -> pipewire-0.3.80.tar.gz/man/pw-top.1.rst.in Changed
20
 
1
@@ -155,11 +155,17 @@
2
 -h | --help
3
   Show help.
4
 
5
+-b | --batch-mode
6
+  Run in non-interactive batch mode, similar to top's batch mode.
7
+
8
+-n | --iterations=NUMBER
9
+  Exit after NUMBER of batch iterations. Only used in batch mode.
10
+
11
 -r | --remote=NAME
12
   The name the *remote* instance to monitor. If left unspecified,
13
   a connection is made to the default PipeWire instance.
14
 
15
---version
16
+-V | --version
17
   Show version information.
18
 
19
 
20
pipewire-0.3.79.tar.gz/meson.build -> pipewire-0.3.80.tar.gz/meson.build Changed
39
 
1
@@ -1,5 +1,5 @@
2
 project('pipewire', 'c' ,
3
-  version : '0.3.79',
4
+  version : '0.3.80',
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
@@ -49,7 +49,9 @@
9
 pipewire_confdatadir = pipewire_datadir / 'pipewire'
10
 modules_install_dir = pipewire_libdir / pipewire_name
11
 
12
-if host_machine.system() == 'linux'
13
+cc = meson.get_compiler('c')
14
+
15
+if cc.has_header('features.h') and cc.get_define('__GLIBC__', prefix: '#include <features.h>') != ''
16
   # glibc ld.so interprets ${LIB} in a library loading path with an
17
   # appropriate value for the current architecture, typically something
18
   # like lib, lib64 or lib/x86_64-linux-gnu.
19
@@ -72,8 +74,6 @@
20
 
21
 pkgconfig = import('pkgconfig')
22
 
23
-cc = meson.get_compiler('c')
24
-
25
 common_flags = 
26
   '-fvisibility=hidden',
27
   '-fno-strict-aliasing',
28
@@ -375,8 +375,8 @@
29
 
30
 cdata.set('HAVE_GSTREAMER_DEVICE_PROVIDER', get_option('gstreamer-device-provider').allowed())
31
 
32
-webrtc_dep = dependency('webrtc-audio-processing',
33
-  version : '>= 0.2', '< 1.0',
34
+webrtc_dep = dependency('webrtc-audio-processing-1',
35
+  version : '>= 1.2' ,
36
   required : get_option('echo-cancel-webrtc'))
37
 summary({'WebRTC Echo Canceling': webrtc_dep.found()}, bool_yn: true, section: 'Misc dependencies')
38
 cdata.set('HAVE_WEBRTC', webrtc_dep.found())
39
pipewire-0.3.79.tar.gz/pipewire-jack/src/pipewire-jack.c -> pipewire-0.3.80.tar.gz/pipewire-jack/src/pipewire-jack.c Changed
201
 
1
@@ -247,6 +247,7 @@
2
 
3
    struct spa_io_buffers io;
4
    struct spa_list mix;
5
+   uint32_t n_mix;
6
    struct mix *global_mix;
7
 
8
    struct port *tied;
9
@@ -526,6 +527,27 @@
10
 
11
 }
12
 
13
+struct io_info {
14
+   struct mix *mix;
15
+   void *data;
16
+};
17
+
18
+static int
19
+do_mix_set_io(struct spa_loop *loop, bool async, uint32_t seq,
20
+       const void *data, size_t size, void *user_data)
21
+{
22
+   struct io_info *info = user_data;
23
+   info->mix->io = info->data;
24
+   return 0;
25
+}
26
+
27
+static inline void mix_set_io(struct mix *mix, void *data)
28
+{
29
+   struct io_info info = { .mix = mix, .data = data };
30
+   pw_data_loop_invoke(mix->port->client->loop,
31
+       do_mix_set_io, SPA_ID_INVALID, NULL, 0, true, &info);
32
+}
33
+
34
 static void init_mix(struct mix *mix, uint32_t mix_id, struct port *port, uint32_t peer_id)
35
 {
36
    pw_log_debug("create %p mix:%d peer:%d", port, mix_id, peer_id);
37
@@ -538,6 +560,8 @@
38
    spa_list_init(&mix->queue);
39
    if (mix_id == SPA_ID_INVALID)
40
        port->global_mix = mix;
41
+   else if (port->n_mix++ == 0 && port->global_mix != NULL)
42
+       mix_set_io(port->global_mix, &port->io);
43
 }
44
 static struct mix *find_mix_peer(struct client *c, uint32_t peer_id)
45
 {
46
@@ -618,10 +642,14 @@
47
 
48
 static void free_mix(struct client *c, struct mix *mix)
49
 {
50
+   struct port *port = mix->port;
51
+
52
    clear_buffers(c, mix);
53
    spa_list_remove(&mix->port_link);
54
    if (mix->id == SPA_ID_INVALID)
55
-       mix->port->global_mix = NULL;
56
+       port->global_mix = NULL;
57
+   else if (--port->n_mix == 0 && port->global_mix != NULL)
58
+       mix_set_io(port->global_mix, NULL);
59
    spa_list_remove(&mix->link);
60
    spa_list_append(&c->free_mix, &mix->link);
61
 }
62
@@ -1108,9 +1136,12 @@
63
        }
64
        pw_log_debug("%p: skip notify %08x active:%d emit:%d", c, type,
65
                c->active, emit);
66
-       if (o != NULL && arg1 == 0 && o->removing) {
67
-           o->removing = false;
68
-           free_object(c, o);
69
+       if (o != NULL) {
70
+           o->registered = arg1;
71
+           if (arg1 == 0 && o->removing) {
72
+               o->removing = false;
73
+               free_object(c, o);
74
+           }
75
        }
76
        return res;
77
    }
78
@@ -1395,6 +1426,7 @@
79
    void *ptr = NULL;
80
    struct buffer *b;
81
    struct spa_data *d;
82
+   struct spa_io_buffers *io;
83
 
84
    if (frames == 0 || !p->valid)
85
        return NULL;
86
@@ -1402,20 +1434,21 @@
87
    if (SPA_UNLIKELY((mix = p->global_mix) == NULL))
88
        return NULL;
89
 
90
-   pw_log_trace_fp("%p: port %s %d get buffer %d n_buffers:%d",
91
-           c, p->object->port.name, p->port_id, frames, mix->n_buffers);
92
+   pw_log_trace_fp("%p: port %s %d get buffer %d n_buffers:%d io:%p",
93
+           c, p->object->port.name, p->port_id, frames,
94
+           mix->n_buffers, mix->io);
95
 
96
-   if (SPA_UNLIKELY(mix->n_buffers == 0))
97
+   if (SPA_UNLIKELY((io = mix->io) == NULL))
98
        return NULL;
99
 
100
-   if (p->io.status == SPA_STATUS_HAVE_DATA &&
101
-       p->io.buffer_id < mix->n_buffers) {
102
-       b = &mix->buffersp->io.buffer_id;
103
+   if (io->status == SPA_STATUS_HAVE_DATA &&
104
+       io->buffer_id < mix->n_buffers) {
105
+       b = &mix->buffersio->buffer_id;
106
        d = &b->datas0;
107
    } else {
108
-       if (p->io.buffer_id < mix->n_buffers) {
109
-           reuse_buffer(c, mix, p->io.buffer_id);
110
-           p->io.buffer_id = SPA_ID_INVALID;
111
+       if (io->buffer_id < mix->n_buffers) {
112
+           reuse_buffer(c, mix, io->buffer_id);
113
+           io->buffer_id = SPA_ID_INVALID;
114
        }
115
        if (SPA_UNLIKELY((b = dequeue_buffer(c, mix)) == NULL)) {
116
            pw_log_warn("port %p: out of buffers", p);
117
@@ -1426,8 +1459,8 @@
118
        d->chunk->size = frames * sizeof(float);
119
        d->chunk->stride = stride;
120
 
121
-       p->io.status = SPA_STATUS_HAVE_DATA;
122
-       p->io.buffer_id = b->id;
123
+       io->status = SPA_STATUS_HAVE_DATA;
124
+       io->buffer_id = b->id;
125
    }
126
    ptr = d->data;
127
    if (buf)
128
@@ -1469,14 +1502,19 @@
129
 static void prepare_output(struct port *p, uint32_t frames)
130
 {
131
    struct mix *mix;
132
+   struct spa_io_buffers *io;
133
 
134
    if (SPA_UNLIKELY(p->empty_out || p->tied))
135
        process_empty(p, frames);
136
 
137
+   if (p->global_mix == NULL || (io = p->global_mix->io) == NULL)
138
+       return;
139
+
140
    spa_list_for_each(mix, &p->mix, port_link) {
141
        if (SPA_LIKELY(mix->io != NULL))
142
-           *mix->io = p->io;
143
+           *mix->io = *io;
144
    }
145
+   io->status = SPA_STATUS_NEED_DATA;
146
 }
147
 
148
 static void complete_process(struct client *c, uint32_t frames)
149
@@ -1492,7 +1530,6 @@
150
        if (!p->valid)
151
            continue;
152
        prepare_output(p, frames);
153
-       p->io.status = SPA_STATUS_NEED_DATA;
154
    }
155
    pw_array_for_each(item, &c->portsSPA_DIRECTION_INPUT.items) {
156
                 if (pw_map_item_is_free(item))
157
@@ -2405,7 +2442,6 @@
158
 
159
    if (param == NULL)
160
        return 0;
161
-
162
    if ((res = spa_latency_parse(param, &info)) < 0)
163
        return res;
164
 
165
@@ -2685,7 +2721,7 @@
166
 
167
    switch (id) {
168
    case SPA_IO_Buffers:
169
-                mix->io = ptr;
170
+       mix_set_io(mix, ptr);
171
        break;
172
    default:
173
        break;
174
@@ -2812,6 +2848,9 @@
175
 
176
    mix = find_mix(c, p, mix_id);
177
 
178
+   pw_log_debug("%p: port %p mix:%d peer_id:%u info:%p", c, p, mix_id,
179
+           peer_id, props);
180
+
181
    if (peer_id == SPA_ID_INVALID) {
182
        if (mix == NULL) {
183
            res = -ENOENT;
184
@@ -3110,15 +3149,23 @@
185
            pw_node_state_as_string(info->state), n->node.is_running);
186
 
187
    if (info->change_mask & PW_NODE_CHANGE_MASK_STATE) {
188
-       struct object *p;
189
+       struct object *p, *l;
190
        spa_list_for_each(p, &c->context.objects, link) {
191
            if (p->type != INTERFACE_Port || p->removed ||
192
                p->port.node_id != info->id)
193
                continue;
194
            if (n->node.is_running)
195
                queue_notify(c, NOTIFY_TYPE_PORTREGISTRATION, p, 1, NULL);
196
-           else
197
+           else {
198
+               spa_list_for_each(l, &c->context.objects, link) {
199
+                   if (l->type != INTERFACE_Link || l->removed ||
200
+                       (l->port_link.src_serial != p->serial &&
201
pipewire-0.3.79.tar.gz/spa/include/spa/debug/log.h -> pipewire-0.3.80.tar.gz/spa/include/spa/debug/log.h Changed
37
 
1
@@ -53,25 +53,32 @@
2
 
3
 #define spa_debug_log_pod(l,lev,indent,info,pod)               \
4
 ({                                     \
5
-   struct spa_debug_log_ctx c = SPA_LOG_DEBUG_INIT(l,lev);         \
6
+   struct spa_debug_log_ctx c = SPA_LOG_DEBUG_INIT(l,lev);         \
7
    if (SPA_UNLIKELY(spa_log_level_topic_enabled(c.log, c.topic, c.level))) \
8
        spa_debugc_pod(&c.ctx, indent, info, pod);          \
9
 })
10
 
11
 #define spa_debug_log_format(l,lev,indent,info,format)                 \
12
 ({                                     \
13
-   struct spa_debug_log_ctx c = SPA_LOG_DEBUG_INIT(l,lev);         \
14
+   struct spa_debug_log_ctx c = SPA_LOG_DEBUG_INIT(l,lev);         \
15
    if (SPA_UNLIKELY(spa_log_level_topic_enabled(c.log, c.topic, c.level))) \
16
        spa_debugc_format(&c.ctx, indent, info, format);        \
17
 })
18
 
19
 #define spa_debug_log_mem(l,lev,indent,data,len)               \
20
 ({                                     \
21
-   struct spa_debug_log_ctx c = SPA_LOG_DEBUG_INIT(l,lev);         \
22
+   struct spa_debug_log_ctx c = SPA_LOG_DEBUG_INIT(l,lev);         \
23
    if (SPA_UNLIKELY(spa_log_level_topic_enabled(c.log, c.topic, c.level))) \
24
        spa_debugc_mem(&c.ctx, indent, data, len);          \
25
 })
26
 
27
+#define spa_debug_log_dict(l,lev,indent,dict)                  \
28
+({                                     \
29
+   struct spa_debug_log_ctx c = SPA_LOG_DEBUG_INIT(l,lev);         \
30
+   if (SPA_UNLIKELY(spa_log_level_topic_enabled(c.log, c.topic, c.level))) \
31
+       spa_debugc_dict(&c.ctx, indent, dict);              \
32
+})
33
+
34
 /**
35
  * \}
36
  */
37
pipewire-0.3.79.tar.gz/spa/include/spa/node/keys.h -> pipewire-0.3.80.tar.gz/spa/include/spa/node/keys.h Changed
10
 
1
@@ -16,6 +16,8 @@
2
 
3
 /** node keys */
4
 #define SPA_KEY_NODE_NAME      "node.name"     /**< a node name */
5
+#define SPA_KEY_NODE_DESCRIPTION   "node.description"  /**< localized human readable node one-line
6
+                                 *  description. Ex. "Foobar USB Headset" */
7
 #define SPA_KEY_NODE_LATENCY       "node.latency"      /**< the requested node latency */
8
 #define SPA_KEY_NODE_MAX_LATENCY   "node.max-latency"  /**< maximum supported latency */
9
 
10
pipewire-0.3.79.tar.gz/spa/include/spa/node/node.h -> pipewire-0.3.80.tar.gz/spa/include/spa/node/node.h Changed
34
 
1
@@ -485,7 +485,9 @@
2
     *
3
     * When \a param is NULL, the parameter will be unset.
4
     *
5
-    * This function must be called from the main thread.
6
+    * This function must be called from the main thread. The node muse be paused
7
+    * or the port SPA_IO_Buffers area is NULL when this function is called with
8
+    * a param that changes the processing state (like a format change).
9
     *
10
     * \param node a struct \ref spa_node
11
     * \param direction a enum \ref spa_direction
12
@@ -540,7 +542,8 @@
13
     * When this function returns async, use the spa_node_sync operation to
14
     * wait for completion.
15
     *
16
-    * This function must be called from the main thread.
17
+    * This function must be called from the main thread. The node muse be paused
18
+    * or the port SPA_IO_Buffers area is NULL when this function is called.
19
     *
20
     * \param object an object implementing the interface
21
     * \param direction a port direction
22
@@ -566,6 +569,11 @@
23
     *
24
     * This function must be called from the main thread.
25
     *
26
+    * This function can be called when the node is running and the node
27
+    * must be prepared to handle changes in io areas while running. This
28
+    * is normally done by synchronizing the port io updates with the
29
+    * data processing loop.
30
+    *
31
     * \param direction a spa_direction
32
     * \param port_id a port id
33
     * \param id the id of the io area, the available ids can be
34
pipewire-0.3.79.tar.gz/spa/include/spa/param/param-types.h -> pipewire-0.3.80.tar.gz/spa/include/spa/param/param-types.h Changed
9
 
1
@@ -40,6 +40,7 @@
2
    { SPA_PARAM_Control, SPA_TYPE_Sequence, SPA_TYPE_INFO_PARAM_ID_BASE "Control", NULL },
3
    { SPA_PARAM_Latency, SPA_TYPE_OBJECT_ParamLatency, SPA_TYPE_INFO_PARAM_ID_BASE "Latency", NULL },
4
    { SPA_PARAM_ProcessLatency, SPA_TYPE_OBJECT_ParamProcessLatency, SPA_TYPE_INFO_PARAM_ID_BASE "ProcessLatency", NULL },
5
+   { SPA_PARAM_Tag, SPA_TYPE_OBJECT_ParamTag, SPA_TYPE_INFO_PARAM_ID_BASE "Tag", NULL },
6
    { 0, 0, NULL, NULL },
7
 };
8
 
9
pipewire-0.3.79.tar.gz/spa/include/spa/param/param.h -> pipewire-0.3.80.tar.gz/spa/include/spa/param/param.h Changed
9
 
1
@@ -39,6 +39,7 @@
2
    SPA_PARAM_Control,      /**< Control parameter, a SPA_TYPE_Sequence */
3
    SPA_PARAM_Latency,      /**< latency reporting, a SPA_TYPE_OBJECT_ParamLatency */
4
    SPA_PARAM_ProcessLatency,   /**< processing latency, a SPA_TYPE_OBJECT_ParamProcessLatency */
5
+   SPA_PARAM_Tag,          /**< tag reporting, a SPA_TYPE_OBJECT_ParamTag. Since 0.3.79 */
6
 };
7
 
8
 /** information about a parameter */
9
pipewire-0.3.80.tar.gz/spa/include/spa/param/tag-types.h Added
41
 
1
@@ -0,0 +1,39 @@
2
+/* Simple Plugin API */
3
+/* SPDX-FileCopyrightText: Copyright © 2018 Wim Taymans */
4
+/* SPDX-License-Identifier: MIT */
5
+
6
+#ifndef SPA_PARAM_TAG_TYPES_H
7
+#define SPA_PARAM_TAG_TYPES_H
8
+
9
+#ifdef __cplusplus
10
+extern "C" {
11
+#endif
12
+
13
+/**
14
+ * \addtogroup spa_param
15
+ * \{
16
+ */
17
+
18
+#include <spa/utils/enum-types.h>
19
+#include <spa/param/param-types.h>
20
+#include <spa/param/tag.h>
21
+
22
+#define SPA_TYPE_INFO_PARAM_Tag        SPA_TYPE_INFO_PARAM_BASE "Tag"
23
+#define SPA_TYPE_INFO_PARAM_TAG_BASE   SPA_TYPE_INFO_PARAM_Tag ":"
24
+
25
+static const struct spa_type_info spa_type_param_tag = {
26
+   { SPA_PARAM_TAG_START, SPA_TYPE_Id, SPA_TYPE_INFO_PARAM_TAG_BASE, spa_type_param, },
27
+   { SPA_PARAM_TAG_direction, SPA_TYPE_Id, SPA_TYPE_INFO_PARAM_TAG_BASE "direction", spa_type_direction, },
28
+   { SPA_PARAM_TAG_info, SPA_TYPE_Struct, SPA_TYPE_INFO_PARAM_TAG_BASE "info", NULL, },
29
+   { 0, 0, NULL, NULL },
30
+};
31
+
32
+/**
33
+ * \}
34
+ */
35
+
36
+#ifdef __cplusplus
37
+}  /* extern "C" */
38
+#endif
39
+
40
+#endif /* SPA_PARAM_TAG_TYPES_H */
41
pipewire-0.3.80.tar.gz/spa/include/spa/param/tag-utils.h Added
145
 
1
@@ -0,0 +1,143 @@
2
+/* Simple Plugin API */
3
+/* SPDX-FileCopyrightText: Copyright © 2023 Wim Taymans */
4
+/* SPDX-License-Identifier: MIT */
5
+
6
+#ifndef SPA_PARAM_TAG_UTILS_H
7
+#define SPA_PARAM_TAG_UTILS_H
8
+
9
+#ifdef __cplusplus
10
+extern "C" {
11
+#endif
12
+
13
+/**
14
+ * \addtogroup spa_param
15
+ * \{
16
+ */
17
+
18
+#include <float.h>
19
+
20
+#include <spa/utils/dict.h>
21
+#include <spa/pod/builder.h>
22
+#include <spa/pod/parser.h>
23
+#include <spa/param/tag.h>
24
+
25
+static inline int
26
+spa_tag_compare(const struct spa_pod *a, const struct spa_pod *b)
27
+{
28
+   return ((a == b) || (a && b && SPA_POD_SIZE(a) == SPA_POD_SIZE(b) &&
29
+       memcmp(a, b, SPA_POD_SIZE(b)) == 0)) ? 0 : 1;
30
+}
31
+
32
+static inline int
33
+spa_tag_parse(const struct spa_pod *tag, struct spa_tag_info *info, void **state)
34
+{
35
+   int res;
36
+   const struct spa_pod_object *obj = (const struct spa_pod_object*)tag;
37
+   const struct spa_pod_prop *first, *start, *cur;
38
+
39
+   spa_zero(*info);
40
+
41
+   if ((res = spa_pod_parse_object(tag,
42
+           SPA_TYPE_OBJECT_ParamTag, NULL,
43
+           SPA_PARAM_TAG_direction, SPA_POD_Id(&info->direction))) < 0)
44
+       return res;
45
+
46
+        first = spa_pod_prop_first(&obj->body);
47
+        start = *state ? spa_pod_prop_next((struct spa_pod_prop*)*state) : first;
48
+
49
+   res = 0;
50
+   for (cur = start; spa_pod_prop_is_inside(&obj->body, obj->pod.size, cur);
51
+        cur = spa_pod_prop_next(cur)) {
52
+       if (cur->key == SPA_PARAM_TAG_info) {
53
+           info->info = &cur->value;
54
+           *state = (void*)cur;
55
+           return 1;
56
+       }
57
+        }
58
+   return 0;
59
+}
60
+
61
+static inline int
62
+spa_tag_info_parse(const struct spa_tag_info *info, struct spa_dict *dict, struct spa_dict_item *items)
63
+{
64
+   struct spa_pod_parser prs;
65
+   uint32_t n, n_items;
66
+   const char *key, *value;
67
+   struct spa_pod_frame f1;
68
+
69
+   spa_pod_parser_pod(&prs, info->info);
70
+   if (spa_pod_parser_push_struct(&prs, &f0) < 0 ||
71
+       spa_pod_parser_get_int(&prs, (int32_t*)&n_items) < 0)
72
+       return -EINVAL;
73
+
74
+   if (items == NULL) {
75
+       dict->n_items = n_items;
76
+       return 0;
77
+   }
78
+   n_items = SPA_MIN(dict->n_items, n_items);
79
+
80
+   for (n = 0; n < n_items; n++) {
81
+       if (spa_pod_parser_get(&prs,
82
+               SPA_POD_String(&key),
83
+               SPA_POD_String(&value),
84
+               NULL) < 0)
85
+           break;
86
+       itemsn.key = key;
87
+       itemsn.value = value;
88
+   }
89
+   dict->items = items;
90
+   spa_pod_parser_pop(&prs, &f0);
91
+   return 0;
92
+}
93
+
94
+static inline void
95
+spa_tag_build_start(struct spa_pod_builder *builder, struct spa_pod_frame *f,
96
+       uint32_t id, enum spa_direction direction)
97
+{
98
+   spa_pod_builder_push_object(builder, f, SPA_TYPE_OBJECT_ParamTag, id);
99
+   spa_pod_builder_add(builder,
100
+           SPA_PARAM_TAG_direction, SPA_POD_Id(direction),
101
+           0);
102
+}
103
+
104
+static inline void
105
+spa_tag_build_add_info(struct spa_pod_builder *builder, const struct spa_pod *info)
106
+{
107
+   spa_pod_builder_add(builder,
108
+           SPA_PARAM_TAG_info, SPA_POD_Pod(info),
109
+           0);
110
+}
111
+
112
+static inline void
113
+spa_tag_build_add_dict(struct spa_pod_builder *builder, const struct spa_dict *dict)
114
+{
115
+   uint32_t i, n_items;
116
+   struct spa_pod_frame f;
117
+
118
+   n_items = dict ? dict->n_items : 0;
119
+
120
+   spa_pod_builder_prop(builder, SPA_PARAM_TAG_info, SPA_POD_PROP_FLAG_HINT_DICT);
121
+   spa_pod_builder_push_struct(builder, &f);
122
+   spa_pod_builder_int(builder, n_items);
123
+        for (i = 0; i < n_items; i++) {
124
+       spa_pod_builder_string(builder, dict->itemsi.key);
125
+       spa_pod_builder_string(builder, dict->itemsi.value);
126
+   }
127
+        spa_pod_builder_pop(builder, &f);
128
+}
129
+
130
+static inline struct spa_pod *
131
+spa_tag_build_end(struct spa_pod_builder *builder, struct spa_pod_frame *f)
132
+{
133
+   return (struct spa_pod*)spa_pod_builder_pop(builder, f);
134
+}
135
+
136
+/**
137
+ * \}
138
+ */
139
+
140
+#ifdef __cplusplus
141
+}  /* extern "C" */
142
+#endif
143
+
144
+#endif /* SPA_PARAM_TAG_UTILS_H */
145
pipewire-0.3.80.tar.gz/spa/include/spa/param/tag.h Added
48
 
1
@@ -0,0 +1,46 @@
2
+/* Simple Plugin API */
3
+/* SPDX-FileCopyrightText: Copyright © 2023 Wim Taymans */
4
+/* SPDX-License-Identifier: MIT */
5
+
6
+#ifndef SPA_PARAM_TAG_H
7
+#define SPA_PARAM_TAG_H
8
+
9
+#ifdef __cplusplus
10
+extern "C" {
11
+#endif
12
+
13
+/**
14
+ * \addtogroup spa_param
15
+ * \{
16
+ */
17
+
18
+#include <spa/param/param.h>
19
+
20
+/** properties for SPA_TYPE_OBJECT_ParamTag */
21
+enum spa_param_tag {
22
+   SPA_PARAM_TAG_START,
23
+   SPA_PARAM_TAG_direction,        /**< direction, input/output (Id enum spa_direction) */
24
+   SPA_PARAM_TAG_info,         /**< Struct(
25
+                         *      Int: n_items
26
+                         *      (String: key
27
+                         *       String: value)*
28
+                         *  ) */
29
+};
30
+
31
+/** helper structure for managing tag objects */
32
+struct spa_tag_info {
33
+   enum spa_direction direction;
34
+   const struct spa_pod *info;
35
+};
36
+
37
+#define SPA_TAG_INFO(dir,...) ((struct spa_tag_info) { .direction = (dir), ## __VA_ARGS__ })
38
+
39
+/**
40
+ * \}
41
+ */
42
+
43
+#ifdef __cplusplus
44
+}  /* extern "C" */
45
+#endif
46
+
47
+#endif /* SPA_PARAM_TAG_H */
48
pipewire-0.3.79.tar.gz/spa/include/spa/param/type-info.h -> pipewire-0.3.80.tar.gz/spa/include/spa/param/type-info.h Changed
8
 
1
@@ -14,5 +14,6 @@
2
 #include <spa/param/profiler-types.h>
3
 #include <spa/param/profile-types.h>
4
 #include <spa/param/route-types.h>
5
+#include <spa/param/tag-types.h>
6
 
7
 #endif /* SPA_PARAM_TYPE_INFO_H */
8
pipewire-0.3.79.tar.gz/spa/include/spa/utils/names.h -> pipewire-0.3.80.tar.gz/spa/include/spa/utils/names.h Changed
14
 
1
@@ -90,8 +90,10 @@
2
 #define SPA_NAME_API_ALSA_SEQ_BRIDGE   "api.alsa.seq.bridge"       /**< an alsa Node interface for
3
                                      *  bridging midi ports */
4
 #define SPA_NAME_API_ALSA_ACP_DEVICE   "api.alsa.acp.device"       /**< an alsa ACP Device interface */
5
-#define SPA_NAME_API_ALSA_COMPRESS_OFFLOAD_SINK    "api.alsa.compress.offload.sink"    /**< an alsa Node interface for
6
-                                             * compressed audio */
7
+#define SPA_NAME_API_ALSA_COMPRESS_OFFLOAD_DEVICE  "api.alsa.compress.offload.device"  /**< an alsa Device interface for
8
+                                                 * compressed audio */
9
+#define SPA_NAME_API_ALSA_COMPRESS_OFFLOAD_SINK        "api.alsa.compress.offload.sink"    /**< an alsa Node interface for
10
+                                                 * compressed audio */
11
 
12
 /** keys for bluez5 factory names */
13
 #define SPA_NAME_API_BLUEZ5_ENUM_DBUS  "api.bluez5.enum.dbus"      /**< a dbus Device interface */
14
pipewire-0.3.79.tar.gz/spa/include/spa/utils/ratelimit.h -> pipewire-0.3.80.tar.gz/spa/include/spa/utils/ratelimit.h Changed
31
 
1
@@ -17,23 +17,23 @@
2
    uint64_t begin;
3
    unsigned burst;
4
    unsigned n_printed;
5
-   unsigned n_missed;
6
+   unsigned n_suppressed;
7
 };
8
 
9
 static inline int spa_ratelimit_test(struct spa_ratelimit *r, uint64_t now)
10
 {
11
-   unsigned missed = 0;
12
+   unsigned suppressed = 0;
13
    if (r->begin + r->interval < now) {
14
-       missed = r->n_missed;
15
+       suppressed = r->n_suppressed;
16
        r->begin = now;
17
        r->n_printed = 0;
18
-       r->n_missed = 0;
19
+       r->n_suppressed = 0;
20
    } else if (r->n_printed >= r->burst) {
21
-       r->n_missed++;
22
+       r->n_suppressed++;
23
        return -1;
24
    }
25
    r->n_printed++;
26
-   return missed;
27
+   return suppressed;
28
 }
29
 
30
 #ifdef __cplusplus
31
pipewire-0.3.79.tar.gz/spa/include/spa/utils/type-info.h -> pipewire-0.3.80.tar.gz/spa/include/spa/utils/type-info.h Changed
9
 
1
@@ -83,6 +83,7 @@
2
    { SPA_TYPE_OBJECT_Profiler, SPA_TYPE_Object, SPA_TYPE_INFO_Profiler, spa_type_profiler },
3
    { SPA_TYPE_OBJECT_ParamLatency, SPA_TYPE_Object, SPA_TYPE_INFO_PARAM_Latency, spa_type_param_latency },
4
    { SPA_TYPE_OBJECT_ParamProcessLatency, SPA_TYPE_Object, SPA_TYPE_INFO_PARAM_ProcessLatency, spa_type_param_process_latency },
5
+   { SPA_TYPE_OBJECT_ParamTag, SPA_TYPE_Object, SPA_TYPE_INFO_PARAM_Tag, spa_type_param_tag },
6
 
7
    { 0, 0, NULL, NULL }
8
 };
9
pipewire-0.3.79.tar.gz/spa/include/spa/utils/type.h -> pipewire-0.3.80.tar.gz/spa/include/spa/utils/type.h Changed
9
 
1
@@ -78,6 +78,7 @@
2
    SPA_TYPE_OBJECT_Profiler,
3
    SPA_TYPE_OBJECT_ParamLatency,
4
    SPA_TYPE_OBJECT_ParamProcessLatency,
5
+   SPA_TYPE_OBJECT_ParamTag,
6
    _SPA_TYPE_OBJECT_LAST,          /**< not part of ABI */
7
 
8
    /* vendor extensions */
9
pipewire-0.3.79.tar.gz/spa/plugins/aec/aec-webrtc.cpp -> pipewire-0.3.80.tar.gz/spa/plugins/aec/aec-webrtc.cpp Changed
182
 
1
@@ -13,9 +13,7 @@
2
 #include <spa/utils/json.h>
3
 #include <spa/support/plugin.h>
4
 
5
-#include <webrtc/modules/audio_processing/include/audio_processing.h>
6
-#include <webrtc/modules/interface/module_common_types.h>
7
-#include <webrtc/system_wrappers/include/trace.h>
8
+#include <modules/audio_processing/include/audio_processing.h>
9
 
10
 struct impl_data {
11
    struct spa_handle handle;
12
@@ -41,53 +39,6 @@
13
    return default_value;
14
 }
15
 
16
-
17
-/*  f0 f1 f2  */
18
-static int parse_point(struct spa_json *it, float (&f)3)
19
-{
20
-   struct spa_json arr;
21
-   int i, res;
22
-
23
-   if (spa_json_enter_array(it, &arr) <= 0)
24
-       return -EINVAL;
25
-
26
-   for (i = 0; i < 3; i++) {
27
-       if ((res = spa_json_get_float(&arr, &fi)) <= 0)
28
-           return -EINVAL;
29
-   }
30
-   return 0;
31
-}
32
-
33
-/*  point1 point2 ...  */
34
-static int parse_mic_geometry(struct impl_data *impl, const char *mic_geometry,
35
-       std::vector<webrtc::Point>& geometry)
36
-{
37
-   int res;
38
-   size_t i;
39
-   struct spa_json it2;
40
-
41
-   spa_json_init(&it0, mic_geometry, strlen(mic_geometry));
42
-   if (spa_json_enter_array(&it0, &it1) <= 0) {
43
-       spa_log_error(impl->log, "Error: webrtc.mic-geometry expects an array");
44
-       return -EINVAL;
45
-   }
46
-
47
-   for (i = 0; i < geometry.size(); i++) {
48
-       float f3;
49
-
50
-       if ((res = parse_point(&it1, f)) < 0) {
51
-           spa_log_error(impl->log, "Error: can't parse webrtc.mic-geometry points: %d", res);
52
-           return res;
53
-       }
54
-
55
-       spa_log_info(impl->log, "mic %zd position: (%g %g %g)", i, f0, f1, f2);
56
-       geometryi.c0 = f0;
57
-       geometryi.c1 = f1;
58
-       geometryi.c2 = f2;
59
-   }
60
-   return 0;
61
-}
62
-
63
 static int webrtc_init2(void *object, const struct spa_dict *args,
64
        struct spa_audio_info_raw *rec_info, struct spa_audio_info_raw *out_info,
65
        struct spa_audio_info_raw *play_info)
66
@@ -95,69 +46,33 @@
67
    auto impl = static_cast<struct impl_data*>(object);
68
    int res;
69
 
70
-   bool extended_filter = webrtc_get_spa_bool(args, "webrtc.extended_filter", true);
71
-   bool delay_agnostic = webrtc_get_spa_bool(args, "webrtc.delay_agnostic", true);
72
    bool high_pass_filter = webrtc_get_spa_bool(args, "webrtc.high_pass_filter", true);
73
    bool noise_suppression = webrtc_get_spa_bool(args, "webrtc.noise_suppression", true);
74
+   bool transient_suppression = webrtc_get_spa_bool(args, "webrtc.transient_suppression", true);
75
    bool voice_detection = webrtc_get_spa_bool(args, "webrtc.voice_detection", true);
76
 
77
    // Note: AGC seems to mess up with Agnostic Delay Detection, especially with speech,
78
    // result in very poor performance, disable by default
79
    bool gain_control = webrtc_get_spa_bool(args, "webrtc.gain_control", false);
80
 
81
-   // Disable experimental flags by default
82
-   bool experimental_agc = webrtc_get_spa_bool(args, "webrtc.experimental_agc", false);
83
-   bool experimental_ns = webrtc_get_spa_bool(args, "webrtc.experimental_ns", false);
84
-
85
-   bool beamforming = webrtc_get_spa_bool(args, "webrtc.beamforming", false);
86
-
87
    // FIXME: Intelligibility enhancer is not currently supported
88
    // This filter will modify playback buffer (when calling ProcessReverseStream), but now
89
    // playback buffer modifications are discarded.
90
 
91
-   webrtc::Config config;
92
-   config.Set<webrtc::ExtendedFilter>(new webrtc::ExtendedFilter(extended_filter));
93
-   config.Set<webrtc::DelayAgnostic>(new webrtc::DelayAgnostic(delay_agnostic));
94
-   config.Set<webrtc::ExperimentalAgc>(new webrtc::ExperimentalAgc(experimental_agc));
95
-   config.Set<webrtc::ExperimentalNs>(new webrtc::ExperimentalNs(experimental_ns));
96
-
97
-   if (beamforming) {
98
-       std::vector<webrtc::Point> geometry(rec_info->channels);
99
-       const char *mic_geometry, *target_direction;
100
-
101
-       /* The beamformer gives a single mono channel */
102
-       out_info->channels = 1;
103
-       out_info->position0 = SPA_AUDIO_CHANNEL_MONO;
104
-
105
-       if ((mic_geometry = spa_dict_lookup(args, "webrtc.mic-geometry")) == NULL) {
106
-           spa_log_error(impl->log, "Error: webrtc.beamforming requires webrtc.mic-geometry");
107
-           return -EINVAL;
108
-       }
109
-
110
-       if ((res = parse_mic_geometry(impl, mic_geometry, geometry)) < 0)
111
-           return res;
112
-
113
-       if ((target_direction = spa_dict_lookup(args, "webrtc.target-direction")) != NULL) {
114
-           webrtc::SphericalPointf direction(0.0f, 0.0f, 0.0f);
115
-           struct spa_json it;
116
-           float f3;
117
-
118
-           spa_json_init(&it, target_direction, strlen(target_direction));
119
-           if (parse_point(&it, f) < 0) {
120
-               spa_log_error(impl->log, "Error: can't parse target-direction %s",
121
-                       target_direction);
122
-               return -EINVAL;
123
-           }
124
-
125
-           direction.s0 = f0;
126
-           direction.s1 = f1;
127
-           direction.s2 = f2;
128
-
129
-           config.Set<webrtc::Beamforming>(new webrtc::Beamforming(true, geometry, direction));
130
-       } else {
131
-           config.Set<webrtc::Beamforming>(new webrtc::Beamforming(true, geometry));
132
-       }
133
-   }
134
+   webrtc::AudioProcessing::Config config;
135
+   config.echo_canceller.enabled = true;
136
+   // FIXME: Example code enables both gain controllers, but that seems sus
137
+   config.gain_controller1.enabled = gain_control;
138
+   config.gain_controller1.mode = webrtc::AudioProcessing::Config::GainController1::Mode::kAdaptiveDigital;
139
+   config.gain_controller1.analog_level_minimum = 0;
140
+   config.gain_controller1.analog_level_maximum = 255;
141
+   config.gain_controller2.enabled = gain_control;
142
+   config.high_pass_filter.enabled = high_pass_filter;
143
+   config.noise_suppression.enabled = noise_suppression;
144
+   config.noise_suppression.level = webrtc::AudioProcessing::Config::NoiseSuppression::kHigh;
145
+   // FIXME: expose pre/postamp gain
146
+   config.transient_suppression.enabled = transient_suppression;
147
+   config.voice_detection.enabled = voice_detection;
148
 
149
    webrtc::ProcessingConfig pconfig = {{
150
        webrtc::StreamConfig(rec_info->rate, rec_info->channels, false), /* input stream */
151
@@ -166,26 +81,15 @@
152
        webrtc::StreamConfig(play_info->rate, play_info->channels, false), /* reverse output stream */
153
    }};
154
 
155
-   auto apm = std::unique_ptr<webrtc::AudioProcessing>(webrtc::AudioProcessing::Create(config));
156
+   auto apm = std::unique_ptr<webrtc::AudioProcessing>(webrtc::AudioProcessingBuilder().Create());
157
+
158
+   apm->ApplyConfig(config);
159
+
160
    if ((res = apm->Initialize(pconfig)) != webrtc::AudioProcessing::kNoError) {
161
        spa_log_error(impl->log, "Error initialising webrtc audio processing module: %d", res);
162
        return -EINVAL;
163
    }
164
 
165
-   apm->high_pass_filter()->Enable(high_pass_filter);
166
-   // Always disable drift compensation since PipeWire will already do
167
-   // drift compensation on all sinks and sources linked to this echo-canceler
168
-   apm->echo_cancellation()->enable_drift_compensation(false);
169
-   apm->echo_cancellation()->Enable(true);
170
-   // TODO: wire up supression levels to args
171
-   apm->echo_cancellation()->set_suppression_level(webrtc::EchoCancellation::kHighSuppression);
172
-   apm->noise_suppression()->set_level(webrtc::NoiseSuppression::kHigh);
173
-   apm->noise_suppression()->Enable(noise_suppression);
174
-   apm->voice_detection()->Enable(voice_detection);
175
-   // TODO: wire up AGC parameters to args
176
-   apm->gain_control()->set_analog_level_limits(0, 255);
177
-   apm->gain_control()->set_mode(webrtc::GainControl::kAdaptiveDigital);
178
-   apm->gain_control()->Enable(gain_control);
179
    impl->apm = std::move(apm);
180
    impl->rec_info = *rec_info;
181
    impl->out_info = *out_info;
182
pipewire-0.3.79.tar.gz/spa/plugins/alsa/acp/volume.h -> pipewire-0.3.80.tar.gz/spa/plugins/alsa/acp/volume.h Changed
10
 
1
@@ -83,7 +83,7 @@
2
 
3
 static inline pa_volume_t pa_sw_volume_from_dB(double dB)
4
 {
5
-    if (isinf(dB) < 0 || dB <= PA_DECIBEL_MININFTY)
6
+    if (dB == -INFINITY || dB <= PA_DECIBEL_MININFTY)
7
         return PA_VOLUME_MUTED;
8
     return pa_sw_volume_from_linear(pa_volume_dB_to_linear(dB));
9
 }
10
pipewire-0.3.80.tar.gz/spa/plugins/alsa/alsa-compress-offload-device.c Added
201
 
1
@@ -0,0 +1,578 @@
2
+/* Spa ALSA Compress-Offload device */
3
+/* SPDX-FileCopyrightText: Copyright @ 2023 Carlos Rafael Giani */
4
+/* SPDX-License-Identifier: MIT */
5
+
6
+#include <sys/types.h>
7
+#include <limits.h>
8
+#include <dirent.h>
9
+
10
+#include <alsa/asoundlib.h>
11
+
12
+#include <spa/debug/dict.h>
13
+#include <spa/debug/log.h>
14
+#include <spa/debug/pod.h>
15
+#include <spa/node/node.h>
16
+#include <spa/support/plugin.h>
17
+#include <spa/monitor/device.h>
18
+#include <spa/monitor/utils.h>
19
+#include <spa/node/keys.h>
20
+#include <spa/param/param.h>
21
+#include <spa/pod/filter.h>
22
+#include <spa/pod/parser.h>
23
+#include <spa/utils/cleanup.h>
24
+#include <spa/utils/dict.h>
25
+#include <spa/utils/keys.h>
26
+#include <spa/utils/names.h>
27
+#include <spa/utils/string.h>
28
+
29
+#include "compress-offload-api-util.h"
30
+#include "alsa.h"
31
+
32
+static const char default_device = "hw:0";
33
+
34
+struct props {
35
+   char device64;
36
+   unsigned int card_nr;
37
+};
38
+
39
+static void reset_props(struct props *props)
40
+{
41
+   strncpy(props->device, default_device, 64);
42
+   props->card_nr = 0;
43
+}
44
+
45
+struct impl {
46
+   struct spa_handle handle;
47
+   struct spa_device device;
48
+
49
+   struct spa_log *log;
50
+
51
+   struct spa_hook_list hooks;
52
+
53
+   struct props props;
54
+   uint32_t n_nodes;
55
+   uint32_t n_capture;
56
+   uint32_t n_playback;
57
+
58
+   uint32_t profile;
59
+};
60
+
61
+#define ADD_DICT_ITEM(key, value) do { itemsn_items++ = SPA_DICT_ITEM_INIT(key, value); } while (0)
62
+
63
+static void emit_node(struct impl *this, const char *device_node, unsigned int device_nr,
64
+                      enum spa_compress_offload_direction direction, snd_ctl_card_info_t *cardinfo,
65
+                      uint32_t id)
66
+{
67
+   struct spa_dict_item items5;
68
+   uint32_t n_items = 0;
69
+   char alsa_path128, path180;
70
+   char node_name200;
71
+   char node_desc200;
72
+   struct spa_device_object_info info;
73
+   const char *stream;
74
+
75
+   spa_log_debug(this->log, "emitting node info for device %s (card nr %u device nr %u)",
76
+                 device_node, this->props.card_nr, device_nr);
77
+
78
+   info = SPA_DEVICE_OBJECT_INFO_INIT();
79
+   info.type = SPA_TYPE_INTERFACE_Node;
80
+
81
+   if (direction == SPA_COMPRESS_OFFLOAD_DIRECTION_PLAYBACK) {
82
+       stream = "playback";
83
+       info.factory_name = SPA_NAME_API_ALSA_COMPRESS_OFFLOAD_SINK;
84
+   } else {
85
+       stream = "capture";
86
+       /* TODO: This is not yet implemented, because getting Compress-Offload
87
+        * hardware that can capture audio is difficult to do. The only hardware
88
+        * known is the Wolfson ADSP; the only driver in the kernel that exposes
89
+        * Compress-Offload capture devices is the one for that hardware. */
90
+       assert(false);
91
+   }
92
+
93
+   info.change_mask = SPA_DEVICE_OBJECT_CHANGE_MASK_PROPS;
94
+
95
+   snprintf(alsa_path, sizeof(alsa_path), "%s,%u", this->props.device, device_nr);
96
+   snprintf(path, sizeof(path), "alsa:compressed:%s:%u:%s", snd_ctl_card_info_get_id(cardinfo), device_nr, stream);
97
+   snprintf(node_name, sizeof(node_name), "comprC%uD%u", this->props.card_nr, device_nr);
98
+   snprintf(node_desc, sizeof(node_desc), "Compress-Offload sink node (ALSA card %u device %u)", this->props.card_nr, device_nr);
99
+
100
+   ADD_DICT_ITEM(SPA_KEY_NODE_NAME,        node_name);
101
+   ADD_DICT_ITEM(SPA_KEY_NODE_DESCRIPTION, node_desc);
102
+   ADD_DICT_ITEM(SPA_KEY_OBJECT_PATH,      path);
103
+   ADD_DICT_ITEM(SPA_KEY_API_ALSA_PATH,    alsa_path);
104
+   /* NOTE: Set alsa.name, since session managers look for this, or for
105
+    * SPA_KEY_API_ALSA_PCM_NAME, or other items. The best fit in this
106
+    * case seems to be alsa.name, since SPA_KEY_API_ALSA_PCM_NAME is
107
+    * PCM specific, as the name suggests. If none of these items are
108
+    * provided, session managers may not work properly. WirePlumber's
109
+    * alsa.lua script looks for these for example.
110
+    * And, since we have no good way of getting a name, just reuse
111
+    * the alsa_path here. */
112
+   ADD_DICT_ITEM("alsa.name",              alsa_path);
113
+
114
+   info.props = &SPA_DICT_INIT(items, n_items);
115
+
116
+   spa_log_debug(this->log, "node information:");
117
+   spa_debug_dict(2, info.props);
118
+
119
+   spa_device_emit_object_info(&this->hooks, id, &info);
120
+}
121
+
122
+static int set_profile(struct impl *this, uint32_t id)
123
+{
124
+   int ret = 0;
125
+   uint32_t i, n_cap, n_play;
126
+   char prefix32;
127
+   int prefix_length;
128
+   struct dirent *entry;
129
+   DIR *snd_dir = NULL;
130
+   snd_ctl_t *ctl_handle = NULL;
131
+   snd_ctl_card_info_t *cardinfo;
132
+
133
+   spa_log_debug(this->log, "enumerate Compress-Offload nodes for card %s; profile: %d",
134
+                 this->props.device, id);
135
+
136
+   if ((ret = snd_ctl_open(&ctl_handle, this->props.device, 0)) < 0) {
137
+       spa_log_error(this->log, "can't open control for card %s: %s",
138
+                     this->props.device, snd_strerror(ret));
139
+       goto finish;
140
+   }
141
+
142
+   this->profile = id;
143
+
144
+   snd_ctl_card_info_alloca(&cardinfo);
145
+   if ((ret = snd_ctl_card_info(ctl_handle, cardinfo)) < 0) {
146
+       spa_log_error(this->log, "error card info: %s", snd_strerror(ret));
147
+       goto finish;
148
+   }
149
+
150
+   /* Clear any previous node object info. */
151
+   for (i = 0; i < this->n_nodes; i++)
152
+       spa_device_emit_object_info(&this->hooks, i, NULL);
153
+
154
+   this->n_nodes = this->n_capture = this->n_playback = 0;
155
+
156
+   /* Profile ID 0 is the "off" profile, that is, the profile where the device
157
+    * is "disabled". To implement such a disabled state, simply exit here without
158
+    * adding any nodes after we removed any existing one (see above). */
159
+   if (id == 0)
160
+   {
161
+       spa_log_debug(this->log, "\"Off\" profile selected - exiting without "
162
+                     "creating any nodes after all previous ones were removed");
163
+       goto finish;
164
+   }
165
+
166
+   spa_scnprintf(prefix, sizeof(prefix), "comprC%uD", this->props.card_nr);
167
+   prefix_length = strlen(prefix);
168
+
169
+   /* There is no API to enumerate all Compress-Offload devices, so we have
170
+    * to stick to walking through the /dev/snd directory entries and looking
171
+    * for device nodes that match the comprC<card number>D prefix. */
172
+   snd_dir = opendir("/dev/snd");
173
+   if (snd_dir == NULL)
174
+       goto errno_error;
175
+
176
+   i = 0;
177
+   i = n_cap = n_play = 0;
178
+   while ((errno = 0, entry = readdir(snd_dir)) != NULL) {
179
+       long long device_nr;
180
+       enum spa_compress_offload_direction direction;
181
+
182
+       if (!(entry->d_type == DT_CHR && spa_strstartswith(entry->d_name, prefix)))
183
+           continue;
184
+
185
+       /* Parse the device number from the device filename. We know that the filename
186
+        * is always structured like this: comprC<card number>D<device number>
187
+        * We consider "comprC<card number>D" to form the "prefix" here. Right after
188
+        * that prefix, the device number can be parsed, so skip the prefix. */
189
+       device_nr = strtol(entry->d_name + prefix_length, NULL, 10);
190
+       if ((device_nr < 0) || (device_nr > UINT_MAX)) {
191
+           spa_log_warn(this->log, "device %s contains unusable device number; "
192
+                        "skipping", entry->d_name);
193
+           continue;
194
+       }
195
+
196
+       if (get_compress_offload_device_direction(this->props.card_nr, device_nr,
197
+                                                 this->log, &direction) < 0)
198
+           goto finish;
199
+
200
+       switch (direction) {
201
pipewire-0.3.79.tar.gz/spa/plugins/alsa/alsa-pcm-device.c -> pipewire-0.3.80.tar.gz/spa/plugins/alsa/alsa-pcm-device.c Changed
38
 
1
@@ -154,7 +154,6 @@
2
    snd_pcm_info_t *pcminfo;
3
    snd_ctl_card_info_t *cardinfo;
4
 
5
-   spa_log_debug(this->log, "profile %d", id);
6
    this->profile = id;
7
 
8
    snd_ctl_card_info_alloca(&cardinfo);
9
@@ -216,7 +215,9 @@
10
    snd_ctl_t *ctl_hndl;
11
    int err;
12
 
13
-   spa_log_debug(this->log, "open card %s", this->props.device);
14
+   spa_log_debug(this->log, "enumerate PCM nodes for card %s; profile: %d",
15
+                 this->props.device, id);
16
+
17
    if ((err = snd_ctl_open(&ctl_hndl, this->props.device, 0)) < 0) {
18
        spa_log_error(this->log, "can't open control for card %s: %s",
19
                this->props.device, snd_strerror(err));
20
@@ -225,7 +226,7 @@
21
 
22
    err = activate_profile(this, ctl_hndl, id);
23
 
24
-   spa_log_debug(this->log, "close card %s", this->props.device);
25
+   spa_log_debug(this->log, "done enumerating PCM nodes for card %s", this->props.device);
26
    snd_ctl_close(ctl_hndl);
27
 
28
    return err;
29
@@ -551,7 +552,7 @@
30
    return 1;
31
 }
32
 
33
-const struct spa_handle_factory spa_alsa_device_factory = {
34
+const struct spa_handle_factory spa_alsa_pcm_device_factory = {
35
    SPA_VERSION_HANDLE_FACTORY,
36
    SPA_NAME_API_ALSA_PCM_DEVICE,
37
    NULL,
38
pipewire-0.3.79.tar.gz/spa/plugins/alsa/alsa-pcm-sink.c -> pipewire-0.3.80.tar.gz/spa/plugins/alsa/alsa-pcm-sink.c Changed
49
 
1
@@ -15,6 +15,8 @@
2
 #include <spa/utils/string.h>
3
 #include <spa/param/audio/format.h>
4
 #include <spa/pod/filter.h>
5
+#include <spa/debug/log.h>
6
+#include <spa/debug/pod.h>
7
 
8
 #include "alsa-pcm.h"
9
 
10
@@ -669,7 +671,7 @@
11
             const struct spa_pod *param)
12
 {
13
    struct state *this = object;
14
-   int res;
15
+   int res = 0;
16
 
17
    spa_return_val_if_fail(this != NULL, -EINVAL);
18
 
19
@@ -693,9 +695,12 @@
20
        this->port_info.change_mask |= SPA_PORT_CHANGE_MASK_PARAMS;
21
        this->port_paramsPORT_Latency.user++;
22
        emit_port_info(this, false);
23
-       res = 0;
24
        break;
25
    }
26
+   case SPA_PARAM_Tag:
27
+       if (param != NULL)
28
+           spa_debug_log_pod(this->log, SPA_LOG_LEVEL_DEBUG, 0, NULL, param);
29
+       break;
30
    default:
31
        res = -ENOENT;
32
        break;
33
@@ -815,14 +820,9 @@
34
        spa_list_append(&this->ready, &b->link);
35
        SPA_FLAG_CLEAR(b->flags, BUFFER_FLAG_OUT);
36
        io->buffer_id = SPA_ID_INVALID;
37
-
38
-       spa_alsa_write(this);
39
-
40
-       io->status = SPA_STATUS_OK;
41
    }
42
-   else if (!spa_list_is_empty(&this->ready)) {
43
+   if (!spa_list_is_empty(&this->ready)) {
44
        spa_alsa_write(this);
45
-
46
        io->status = SPA_STATUS_OK;
47
    }
48
    return SPA_STATUS_HAVE_DATA;
49
pipewire-0.3.79.tar.gz/spa/plugins/alsa/alsa-pcm.c -> pipewire-0.3.80.tar.gz/spa/plugins/alsa/alsa-pcm.c Changed
201
 
1
@@ -920,7 +920,7 @@
2
        min = max = rate;
3
 
4
    if (rate == 0)
5
-       rate = state->position ? state->position->clock.rate.denom : DEFAULT_RATE;
6
+       rate = state->position ? state->position->clock.target_rate.denom : DEFAULT_RATE;
7
 
8
    rate = SPA_CLAMP(rate, min, max);
9
 
10
@@ -1349,6 +1349,17 @@
11
    return 1;
12
 }
13
 
14
+/* find smaller power of 2 */
15
+static uint32_t flp2(uint32_t x)
16
+{
17
+   x = x | (x >> 1);
18
+   x = x | (x >> 2);
19
+   x = x | (x >> 4);
20
+   x = x | (x >> 8);
21
+   x = x | (x >> 16);
22
+   return x - (x >> 1);
23
+}
24
+
25
 int
26
 spa_alsa_enum_format(struct state *state, int seq, uint32_t start, uint32_t num,
27
             const struct spa_pod *filter)
28
@@ -1426,6 +1437,7 @@
29
    unsigned int periods;
30
    bool match = true, planar = false, is_batch;
31
    char spdif_params128 = "";
32
+   uint32_t default_period, latency;
33
 
34
    spa_log_debug(state->log, "opened:%d format:%d started:%d", state->opened,
35
            state->have_format, state->started);
36
@@ -1651,12 +1663,15 @@
37
    period_size = state->default_period_size;
38
    is_batch = snd_pcm_hw_params_is_batch(params) && !state->disable_batch;
39
 
40
+   default_period = SPA_SCALE32_UP(DEFAULT_PERIOD, state->rate, DEFAULT_RATE);
41
+   default_period = flp2(2 * default_period - 1);
42
+
43
    /* no period size specified. If we are batch or not using timers,
44
     * use the graph duration as the period */
45
    if (period_size == 0 && (is_batch || state->disable_tsched))
46
-       period_size = state->position ? state->position->clock.target_duration : DEFAULT_PERIOD;
47
+       period_size = state->position ? state->position->clock.target_duration : default_period;
48
    if (period_size == 0)
49
-       period_size = DEFAULT_PERIOD;
50
+       period_size = default_period;
51
 
52
    if (!state->disable_tsched) {
53
        if (is_batch) {
54
@@ -1664,7 +1679,7 @@
55
             * the period smaller and add one period of headroom. Limit the
56
             * period size to our default so that we don't create too much
57
             * headroom. */
58
-           period_size = SPA_MIN(period_size, DEFAULT_PERIOD) / 2;
59
+           period_size = SPA_MIN(period_size, default_period) / 2;
60
        } else {
61
            /* disable ALSA wakeups */
62
            if (snd_pcm_hw_params_can_disable_period_wakeup(params))
63
@@ -1723,9 +1738,12 @@
64
    state->headroom = SPA_MIN(state->headroom, state->buffer_frames);
65
    state->start_delay = state->default_start_delay;
66
 
67
+   latency = SPA_MAX(state->min_delay, SPA_MIN(state->max_delay, state->headroom));
68
+   if (state->position != NULL)
69
+       latency = SPA_SCALE32_UP(latency, state->position->clock.target_rate.denom, state->rate);
70
+
71
    state->latencystate->port_direction.min_rate =
72
-       state->latencystate->port_direction.max_rate =
73
-           SPA_MAX(state->min_delay, SPA_MIN(state->max_delay, state->headroom));
74
+       state->latencystate->port_direction.max_rate = latency;
75
 
76
    spa_log_info(state->log, "%s (%s): format:%s access:%s-%s rate:%d channels:%d "
77
            "buffer frames %lu, period frames %lu, periods %u, frame_size %zd "
78
@@ -1931,16 +1949,15 @@
79
 
80
        delay = SPA_TIMEVAL_TO_USEC(&diff);
81
        missing = delay * state->rate / SPA_USEC_PER_SEC;
82
-       if (missing == 0)
83
-           missing = state->threshold;
84
+       missing += state->start_delay + state->threshold + state->headroom;
85
 
86
        spa_log_trace(state->log, "%p: xrun of %"PRIu64" usec %"PRIu64,
87
                state, delay, missing);
88
 
89
-       if (state->clock)
90
-           state->clock->xrun += missing;
91
-       state->sample_count += missing;
92
-
93
+       if (state->clock) {
94
+           state->clock->xrun += SPA_SCALE32_UP(missing,
95
+                   state->clock->rate.denom, state->rate);
96
+       }
97
        spa_node_call_xrun(&state->callbacks,
98
                SPA_TIMEVAL_TO_USEC(&trigger), delay, NULL);
99
        break;
100
@@ -1977,16 +1994,16 @@
101
 
102
 static int get_avail(struct state *state, uint64_t current_time, snd_pcm_uframes_t *delay)
103
 {
104
-   int res, missed;
105
+   int res, suppressed;
106
    snd_pcm_sframes_t avail;
107
 
108
    if (SPA_UNLIKELY((avail = snd_pcm_avail(state->hndl)) < 0)) {
109
        if ((res = alsa_recover(state, avail)) < 0)
110
            return res;
111
        if ((avail = snd_pcm_avail(state->hndl)) < 0) {
112
-           if ((missed = spa_ratelimit_test(&state->rate_limit, current_time)) >= 0) {
113
-               spa_log_warn(state->log, "%s: (%d missed) snd_pcm_avail after recover: %s",
114
-                       state->props.device, missed, snd_strerror(avail));
115
+           if ((suppressed = spa_ratelimit_test(&state->rate_limit, current_time)) >= 0) {
116
+               spa_log_warn(state->log, "%s: (%d suppressed) snd_pcm_avail after recover: %s",
117
+                       state->props.device, suppressed, snd_strerror(avail));
118
            }
119
            avail = state->threshold * 2;
120
        }
121
@@ -2001,9 +2018,9 @@
122
        uint64_t then;
123
 
124
        if ((res = snd_pcm_htimestamp(state->hndl, &havail, &tstamp)) < 0) {
125
-           if ((missed = spa_ratelimit_test(&state->rate_limit, current_time)) >= 0) {
126
-               spa_log_warn(state->log, "%s: (%d missed) snd_pcm_htimestamp error: %s",
127
-                   state->props.device, missed, snd_strerror(res));
128
+           if ((suppressed = spa_ratelimit_test(&state->rate_limit, current_time)) >= 0) {
129
+               spa_log_warn(state->log, "%s: (%d suppressed) snd_pcm_htimestamp error: %s",
130
+                   state->props.device, suppressed, snd_strerror(res));
131
            }
132
            return avail;
133
        }
134
@@ -2029,9 +2046,9 @@
135
                    state->htimestamp_error = 0;
136
                    state->htimestamp = false;
137
                }
138
-               else if ((missed = spa_ratelimit_test(&state->rate_limit, current_time)) >= 0) {
139
-                   spa_log_warn(state->log, "%s: (%d missed) impossible htimestamp diff:%"PRIi64,
140
-                       state->props.device, missed, diff);
141
+               else if ((suppressed = spa_ratelimit_test(&state->rate_limit, current_time)) >= 0) {
142
+                   spa_log_warn(state->log, "%s: (%d suppressed) impossible htimestamp diff:%"PRIi64,
143
+                       state->props.device, suppressed, diff);
144
                }
145
            }
146
        }
147
@@ -2205,8 +2222,15 @@
148
    if (SPA_UNLIKELY(state->position  == NULL))
149
        return 0;
150
 
151
-   target_duration = state->position->clock.target_duration;
152
-   target_rate = state->position->clock.target_rate;
153
+   if (state->disable_tsched && state->started && !state->following) {
154
+       target_duration = state->period_frames;
155
+       target_rate = SPA_FRACTION(1, state->rate);
156
+       state->position->clock.target_duration = target_duration;
157
+       state->position->clock.target_rate = target_rate;
158
+   } else {
159
+       target_duration = state->position->clock.target_duration;
160
+       target_rate = state->position->clock.target_rate;
161
+   }
162
 
163
    if (SPA_UNLIKELY((state->duration != target_duration) ||
164
        (state->rate_denom != target_rate.denom))) {
165
@@ -2229,7 +2253,7 @@
166
    const snd_pcm_channel_area_t *my_areas;
167
    snd_pcm_uframes_t written, frames, offset, off, to_write, total_written, max_write;
168
    snd_pcm_sframes_t commitres;
169
-   int res, missed;
170
+   int res, suppressed;
171
    size_t frame_size = state->frame_size;
172
 
173
    if ((res = check_position_config(state)) < 0)
174
@@ -2257,11 +2281,11 @@
175
            else
176
                lev = SPA_LOG_LEVEL_INFO;
177
 
178
-           if ((missed = spa_ratelimit_test(&state->rate_limit, current_time)) >= 0) {
179
+           if ((suppressed = spa_ratelimit_test(&state->rate_limit, current_time)) >= 0) {
180
                spa_log_lev(state->log, lev, "%s: follower avail:%lu delay:%ld "
181
-                       "target:%ld thr:%u, resync (%d missed)",
182
+                       "target:%ld thr:%u, resync (%d suppressed)",
183
                        state->props.device, avail, delay,
184
-                       target, state->threshold, missed);
185
+                       target, state->threshold, suppressed);
186
            }
187
 
188
            if (avail > target)
189
@@ -2467,7 +2491,7 @@
190
    const snd_pcm_channel_area_t *my_areas;
191
    snd_pcm_uframes_t read, frames, offset;
192
    snd_pcm_sframes_t commitres;
193
-   int res, missed;
194
+   int res, suppressed;
195
 
196
    if ((res = check_position_config(state)) < 0)
197
        return res;
198
@@ -2494,10 +2518,10 @@
199
            else
200
                lev = SPA_LOG_LEVEL_INFO;
201
pipewire-0.3.79.tar.gz/spa/plugins/alsa/alsa-seq-bridge.c -> pipewire-0.3.80.tar.gz/spa/plugins/alsa/alsa-seq-bridge.c Changed
12
 
1
@@ -691,7 +691,9 @@
2
    case SPA_PARAM_Latency:
3
    {
4
        struct spa_latency_info info;
5
-       if ((res = spa_latency_parse(param, &info)) < 0)
6
+       if (param == NULL)
7
+           info = SPA_LATENCY_INFO(SPA_DIRECTION_REVERSE(direction));
8
+       else if ((res = spa_latency_parse(param, &info)) < 0)
9
            return res;
10
        if (direction == info.direction)
11
            return -EINVAL;
12
pipewire-0.3.79.tar.gz/spa/plugins/alsa/alsa-udev.c -> pipewire-0.3.80.tar.gz/spa/plugins/alsa/alsa-udev.c Changed
201
 
1
@@ -24,24 +24,52 @@
2
 #include <spa/support/plugin.h>
3
 #include <spa/monitor/device.h>
4
 #include <spa/monitor/utils.h>
5
+#include <spa/debug/log.h>
6
+#include <spa/debug/dict.h>
7
 
8
 #include "alsa.h"
9
 
10
-#define MAX_DEVICES    64
11
+#define MAX_CARDS  64
12
 
13
 #define ACTION_ADD 0
14
 #define ACTION_REMOVE  1
15
 #define ACTION_DISABLE 2
16
 
17
-struct device {
18
-   uint32_t id;
19
-   struct udev_device *dev;
20
+/* Used for unavailable devices in the card structure. */
21
+#define ID_DEVICE_NOT_SUPPORTED 0
22
+
23
+/* This represents an ALSA card.
24
+ * One card can have up to 1 PCM and 1 Compress-Offload device. */
25
+struct card {
26
+   unsigned int card_nr;
27
+   struct udev_device *udev_device;
28
    unsigned int unavailable:1;
29
    unsigned int accessible:1;
30
    unsigned int ignored:1;
31
    unsigned int emitted:1;
32
+
33
+   /* Local SPA object IDs. (Global IDs are produced by PipeWire
34
+    * out of this using its registry.) Compress-Offload or PCM
35
+    * is not available, the corresponding ID is set to
36
+    * ID_DEVICE_NOT_SUPPORTED (= 0).
37
+    * PCM device IDs are (card nr + 1) * 2, and Compress-Offload
38
+    * device IDs are (card nr + 1) * 2 + 1. Assigning IDs like this
39
+    * makes it easy to deal with removed devices. (card nr + 1)
40
+    * is used because 0 is a valid ALSA card number. */
41
+   uint32_t pcm_device_id;
42
+   uint32_t compress_offload_device_id;
43
 };
44
 
45
+static uint32_t calc_pcm_device_id(struct card *card)
46
+{
47
+   return (card->card_nr + 1) * 2 + 0;
48
+}
49
+
50
+static uint32_t calc_compress_offload_device_id(struct card *card)
51
+{
52
+   return (card->card_nr + 1) * 2 + 1;
53
+}
54
+
55
 struct impl {
56
    struct spa_handle handle;
57
    struct spa_device device;
58
@@ -58,8 +86,8 @@
59
    struct udev *udev;
60
    struct udev_monitor *umonitor;
61
 
62
-   struct device devicesMAX_DEVICES;
63
-        uint32_t n_devices;
64
+   struct card cardsMAX_CARDS;
65
+        unsigned int n_cards;
66
 
67
    struct spa_source source;
68
    struct spa_source notify;
69
@@ -84,58 +112,60 @@
70
    return 0;
71
 }
72
 
73
-static struct device *add_device(struct impl *this, uint32_t id, struct udev_device *dev)
74
+static struct card *add_card(struct impl *this, unsigned int card_nr, struct udev_device *udev_device)
75
 {
76
-   struct device *device;
77
+   struct card *card;
78
 
79
-   if (this->n_devices >= MAX_DEVICES)
80
+   if (this->n_cards >= MAX_CARDS)
81
        return NULL;
82
-   device = &this->devicesthis->n_devices++;
83
-   spa_zero(*device);
84
-   device->id = id;
85
-   udev_device_ref(dev);
86
-   device->dev = dev;
87
-   return device;
88
+
89
+   card = &this->cardsthis->n_cards++;
90
+   spa_zero(*card);
91
+   card->card_nr = card_nr;
92
+   udev_device_ref(udev_device);
93
+   card->udev_device = udev_device;
94
+
95
+   return card;
96
 }
97
 
98
-static struct device *find_device(struct impl *this, uint32_t id)
99
+static struct card *find_card(struct impl *this, unsigned int card_nr)
100
 {
101
-   uint32_t i;
102
-   for (i = 0; i < this->n_devices; i++) {
103
-       if (this->devicesi.id == id)
104
-           return &this->devicesi;
105
+   unsigned int i;
106
+   for (i = 0; i < this->n_cards; i++) {
107
+       if (this->cardsi.card_nr == card_nr)
108
+           return &this->cardsi;
109
    }
110
    return NULL;
111
 }
112
 
113
-static void remove_device(struct impl *this, struct device *device)
114
+static void remove_card(struct impl *this, struct card *card)
115
 {
116
-   udev_device_unref(device->dev);
117
-   *device = this->devices--this->n_devices;
118
+   udev_device_unref(card->udev_device);
119
+   *card = this->cards--this->n_cards;
120
 }
121
 
122
-static void clear_devices(struct impl *this)
123
+static void clear_cards(struct impl *this)
124
 {
125
-        uint32_t i;
126
-   for (i = 0; i < this->n_devices; i++)
127
-           udev_device_unref(this->devicesi.dev);
128
-   this->n_devices = 0;
129
+        unsigned int i;
130
+   for (i = 0; i < this->n_cards; i++)
131
+           udev_device_unref(this->cardsi.udev_device);
132
+   this->n_cards = 0;
133
 }
134
 
135
-static uint32_t get_card_id(struct impl *this, struct udev_device *dev)
136
+static unsigned int get_card_nr(struct impl *this, struct udev_device *udev_device)
137
 {
138
    const char *e, *str;
139
 
140
-   if (udev_device_get_property_value(dev, "ACP_IGNORE"))
141
+   if (udev_device_get_property_value(udev_device, "ACP_IGNORE"))
142
        return SPA_ID_INVALID;
143
 
144
-   if ((str = udev_device_get_property_value(dev, "SOUND_CLASS")) && spa_streq(str, "modem"))
145
+   if ((str = udev_device_get_property_value(udev_device, "SOUND_CLASS")) && spa_streq(str, "modem"))
146
        return SPA_ID_INVALID;
147
 
148
-   if (udev_device_get_property_value(dev, "SOUND_INITIALIZED") == NULL)
149
+   if (udev_device_get_property_value(udev_device, "SOUND_INITIALIZED") == NULL)
150
        return SPA_ID_INVALID;
151
 
152
-   if ((str = udev_device_get_property_value(dev, "DEVPATH")) == NULL)
153
+   if ((str = udev_device_get_property_value(udev_device, "DEVPATH")) == NULL)
154
        return SPA_ID_INVALID;
155
 
156
    if ((e = strrchr(str, '/')) == NULL)
157
@@ -244,7 +274,7 @@
158
    return spa_strstartswith(buf, "modem") ? -ENXIO : 0;
159
 }
160
 
161
-static int get_num_pcm_devices(unsigned int card_id)
162
+static int get_num_pcm_devices(unsigned int card_nr)
163
 {
164
    char prefix32;
165
    struct dirent *entry;
166
@@ -253,7 +283,7 @@
167
 
168
    /* Check if card has PCM devices, without opening them */
169
 
170
-   spa_scnprintf(prefix, sizeof(prefix), "pcmC%uD", card_id);
171
+   spa_scnprintf(prefix, sizeof(prefix), "pcmC%uD", card_nr);
172
 
173
    spa_autoptr(DIR) snd = opendir("/dev/snd");
174
    if (snd == NULL)
175
@@ -274,7 +304,33 @@
176
    return errno != 0 ? -errno : num_dev;
177
 }
178
 
179
-static int check_device_available(struct impl *this, struct device *device, int *num_pcm)
180
+static int get_num_compress_offload_devices(unsigned int card_nr)
181
+{
182
+   char prefix32;
183
+   struct dirent *entry;
184
+   int num_dev = 0;
185
+
186
+   /* Check if card has Compress-Offload devices, without opening them */
187
+
188
+   spa_scnprintf(prefix, sizeof(prefix), "comprC%uD", card_nr);
189
+
190
+   spa_autoptr(DIR) snd = opendir("/dev/snd");
191
+   if (snd == NULL)
192
+       return -errno;
193
+
194
+   while ((errno = 0, entry = readdir(snd)) != NULL) {
195
+       if (!(entry->d_type == DT_CHR &&
196
+               spa_strstartswith(entry->d_name, prefix)))
197
+           continue;
198
+
199
+       ++num_dev;
200
+   }
201
pipewire-0.3.79.tar.gz/spa/plugins/alsa/alsa.c -> pipewire-0.3.80.tar.gz/spa/plugins/alsa/alsa.c Changed
34
 
1
@@ -12,11 +12,12 @@
2
 extern const struct spa_handle_factory spa_alsa_source_factory;
3
 extern const struct spa_handle_factory spa_alsa_sink_factory;
4
 extern const struct spa_handle_factory spa_alsa_udev_factory;
5
-extern const struct spa_handle_factory spa_alsa_device_factory;
6
+extern const struct spa_handle_factory spa_alsa_pcm_device_factory;
7
 extern const struct spa_handle_factory spa_alsa_seq_bridge_factory;
8
 extern const struct spa_handle_factory spa_alsa_acp_device_factory;
9
 #ifdef HAVE_ALSA_COMPRESS_OFFLOAD
10
 extern const struct spa_handle_factory spa_alsa_compress_offload_sink_factory;
11
+extern const struct spa_handle_factory spa_alsa_compress_offload_device_factory;
12
 #endif
13
 
14
 struct spa_log_topic log_topic = SPA_LOG_TOPIC(0, "spa.alsa");
15
@@ -39,7 +40,7 @@
16
        *factory = &spa_alsa_udev_factory;
17
        break;
18
    case 3:
19
-       *factory = &spa_alsa_device_factory;
20
+       *factory = &spa_alsa_pcm_device_factory;
21
        break;
22
    case 4:
23
        *factory = &spa_alsa_seq_bridge_factory;
24
@@ -51,6 +52,9 @@
25
    case 6:
26
        *factory = &spa_alsa_compress_offload_sink_factory;
27
        break;
28
+   case 7:
29
+       *factory = &spa_alsa_compress_offload_device_factory;
30
+       break;
31
 #endif
32
    default:
33
        return 0;
34
pipewire-0.3.80.tar.gz/spa/plugins/alsa/compress-offload-api-util.c Added
38
 
1
@@ -0,0 +1,36 @@
2
+#include <errno.h>
3
+#include <inttypes.h>
4
+#include "compress-offload-api.h"
5
+#include "compress-offload-api-util.h"
6
+
7
+int get_compress_offload_device_direction(int card_nr, int device_nr,
8
+                                          struct spa_log *log,
9
+                                          enum spa_compress_offload_direction *direction)
10
+{
11
+   int ret = 0;
12
+   struct compress_offload_api_context *device_context;
13
+   const struct snd_compr_caps *compr_caps;
14
+
15
+   device_context = compress_offload_api_open(card_nr, device_nr, log);
16
+   if (device_context == NULL)
17
+       return -errno;
18
+
19
+   compr_caps = compress_offload_api_get_caps(device_context);
20
+
21
+   switch (compr_caps->direction) {
22
+   case SND_COMPRESS_PLAYBACK:
23
+       *direction = SPA_COMPRESS_OFFLOAD_DIRECTION_PLAYBACK;
24
+       break;
25
+   case SND_COMPRESS_CAPTURE:
26
+       *direction = SPA_COMPRESS_OFFLOAD_DIRECTION_CAPTURE;
27
+       break;
28
+   default:
29
+       spa_log_error(log, "card nr %d device nr %d: unknown direction %#" PRIx32,
30
+                    card_nr, device_nr, (uint32_t)(compr_caps->direction));
31
+       ret = -EINVAL;
32
+   }
33
+
34
+   compress_offload_api_close(device_context);
35
+
36
+   return ret;
37
+}
38
pipewire-0.3.80.tar.gz/spa/plugins/alsa/compress-offload-api-util.h Added
32
 
1
@@ -0,0 +1,30 @@
2
+#ifndef SPA_ALSA_COMPRESS_OFFLOAD_DEVICE_UTIL_H
3
+#define SPA_ALSA_COMPRESS_OFFLOAD_DEVICE_UTIL_H
4
+
5
+#include <spa/support/log.h>
6
+
7
+#if defined(__GNUC__) && __GNUC__ >= 4
8
+#define COMPR_API_PRIVATE __attribute__((visibility("hidden")))
9
+#elif defined(__SUNPRO_C) && (__SUNPRO_C >= 0x590)
10
+#define COMPR_API_PRIVATE __attribute__((visibility("hidden")))
11
+#elif defined(__SUNPRO_C) && (__SUNPRO_C >= 0x550)
12
+#define COMPR_API_PRIVATE __hidden
13
+#else
14
+#define COMPR_API_PRIVATE
15
+#endif
16
+
17
+enum spa_compress_offload_direction {
18
+   SPA_COMPRESS_OFFLOAD_DIRECTION_PLAYBACK,
19
+   SPA_COMPRESS_OFFLOAD_DIRECTION_CAPTURE
20
+};
21
+
22
+/* This exists for situations where both the direction of the compress-offload
23
+ * device and the functions from asoundlib.h are needed. It is not possible to
24
+ * include asoundlib.h and the compress-offload headers in the same C file,
25
+ * since these headers contain conflicting declarations. Provide this direction
26
+ * check function to keep the compress-offload headers encapsulated. */
27
+COMPR_API_PRIVATE int get_compress_offload_device_direction(int card_nr, int device_nr,
28
+                                                            struct spa_log *log,
29
+                                                            enum spa_compress_offload_direction *direction);
30
+
31
+#endif /* SPA_ALSA_COMPRESS_OFFLOAD_DEVICE_UTIL_H */
32
pipewire-0.3.79.tar.gz/spa/plugins/alsa/compress-offload-api.h -> pipewire-0.3.80.tar.gz/spa/plugins/alsa/compress-offload-api.h Changed
25
 
1
@@ -6,22 +6,12 @@
2
 #include <sound/compress_offload.h>
3
 #include <sound/compress_params.h>
4
 #include <spa/support/log.h>
5
+#include "compress-offload-api-util.h"
6
 
7
 
8
 struct compress_offload_api_context;
9
 
10
 
11
-#if defined(__GNUC__) && __GNUC__ >= 4
12
-#define COMPR_API_PRIVATE __attribute__((visibility("hidden")))
13
-#elif defined(__SUNPRO_C) && (__SUNPRO_C >= 0x590)
14
-#define COMPR_API_PRIVATE __attribute__((visibility("hidden")))
15
-#elif defined(__SUNPRO_C) && (__SUNPRO_C >= 0x550)
16
-#define COMPR_API_PRIVATE __hidden
17
-#else
18
-#define COMPR_API_PRIVATE
19
-#endif
20
-
21
-
22
 /* This is a simple encapsulation of the ALSA Compress-Offload API
23
  * and its ioctl calls. It is intentionally not using any PipeWire
24
  * or SPA headers to allow for porting it or extracting it as its
25
pipewire-0.3.79.tar.gz/spa/plugins/alsa/meson.build -> pipewire-0.3.80.tar.gz/spa/plugins/alsa/meson.build Changed
13
 
1
@@ -15,7 +15,10 @@
2
                 'alsa-seq.c'
3
 
4
 if compress_offload_option.allowed()
5
-  spa_alsa_sources +=  'alsa-compress-offload-sink.c', 'compress-offload-api.c' 
6
+  spa_alsa_sources += 'alsa-compress-offload-sink.c',
7
+                       'alsa-compress-offload-device.c',
8
+                       'compress-offload-api-util.c',
9
+                       'compress-offload-api.c'
10
 endif
11
 
12
 spa_alsa = shared_library(
13
pipewire-0.3.79.tar.gz/spa/plugins/audioconvert/audioadapter.c -> pipewire-0.3.80.tar.gz/spa/plugins/audioconvert/audioadapter.c Changed
201
 
1
@@ -20,6 +20,7 @@
2
 #include <spa/param/param.h>
3
 #include <spa/param/audio/format-utils.h>
4
 #include <spa/param/latency-utils.h>
5
+#include <spa/param/tag-utils.h>
6
 #include <spa/debug/format.h>
7
 #include <spa/debug/pod.h>
8
 #include <spa/debug/log.h>
9
@@ -75,7 +76,8 @@
10
 #define IDX_PortConfig     5
11
 #define IDX_Latency        6
12
 #define IDX_ProcessLatency 7
13
-#define N_NODE_PARAMS      8
14
+#define IDX_Tag            8
15
+#define N_NODE_PARAMS      9
16
    struct spa_param_info paramsN_NODE_PARAMS;
17
    uint32_t convert_params_flagsN_NODE_PARAMS;
18
    uint32_t follower_params_flagsN_NODE_PARAMS;
19
@@ -93,6 +95,7 @@
20
    unsigned int async:1;
21
    unsigned int passthrough:1;
22
    unsigned int follower_removing:1;
23
+   unsigned int in_recalc;
24
 };
25
 
26
 /** \endcond */
27
@@ -204,6 +207,7 @@
28
    case SPA_PARAM_EnumFormat:
29
    case SPA_PARAM_Format:
30
    case SPA_PARAM_Latency:
31
+   case SPA_PARAM_Tag:
32
        res = spa_node_port_enum_params_sync(this->follower,
33
                this->direction, 0,
34
                id, &result.next, filter, &result.param, &b.b);
35
@@ -541,6 +545,80 @@
36
 
37
 static const struct spa_node_events follower_node_events;
38
 
39
+static int recalc_latency(struct impl *this, struct spa_node *src, enum spa_direction direction,
40
+       uint32_t port_id, struct spa_node *dst)
41
+{
42
+   struct spa_pod_builder b = { 0 };
43
+   uint8_t buffer1024;
44
+   struct spa_pod *param;
45
+   uint32_t index = 0;
46
+   struct spa_latency_info latency;
47
+   int res;
48
+
49
+   spa_log_info(this->log, "%p: %d:%d", this, direction, port_id);
50
+
51
+   if (this->target == this->follower)
52
+       return 0;
53
+
54
+   while (true) {
55
+       spa_pod_builder_init(&b, buffer, sizeof(buffer));
56
+       if ((res = spa_node_port_enum_params_sync(src,
57
+                       direction, port_id, SPA_PARAM_Latency,
58
+                       &index, NULL, &param, &b)) != 1) {
59
+           param = NULL;
60
+           break;
61
+       }
62
+       if ((res = spa_latency_parse(param, &latency)) < 0)
63
+           return res;
64
+       if (latency.direction == direction)
65
+           break;
66
+   }
67
+   if ((res = spa_node_port_set_param(dst,
68
+                   SPA_DIRECTION_REVERSE(direction), 0,
69
+                   SPA_PARAM_Latency, 0, param)) < 0)
70
+       return res;
71
+
72
+   return 0;
73
+}
74
+
75
+static int recalc_tag(struct impl *this, struct spa_node *src, enum spa_direction direction,
76
+       uint32_t port_id, struct spa_node *dst)
77
+{
78
+   struct spa_pod_builder b = { 0 };
79
+   uint8_t buffer1024;
80
+   struct spa_pod *param;
81
+   uint32_t index = 0;
82
+   struct spa_tag_info info;
83
+   int res;
84
+
85
+   spa_log_debug(this->log, "%p: %d:%d", this, direction, port_id);
86
+
87
+   if (this->target == this->follower)
88
+       return 0;
89
+
90
+   while (true) {
91
+       void *state = NULL;
92
+       spa_pod_builder_init(&b, buffer, sizeof(buffer));
93
+       if ((res = spa_node_port_enum_params_sync(src,
94
+                       direction, port_id, SPA_PARAM_Tag,
95
+                       &index, NULL, &param, &b)) != 1) {
96
+           param = NULL;
97
+           break;
98
+       }
99
+       if ((res = spa_tag_parse(param, &info, &state)) < 0)
100
+           return res;
101
+       if (info.direction == direction)
102
+           break;
103
+   }
104
+   if ((res = spa_node_port_set_param(dst,
105
+                   SPA_DIRECTION_REVERSE(direction), 0,
106
+                   SPA_PARAM_Tag, 0, param)) < 0)
107
+       return res;
108
+
109
+   return 0;
110
+}
111
+
112
+
113
 static int reconfigure_mode(struct impl *this, bool passthrough,
114
                 enum spa_direction direction, struct spa_pod *format)
115
 {
116
@@ -590,6 +668,8 @@
117
 
118
    emit_node_info(this, false);
119
 
120
+   spa_log_debug(this->log, "%p: passthrough mode %d", this, passthrough);
121
+
122
    return 0;
123
 }
124
 
125
@@ -673,6 +753,8 @@
126
        if (this->target != this->follower) {
127
            if ((res = spa_node_set_param(this->target, id, flags, param)) < 0)
128
                return res;
129
+
130
+           res = recalc_latency(this, this->follower, this->direction, 0, this->convert);
131
        }
132
        break;
133
    }
134
@@ -953,6 +1035,61 @@
135
    emit_node_info(this, false);
136
 }
137
 
138
+static void follower_convert_port_info(void *data,
139
+       enum spa_direction direction, uint32_t port_id,
140
+       const struct spa_port_info *info)
141
+{
142
+   struct impl *this = data;
143
+   uint32_t i;
144
+   int res;
145
+
146
+   spa_log_info(this->log, "%p: convert port info %s %p %08"PRIx64, this,
147
+           this->direction == SPA_DIRECTION_INPUT ?
148
+               "Input" : "Output", info, info->change_mask);
149
+
150
+   if (info->change_mask & SPA_PORT_CHANGE_MASK_PARAMS) {
151
+       for (i = 0; i < info->n_params; i++) {
152
+           uint32_t idx;
153
+
154
+           switch (info->paramsi.id) {
155
+           case SPA_PARAM_Latency:
156
+               idx = IDX_Latency;
157
+               break;
158
+           case SPA_PARAM_Tag:
159
+               idx = IDX_Tag;
160
+               break;
161
+           default:
162
+               continue;
163
+           }
164
+
165
+           if (!this->add_listener &&
166
+               this->convert_params_flagsidx == info->paramsi.flags)
167
+               continue;
168
+
169
+           this->convert_params_flagsidx = info->paramsi.flags;
170
+
171
+           if (this->add_listener)
172
+               continue;
173
+
174
+           if (idx == IDX_Latency) {
175
+               this->in_recalc++;
176
+               res = recalc_latency(this, this->convert, direction, port_id, this->follower);
177
+               this->in_recalc--;
178
+               spa_log_debug(this->log, "latency: %d (%s)", res,
179
+                       spa_strerror(res));
180
+           }
181
+           if (idx == IDX_Tag) {
182
+               this->in_recalc++;
183
+               res = recalc_tag(this, this->convert, direction, port_id, this->follower);
184
+               this->in_recalc--;
185
+               spa_log_debug(this->log, "tag: %d (%s)", res,
186
+                       spa_strerror(res));
187
+           }
188
+           spa_log_debug(this->log, "param %d changed", info->paramsi.id);
189
+       }
190
+   }
191
+}
192
+
193
 static void convert_port_info(void *data,
194
        enum spa_direction direction, uint32_t port_id,
195
        const struct spa_port_info *info)
196
@@ -961,10 +1098,11 @@
197
    struct spa_port_info pi;
198
 
199
    if (direction != this->direction) {
200
-       /* skip the converter output port into the follower */
201
pipewire-0.3.79.tar.gz/spa/plugins/audioconvert/audioconvert.c -> pipewire-0.3.80.tar.gz/spa/plugins/audioconvert/audioconvert.c Changed
201
 
1
@@ -24,6 +24,7 @@
2
 #include <spa/param/audio/format-utils.h>
3
 #include <spa/param/param.h>
4
 #include <spa/param/latency-utils.h>
5
+#include <spa/param/tag-utils.h>
6
 #include <spa/pod/filter.h>
7
 #include <spa/pod/dynamic.h>
8
 #include <spa/debug/types.h>
9
@@ -138,13 +139,17 @@
10
 #define IDX_Format 3
11
 #define IDX_Buffers    4
12
 #define IDX_Latency    5
13
-#define N_PORT_PARAMS  6
14
+#define IDX_Tag        6
15
+#define N_PORT_PARAMS  7
16
    struct spa_param_info paramsN_PORT_PARAMS;
17
    char position16;
18
 
19
    struct buffer buffersMAX_BUFFERS;
20
    uint32_t n_buffers;
21
 
22
+   struct spa_latency_info latency2;
23
+   unsigned int have_latency:1;
24
+
25
    struct spa_audio_info format;
26
    unsigned int have_format:1;
27
    unsigned int is_dsp:1;
28
@@ -170,7 +175,7 @@
29
    struct spa_audio_info format;
30
    unsigned int have_format:1;
31
    unsigned int have_profile:1;
32
-   struct spa_latency_info latency;
33
+   struct spa_pod *tag;
34
 
35
    uint32_t remapMAX_PORTS;
36
 
37
@@ -323,6 +328,8 @@
38
    }
39
    port->direction = direction;
40
    port->id = port_id;
41
+   port->latencySPA_DIRECTION_INPUT = SPA_LATENCY_INFO(SPA_DIRECTION_INPUT);
42
+   port->latencySPA_DIRECTION_OUTPUT = SPA_LATENCY_INFO(SPA_DIRECTION_OUTPUT);
43
 
44
    name = spa_debug_type_find_short_name(spa_type_audio_channel, position);
45
    snprintf(port->position, sizeof(port->position), "%s", name ? name : "UNK");
46
@@ -339,6 +346,7 @@
47
    port->paramsIDX_Format = SPA_PARAM_INFO(SPA_PARAM_Format, SPA_PARAM_INFO_WRITE);
48
    port->paramsIDX_Buffers = SPA_PARAM_INFO(SPA_PARAM_Buffers, 0);
49
    port->paramsIDX_Latency = SPA_PARAM_INFO(SPA_PARAM_Latency, SPA_PARAM_INFO_READWRITE);
50
+   port->paramsIDX_Tag = SPA_PARAM_INFO(SPA_PARAM_Tag, SPA_PARAM_INFO_READWRITE);
51
    port->info.params = port->params;
52
    port->info.n_params = N_PORT_PARAMS;
53
 
54
@@ -996,7 +1004,7 @@
55
    spa_log_info(this->log, "generating ramp up sequence from %f to %f with a"
56
        " step value %f at scale %d", p->prev_volume, p->volume, volume_step, p->vrp.scale);
57
    do {
58
-       // spa_log_debug(this->log, "volume accum %f", get_volume_at_scale(this, volume_accum));
59
+       spa_log_trace(this->log, "volume accum %f", get_volume_at_scale(this, volume_accum));
60
        spa_pod_builder_control(&b.b, volume_offs, SPA_CONTROL_Properties);
61
        spa_pod_builder_add_object(&b.b,
62
                SPA_TYPE_OBJECT_Props, 0,
63
@@ -1025,7 +1033,7 @@
64
    spa_log_info(this->log, "generating ramp down sequence from %f to %f with a"
65
        " step value %f at scale %d", p->prev_volume, p->volume, volume_step, p->vrp.scale);
66
    do {
67
-       // spa_log_debug(this->log, "volume accum %f", get_volume_at_scale(this, volume_accum));
68
+       spa_log_trace(this->log, "volume accum %f", get_volume_at_scale(this, volume_accum));
69
        spa_pod_builder_control(&b.b, volume_offs, SPA_CONTROL_Properties);
70
        spa_pod_builder_add_object(&b.b,
71
                SPA_TYPE_OBJECT_Props, 0,
72
@@ -2101,7 +2109,23 @@
73
            uint32_t idx = result.index;
74
            if (port->is_monitor)
75
                idx = idx ^ 1;
76
-           param = spa_latency_build(&b, id, &this->diridx.latency);
77
+           param = spa_latency_build(&b, id, &port->latencyidx);
78
+           break;
79
+       }
80
+       default:
81
+           return 0;
82
+       }
83
+       break;
84
+   case SPA_PARAM_Tag:
85
+       switch (result.index) {
86
+       case 0: case 1:
87
+       {
88
+           uint32_t idx = result.index;
89
+           if (port->is_monitor)
90
+               idx = idx ^ 1;
91
+           param = this->diridx.tag;
92
+           if (param == NULL)
93
+               goto next;
94
            break;
95
        }
96
        default:
97
@@ -2142,33 +2166,111 @@
98
    struct impl *this = object;
99
    struct port *port, *oport;
100
    enum spa_direction other = SPA_DIRECTION_REVERSE(direction);
101
+   struct spa_latency_info info;
102
+   bool have_latency, emit = false;;
103
    uint32_t i;
104
 
105
-   spa_log_debug(this->log, "%p: set latency direction:%d id:%d",
106
-           this, direction, port_id);
107
+   spa_log_debug(this->log, "%p: set latency direction:%d id:%d %p",
108
+           this, direction, port_id, latency);
109
 
110
    port = GET_PORT(this, direction, port_id);
111
    if (port->is_monitor)
112
        return 0;
113
 
114
    if (latency == NULL) {
115
-       this->dirother.latency = SPA_LATENCY_INFO(other);
116
+       info = SPA_LATENCY_INFO(other);
117
+       have_latency = false;
118
    } else {
119
-       struct spa_latency_info info;
120
        if (spa_latency_parse(latency, &info) < 0 ||
121
            info.direction != other)
122
            return -EINVAL;
123
-       this->dirother.latency = info;
124
+       have_latency = true;
125
    }
126
+   emit = spa_latency_info_compare(&info, &port->latencyother) != 0 ||
127
+       port->have_latency == have_latency;
128
+
129
+   port->latencyother = info;
130
+   port->have_latency = have_latency;
131
+
132
+   spa_log_debug(this->log, "%p: set %s latency %f-%f %d-%d %"PRIu64"-%"PRIu64, this,
133
+           info.direction == SPA_DIRECTION_INPUT ? "input" : "output",
134
+           info.min_quantum, info.max_quantum,
135
+           info.min_rate, info.max_rate,
136
+           info.min_ns, info.max_ns);
137
+
138
+   spa_latency_info_combine_start(&info, other);
139
+   for (i = 0; i < this->dirdirection.n_ports; i++) {
140
+       oport = GET_PORT(this, direction, i);
141
+       if (oport->is_monitor || !oport->have_latency)
142
+           continue;
143
+       spa_log_debug(this->log, "%p: combine %d", this, i);
144
+       spa_latency_info_combine(&info, &oport->latencyother);
145
+   }
146
+   spa_latency_info_combine_finish(&info);
147
+
148
+   spa_log_debug(this->log, "%p: combined %s latency %f-%f %d-%d %"PRIu64"-%"PRIu64, this,
149
+           info.direction == SPA_DIRECTION_INPUT ? "input" : "output",
150
+           info.min_quantum, info.max_quantum,
151
+           info.min_rate, info.max_rate,
152
+           info.min_ns, info.max_ns);
153
 
154
    for (i = 0; i < this->dirother.n_ports; i++) {
155
        oport = GET_PORT(this, other, i);
156
-       oport->info.change_mask |= SPA_PORT_CHANGE_MASK_PARAMS;
157
-       oport->paramsIDX_Latency.user++;
158
-       emit_port_info(this, oport, false);
159
+
160
+       spa_log_debug(this->log, "%p: change %d", this, i);
161
+       if (spa_latency_info_compare(&info, &oport->latencyother) != 0) {
162
+           oport->latencyother = info;
163
+           oport->info.change_mask |= SPA_PORT_CHANGE_MASK_PARAMS;
164
+           oport->paramsIDX_Latency.user++;
165
+           emit_port_info(this, oport, false);
166
+       }
167
+   }
168
+   if (emit) {
169
+       port->info.change_mask |= SPA_PORT_CHANGE_MASK_PARAMS;
170
+       port->paramsIDX_Latency.user++;
171
+       emit_port_info(this, port, false);
172
+   }
173
+   return 0;
174
+}
175
+
176
+static int port_set_tag(void *object,
177
+              enum spa_direction direction,
178
+              uint32_t port_id,
179
+              uint32_t flags,
180
+              const struct spa_pod *tag)
181
+{
182
+   struct impl *this = object;
183
+   struct port *port, *oport;
184
+   enum spa_direction other = SPA_DIRECTION_REVERSE(direction);
185
+   uint32_t i;
186
+
187
+   spa_log_debug(this->log, "%p: set tag direction:%d id:%d %p",
188
+           this, direction, port_id, tag);
189
+
190
+   port = GET_PORT(this, direction, port_id);
191
+   if (port->is_monitor)
192
+       return 0;
193
+
194
+   if (tag != NULL) {
195
+       struct spa_tag_info info;
196
+       void *state = NULL;
197
+       if (spa_tag_parse(tag, &info, &state) < 0 ||
198
+           info.direction != other)
199
+           return -EINVAL;
200
+   }
201
pipewire-0.3.79.tar.gz/spa/plugins/audioconvert/meson.build -> pipewire-0.3.80.tar.gz/spa/plugins/audioconvert/meson.build Changed
33
 
1
@@ -7,6 +7,13 @@
2
 simd_cargs = 
3
 simd_dependencies = 
4
 
5
+opt_flags = 
6
+if host_machine.cpu_family() != 'alpha'
7
+  opt_flags += '-Ofast'
8
+else
9
+  opt_flags += '-O3'
10
+endif
11
+
12
 audioconvert_c = static_library('audioconvert_c',
13
    'channelmix-ops-c.c',
14
     'biquad.c',
15
@@ -15,7 +22,7 @@
16
     'peaks-ops-c.c',
17
     'resample-native-c.c',
18
     'fmt-ops-c.c' ,
19
-  c_args : '-Ofast', '-ffast-math',
20
+  c_args :  opt_flags ,
21
   dependencies :  spa_dep ,
22
   install : false
23
   )
24
@@ -27,7 +34,7 @@
25
       'volume-ops-sse.c',
26
       'peaks-ops-sse.c',
27
       'channelmix-ops-sse.c' ,
28
-    c_args : sse_args, '-Ofast', '-DHAVE_SSE',
29
+    c_args : sse_args, opt_flags, '-DHAVE_SSE',
30
     dependencies :  spa_dep ,
31
     install : false
32
     )
33
pipewire-0.3.79.tar.gz/spa/plugins/audiomixer/audiomixer.c -> pipewire-0.3.80.tar.gz/spa/plugins/audiomixer/audiomixer.c Changed
80
 
1
@@ -9,6 +9,7 @@
2
 #include <spa/support/plugin.h>
3
 #include <spa/support/log.h>
4
 #include <spa/support/cpu.h>
5
+#include <spa/support/loop.h>
6
 #include <spa/utils/list.h>
7
 #include <spa/utils/names.h>
8
 #include <spa/utils/string.h>
9
@@ -88,6 +89,9 @@
10
    struct spa_cpu *cpu;
11
    uint32_t cpu_flags;
12
    uint32_t max_align;
13
+
14
+   struct spa_loop *data_loop;
15
+
16
    uint32_t quantum_limit;
17
 
18
    struct mix_ops ops;
19
@@ -525,6 +529,8 @@
20
 
21
    port = GET_PORT(this, direction, port_id);
22
 
23
+   spa_return_val_if_fail(!this->started || port->io == NULL, -EIO);
24
+
25
    if (format == NULL) {
26
        if (port->have_format) {
27
            port->have_format = false;
28
@@ -632,6 +638,8 @@
29
 
30
    port = GET_PORT(this, direction, port_id);
31
 
32
+   spa_return_val_if_fail(!this->started || port->io == NULL, -EIO);
33
+
34
    clear_buffers(this, port);
35
 
36
    if (n_buffers > 0 && !port->have_format)
37
@@ -670,6 +678,19 @@
38
    return 0;
39
 }
40
 
41
+struct io_info {
42
+   struct port *port;
43
+   void *data;
44
+};
45
+
46
+static int do_port_set_io(struct spa_loop *loop, bool async, uint32_t seq,
47
+       const void *data, size_t size, void *user_data)
48
+{
49
+   struct io_info *info = user_data;
50
+   info->port->io = info->data;
51
+   return 0;
52
+}
53
+
54
 static int
55
 impl_node_port_set_io(void *object,
56
              enum spa_direction direction, uint32_t port_id,
57
@@ -677,6 +698,7 @@
58
 {
59
    struct impl *this = object;
60
    struct port *port;
61
+   struct io_info info;
62
 
63
    spa_return_val_if_fail(this != NULL, -EINVAL);
64
 
65
@@ -686,10 +708,13 @@
66
    spa_return_val_if_fail(CHECK_PORT(this, direction, port_id), -EINVAL);
67
 
68
    port = GET_PORT(this, direction, port_id);
69
+   info.port = port;
70
+   info.data = data;
71
 
72
    switch (id) {
73
    case SPA_IO_Buffers:
74
-       port->io = data;
75
+       spa_loop_invoke(this->data_loop,
76
+                               do_port_set_io, SPA_ID_INVALID, NULL, 0, true, &info);
77
        break;
78
    default:
79
        return -ENOENT;
80
pipewire-0.3.79.tar.gz/spa/plugins/audiomixer/mixer-dsp.c -> pipewire-0.3.80.tar.gz/spa/plugins/audiomixer/mixer-dsp.c Changed
92
 
1
@@ -9,6 +9,7 @@
2
 #include <spa/support/plugin.h>
3
 #include <spa/support/log.h>
4
 #include <spa/support/cpu.h>
5
+#include <spa/support/loop.h>
6
 #include <spa/utils/list.h>
7
 #include <spa/utils/names.h>
8
 #include <spa/utils/string.h>
9
@@ -85,6 +86,8 @@
10
    uint32_t cpu_flags;
11
    uint32_t max_align;
12
 
13
+   struct spa_loop *data_loop;
14
+
15
    uint32_t quantum_limit;
16
 
17
    struct mix_ops ops;
18
@@ -474,6 +477,8 @@
19
 
20
    port = GET_PORT(this, direction, port_id);
21
 
22
+   spa_return_val_if_fail(!this->started || port->io == NULL, -EIO);
23
+
24
    if (format == NULL) {
25
        if (port->have_format) {
26
            port->have_format = false;
27
@@ -569,6 +574,8 @@
28
 
29
    port = GET_PORT(this, direction, port_id);
30
 
31
+   spa_return_val_if_fail(!this->started || port->io == NULL, -EIO);
32
+
33
    clear_buffers(this, port);
34
 
35
    if (n_buffers > 0 && !port->have_format)
36
@@ -606,6 +613,19 @@
37
    return 0;
38
 }
39
 
40
+struct io_info {
41
+   struct port *port;
42
+   void *data;
43
+};
44
+
45
+static int do_port_set_io(struct spa_loop *loop, bool async, uint32_t seq,
46
+       const void *data, size_t size, void *user_data)
47
+{
48
+   struct io_info *info = user_data;
49
+   info->port->io = info->data;
50
+   return 0;
51
+}
52
+
53
 static int
54
 impl_node_port_set_io(void *object,
55
              enum spa_direction direction, uint32_t port_id,
56
@@ -613,6 +633,7 @@
57
 {
58
    struct impl *this = object;
59
    struct port *port;
60
+   struct io_info info;
61
 
62
    spa_return_val_if_fail(this != NULL, -EINVAL);
63
 
64
@@ -622,10 +643,13 @@
65
    spa_return_val_if_fail(CHECK_PORT(this, direction, port_id), -EINVAL);
66
 
67
    port = GET_PORT(this, direction, port_id);
68
+   info.port = port;
69
+   info.data = data;
70
 
71
    switch (id) {
72
    case SPA_IO_Buffers:
73
-       port->io = data;
74
+       spa_loop_invoke(this->data_loop,
75
+                               do_port_set_io, SPA_ID_INVALID, NULL, 0, true, &info);
76
        break;
77
    default:
78
        return -ENOENT;
79
@@ -833,6 +857,12 @@
80
    this->log = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_Log);
81
    spa_log_topic_init(this->log, log_topic);
82
 
83
+   this->data_loop = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_DataLoop);
84
+   if (this->data_loop == NULL) {
85
+       spa_log_error(this->log, "a data loop is needed");
86
+       return -EINVAL;
87
+   }
88
+
89
    this->cpu = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_CPU);
90
    if (this->cpu) {
91
        this->cpu_flags = spa_cpu_get_flags(this->cpu);
92
pipewire-0.3.79.tar.gz/spa/plugins/avb/avb-pcm-sink.c -> pipewire-0.3.80.tar.gz/spa/plugins/avb/avb-pcm-sink.c Changed
12
 
1
@@ -572,7 +572,9 @@
2
    case SPA_PARAM_Latency:
3
    {
4
        struct spa_latency_info info;
5
-       if ((res = spa_latency_parse(param, &info)) < 0)
6
+       if (param == NULL)
7
+           info = SPA_LATENCY_INFO(SPA_DIRECTION_REVERSE(direction));
8
+       else if ((res = spa_latency_parse(param, &info)) < 0)
9
            return res;
10
        if (direction == info.direction)
11
            return -EINVAL;
12
pipewire-0.3.79.tar.gz/spa/plugins/avb/avb-pcm-source.c -> pipewire-0.3.80.tar.gz/spa/plugins/avb/avb-pcm-source.c Changed
12
 
1
@@ -572,7 +572,9 @@
2
    case SPA_PARAM_Latency:
3
    {
4
        struct spa_latency_info info;
5
-       if ((res = spa_latency_parse(param, &info)) < 0)
6
+       if (param == NULL)
7
+           info = SPA_LATENCY_INFO(SPA_DIRECTION_REVERSE(direction));
8
+       else if ((res = spa_latency_parse(param, &info)) < 0)
9
            return res;
10
        if (direction == info.direction)
11
            return -EINVAL;
12
pipewire-0.3.79.tar.gz/spa/plugins/bluez5/bluez5-dbus.c -> pipewire-0.3.80.tar.gz/spa/plugins/bluez5/bluez5-dbus.c Changed
201
 
1
@@ -526,6 +526,26 @@
2
    }
3
 }
4
 
5
+static enum spa_bt_profile swap_profile(enum spa_bt_profile profile)
6
+{
7
+   switch (profile) {
8
+   case SPA_BT_PROFILE_A2DP_SOURCE:
9
+       return SPA_BT_PROFILE_A2DP_SINK;
10
+   case SPA_BT_PROFILE_A2DP_SINK:
11
+       return SPA_BT_PROFILE_A2DP_SOURCE;
12
+   case SPA_BT_PROFILE_BAP_SOURCE:
13
+       return SPA_BT_PROFILE_BAP_SINK;
14
+   case SPA_BT_PROFILE_BAP_SINK:
15
+       return SPA_BT_PROFILE_BAP_SOURCE;
16
+   case SPA_BT_PROFILE_BAP_BROADCAST_SOURCE:
17
+       return SPA_BT_PROFILE_BAP_BROADCAST_SINK;
18
+   case SPA_BT_PROFILE_BAP_BROADCAST_SINK:
19
+       return SPA_BT_PROFILE_BAP_BROADCAST_SOURCE;
20
+   default:
21
+       return SPA_BT_PROFILE_NULL;
22
+   }
23
+}
24
+
25
 static bool endpoint_should_be_registered(struct spa_bt_monitor *monitor,
26
                      const struct media_codec *codec,
27
                      enum spa_bt_media_direction direction)
28
@@ -2230,11 +2250,11 @@
29
    return device->adapter && device->address;
30
 }
31
 
32
-bool spa_bt_device_supports_media_codec(struct spa_bt_device *device, const struct media_codec *codec, bool sink)
33
+bool spa_bt_device_supports_media_codec(struct spa_bt_device *device, const struct media_codec *codec, enum spa_bt_profile profile)
34
 {
35
    struct spa_bt_monitor *monitor = device->monitor;
36
    struct spa_bt_remote_endpoint *ep;
37
-   enum spa_bt_profile codec_profile;
38
+   enum spa_bt_profile codec_target_profile;
39
    struct spa_bt_transport *t;
40
    const struct { enum spa_bluetooth_audio_codec codec; uint32_t mask; } quirks = {
41
        { SPA_BLUETOOTH_AUDIO_CODEC_SBC_XQ, SPA_BT_FEATURE_SBC_XQ },
42
@@ -2270,17 +2290,14 @@
43
            return false;
44
    }
45
 
46
+   for (i = 0, codec_target_profile = 0; i < (size_t)SPA_BT_MEDIA_DIRECTION_LAST; ++i)
47
+       if (codec_has_direction(codec, i))
48
+           codec_target_profile |= swap_profile(get_codec_profile(codec, i));
49
+
50
    spa_list_for_each(ep, &device->remote_endpoint_list, device_link) {
51
-       enum spa_bt_profile profile = spa_bt_profile_from_uuid(ep->uuid);
52
-       if (codec->bap) {
53
-           if ((profile == SPA_BT_PROFILE_BAP_BROADCAST_SINK) || (profile == SPA_BT_PROFILE_BAP_BROADCAST_SOURCE))
54
-               codec_profile = sink ? SPA_BT_PROFILE_BAP_BROADCAST_SINK : SPA_BT_PROFILE_BAP_BROADCAST_SOURCE;
55
-           else
56
-               codec_profile = sink ? SPA_BT_PROFILE_BAP_SINK : SPA_BT_PROFILE_BAP_SOURCE;
57
-       } else
58
-           codec_profile = sink ? SPA_BT_PROFILE_A2DP_SINK : SPA_BT_PROFILE_A2DP_SOURCE;
59
+       enum spa_bt_profile ep_profile = spa_bt_profile_from_uuid(ep->uuid);
60
 
61
-       if (profile != codec_profile)
62
+       if (!(ep_profile & codec_target_profile & profile))
63
            continue;
64
 
65
        if (media_codec_check_caps(codec, ep->codec, ep->capabilities, ep->capabilities_len,
66
@@ -2296,15 +2313,7 @@
67
     * can only know that the currently configured codec is supported.
68
     */
69
    spa_list_for_each(t, &device->transport_list, device_link) {
70
-       if (codec->bap) {
71
-           if((t->profile == SPA_BT_PROFILE_BAP_BROADCAST_SINK) || (t->profile == SPA_BT_PROFILE_BAP_BROADCAST_SOURCE))
72
-               codec_profile = sink ? SPA_BT_PROFILE_BAP_BROADCAST_SINK : SPA_BT_PROFILE_BAP_BROADCAST_SOURCE;
73
-           else
74
-               codec_profile = sink ? SPA_BT_PROFILE_BAP_SINK : SPA_BT_PROFILE_BAP_SOURCE;
75
-       } else
76
-           codec_profile = sink ? SPA_BT_PROFILE_A2DP_SINK : SPA_BT_PROFILE_A2DP_SOURCE;
77
-
78
-       if (t->profile != codec_profile)
79
+       if (!(t->profile & codec_target_profile & profile))
80
            continue;
81
 
82
        if (codec == t->media_codec)
83
@@ -2314,7 +2323,7 @@
84
    return false;
85
 }
86
 
87
-const struct media_codec **spa_bt_device_get_supported_media_codecs(struct spa_bt_device *device, size_t *count, bool sink)
88
+const struct media_codec **spa_bt_device_get_supported_media_codecs(struct spa_bt_device *device, size_t *count)
89
 {
90
    struct spa_bt_monitor *monitor = device->monitor;
91
    const struct media_codec * const * const media_codecs = monitor->media_codecs;
92
@@ -2329,7 +2338,7 @@
93
 
94
    j = 0;
95
    for (i = 0; media_codecsi != NULL; ++i) {
96
-       if (spa_bt_device_supports_media_codec(device, media_codecsi, sink)) {
97
+       if (spa_bt_device_supports_media_codec(device, media_codecsi, device->connected_profiles)) {
98
            supported_codecsj = media_codecsi;
99
            ++j;
100
        }
101
@@ -2373,10 +2382,17 @@
102
    return NULL;
103
 }
104
 
105
-static struct spa_bt_device* create_bcast_device(struct spa_bt_monitor *monitor,
106
-                                           const char *object_path)
107
+static struct spa_bt_device *create_bcast_device(struct spa_bt_monitor *monitor, const char *object_path)
108
 {  
109
    struct spa_bt_device *d;
110
+   struct spa_bt_adapter *adapter;
111
+
112
+   adapter = adapter_find(monitor, object_path);
113
+   if (adapter == NULL) {
114
+       spa_log_warn(monitor->log, "unknown adapter %s", object_path);
115
+       return NULL;
116
+   }
117
+
118
    d = device_create(monitor, object_path);
119
    if (d == NULL) {
120
        spa_log_warn(monitor->log, "can't create Bluetooth device %s: %m",
121
@@ -2384,22 +2400,12 @@
122
        return NULL;
123
    }
124
 
125
-   d->adapter = adapter_find(monitor, object_path);
126
-   if (d->adapter == NULL) {
127
-       spa_log_warn(monitor->log, "unknown adapter %s", d->adapter_path);
128
-   }
129
-   d->adapter_path = d->adapter->path;
130
-   d->alias = strdup("bcast_device");
131
-   d->name = strdup("bcast_device");
132
+   d->adapter = adapter;
133
+   d->adapter_path = strdup(adapter->path);
134
+   d->alias = strdup(adapter->alias);
135
+   d->name = strdup(adapter->name);
136
    d->address = strdup("00:00:00:00:00:00");
137
-   
138
-   spa_bt_device_check_profiles(d, false);
139
-   d->reconnect_state = BT_DEVICE_RECONNECT_INIT;
140
-
141
-   if (!device_props_ready(d))
142
-   {
143
-       return NULL;
144
-   }
145
+   d->reconnect_state = BT_DEVICE_RECONNECT_STOP;
146
 
147
    device_update_hw_volume_profiles(d);
148
 
149
@@ -2439,6 +2445,7 @@
150
            }
151
            else if (spa_streq(key, "Device")) {
152
                struct spa_bt_device *device;
153
+
154
                device = spa_bt_device_find(monitor, value);
155
                if (device == NULL) {
156
                    /*
157
@@ -2447,11 +2454,11 @@
158
                    * This is done because BlueZ sets the adapter as the device
159
                    * that is connected to for a broadcast sink endpoint/transport.
160
                    */
161
-                   if(spa_streq(remote_endpoint->uuid, SPA_BT_UUID_BAP_BROADCAST_SINK)) {
162
+                   if (spa_streq(remote_endpoint->uuid, SPA_BT_UUID_BAP_BROADCAST_SINK)) {
163
                        device = create_bcast_device(monitor, value);
164
-                       if(device == NULL) {
165
+                       if (device == NULL)
166
                            goto next;
167
-                       }
168
+
169
                        remote_endpoint->acceptor = true;
170
                        device_set_connected(device, 1);
171
                    } else {
172
@@ -3048,29 +3055,9 @@
173
            spa_log_debug(monitor->log, "transport %p: %s=%s", transport, key, value);
174
 
175
            if (spa_streq(key, "UUID")) {
176
-               switch (spa_bt_profile_from_uuid(value)) {
177
-               case SPA_BT_PROFILE_A2DP_SOURCE:
178
-                   transport->profile = SPA_BT_PROFILE_A2DP_SINK;
179
-                   break;
180
-               case SPA_BT_PROFILE_A2DP_SINK:
181
-                   transport->profile = SPA_BT_PROFILE_A2DP_SOURCE;
182
-                   break;
183
-               case SPA_BT_PROFILE_BAP_SOURCE:
184
-                   transport->profile = SPA_BT_PROFILE_BAP_SINK;
185
-                   break;
186
-               case SPA_BT_PROFILE_BAP_SINK:
187
-                   transport->profile = SPA_BT_PROFILE_BAP_SOURCE;
188
-                   break;
189
-               case SPA_BT_PROFILE_BAP_BROADCAST_SOURCE:
190
-                   transport->profile = SPA_BT_PROFILE_BAP_BROADCAST_SINK;
191
-                   break;
192
-               case SPA_BT_PROFILE_BAP_BROADCAST_SINK:
193
-                   transport->profile = SPA_BT_PROFILE_BAP_BROADCAST_SOURCE;
194
-                   break;
195
-               default:
196
+               transport->profile = swap_profile(spa_bt_profile_from_uuid(value));
197
+               if (transport->profile == SPA_BT_PROFILE_NULL)
198
                    spa_log_warn(monitor->log, "unknown profile %s", value);
199
-                   break;
200
-               }
201
pipewire-0.3.79.tar.gz/spa/plugins/bluez5/bluez5-device.c -> pipewire-0.3.80.tar.gz/spa/plugins/bluez5/bluez5-device.c Changed
127
 
1
@@ -190,10 +190,12 @@
2
    *codecs = NULL;
3
 }
4
 
5
-static const struct media_codec *get_supported_media_codec(struct impl *this, enum spa_bluetooth_audio_codec id, size_t *idx)
6
+static const struct media_codec *get_supported_media_codec(struct impl *this, enum spa_bluetooth_audio_codec id,
7
+       size_t *idx, enum spa_bt_profile profile)
8
 {
9
    const struct media_codec *media_codec = NULL;
10
    size_t i;
11
+
12
    for (i = 0; i < this->supported_codec_count; ++i) {
13
        if (this->supported_codecsi->id == id) {
14
            media_codec = this->supported_codecsi;
15
@@ -201,6 +203,13 @@
16
                *idx = i;
17
        }
18
    }
19
+
20
+   if (!media_codec)
21
+       return NULL;
22
+
23
+   if (!spa_bt_device_supports_media_codec(this->bt_dev, media_codec, profile))
24
+       return NULL;
25
+
26
    return media_codec;
27
 }
28
 
29
@@ -625,6 +634,8 @@
30
    char transport32, str_id32, object_path512;
31
    bool is_dyn_node = SPA_FLAG_IS_SET(id, DYNAMIC_NODE_ID_FLAG);
32
 
33
+   spa_log_debug(this->log, "node, transport:%p id:%08x factory:%s", t, id, factory_name);
34
+
35
    snprintf(transport, sizeof(transport), "pointer:%p", t);
36
    items0 = SPA_DICT_ITEM_INIT(SPA_KEY_API_BLUEZ5_TRANSPORT, transport);
37
    items1 = SPA_DICT_ITEM_INIT(SPA_KEY_API_BLUEZ5_PROFILE, spa_bt_profile_name(t->profile));
38
@@ -1016,9 +1027,6 @@
39
                }
40
            }
41
        }
42
-
43
-       if (get_supported_media_codec(this, this->props.codec, NULL) == NULL)
44
-           this->props.codec = 0;
45
        break;
46
    case DEVICE_PROFILE_BAP:
47
        if (this->bt_dev->connected_profiles & (SPA_BT_PROFILE_BAP_SOURCE)) {
48
@@ -1070,9 +1078,6 @@
49
                    DEVICE_ID_SOURCE, SPA_NAME_API_BLUEZ5_MEDIA_SOURCE, false);
50
            }
51
        }
52
-
53
-       if (get_supported_media_codec(this, this->props.codec, NULL) == NULL)
54
-           this->props.codec = 0;
55
        break;
56
    case DEVICE_PROFILE_HSP_HFP:
57
        if (this->bt_dev->connected_profiles & SPA_BT_PROFILE_HEADSET_HEAD_UNIT) {
58
@@ -1119,6 +1124,8 @@
59
 
60
 static void emit_remove_nodes(struct impl *this)
61
 {
62
+   spa_log_debug(this->log, "remove nodes");
63
+
64
    remove_dynamic_node (&this->dyn_media_source);
65
    remove_dynamic_node (&this->dyn_media_sink);
66
    remove_dynamic_node (&this->dyn_sco_source);
67
@@ -1278,11 +1285,9 @@
68
    if (this->switching_codec)
69
        return;
70
 
71
-   if (this->bt_dev->connected_profiles & SPA_BT_PROFILE_MEDIA_SINK) {
72
-       free(this->supported_codecs);
73
-       this->supported_codecs = spa_bt_device_get_supported_media_codecs(
74
-           this->bt_dev, &this->supported_codec_count, true);
75
-   }
76
+   free(this->supported_codecs);
77
+   this->supported_codecs = spa_bt_device_get_supported_media_codecs(
78
+       this->bt_dev, &this->supported_codec_count);
79
 
80
    switch (this->profile) {
81
    case DEVICE_PROFILE_OFF:
82
@@ -1297,8 +1302,6 @@
83
        break;
84
    case DEVICE_PROFILE_A2DP:
85
    case DEVICE_PROFILE_BAP:
86
-       if (get_supported_media_codec(this, this->props.codec, NULL) == NULL)
87
-           this->props.codec = 0;
88
        nodes_changed = (connected_change & (SPA_BT_PROFILE_MEDIA_SINK |
89
                             SPA_BT_PROFILE_MEDIA_SOURCE));
90
        spa_log_debug(this->log, "profiles changed: media nodes changed: %d",
91
@@ -1419,7 +1422,7 @@
92
        if (device->connected_profiles & SPA_BT_PROFILE_A2DP_SINK)
93
            have_output = true;
94
 
95
-       media_codec = get_supported_media_codec(this, codec, NULL);
96
+       media_codec = get_supported_media_codec(this, codec, NULL, device->connected_profiles);
97
        if (media_codec && media_codec->duplex_codec)
98
            have_input = true;
99
        break;
100
@@ -1533,7 +1536,7 @@
101
    if (this->supported_codecs)
102
        free(this->supported_codecs);
103
    this->supported_codecs = spa_bt_device_get_supported_media_codecs(
104
-       this->bt_dev, &this->supported_codec_count, true);
105
+       this->bt_dev, &this->supported_codec_count);
106
 
107
    /* Prefer BAP, then A2DP, then HFP, then null, but select AG if the device
108
       appears not to have BAP_SINK, A2DP_SINK or any HEAD_UNIT profile */
109
@@ -1625,7 +1628,7 @@
110
        n_sink++;
111
        if (codec) {
112
            size_t idx;
113
-           const struct media_codec *media_codec = get_supported_media_codec(this, codec, &idx);
114
+           const struct media_codec *media_codec = get_supported_media_codec(this, codec, &idx, profile);
115
            if (media_codec == NULL) {
116
                errno = EINVAL;
117
                return NULL;
118
@@ -1686,7 +1689,7 @@
119
        name = spa_bt_profile_name(profile);
120
 
121
        if (codec) {
122
-           media_codec = get_supported_media_codec(this, codec, &idx);
123
+           media_codec = get_supported_media_codec(this, codec, &idx, profile);
124
            if (media_codec == NULL) {
125
                errno = EINVAL;
126
                return NULL;
127
pipewire-0.3.79.tar.gz/spa/plugins/bluez5/defs.h -> pipewire-0.3.80.tar.gz/spa/plugins/bluez5/defs.h Changed
20
 
1
@@ -169,6 +169,7 @@
2
    SPA_BT_MEDIA_SINK,
3
    SPA_BT_MEDIA_SOURCE_BROADCAST,
4
    SPA_BT_MEDIA_SINK_BROADCAST,
5
+   SPA_BT_MEDIA_DIRECTION_LAST,
6
 };
7
 
8
 enum spa_bt_profile {
9
@@ -538,8 +539,8 @@
10
 int spa_bt_device_connect_profile(struct spa_bt_device *device, enum spa_bt_profile profile);
11
 int spa_bt_device_check_profiles(struct spa_bt_device *device, bool force);
12
 int spa_bt_device_ensure_media_codec(struct spa_bt_device *device, const struct media_codec * const *codecs);
13
-bool spa_bt_device_supports_media_codec(struct spa_bt_device *device, const struct media_codec *codec, bool sink);
14
-const struct media_codec **spa_bt_device_get_supported_media_codecs(struct spa_bt_device *device, size_t *count, bool sink);
15
+bool spa_bt_device_supports_media_codec(struct spa_bt_device *device, const struct media_codec *codec, enum spa_bt_profile profile);
16
+const struct media_codec **spa_bt_device_get_supported_media_codecs(struct spa_bt_device *device, size_t *count);
17
 int spa_bt_device_ensure_hfp_codec(struct spa_bt_device *device, unsigned int codec);
18
 int spa_bt_device_supports_hfp_codec(struct spa_bt_device *device, unsigned int codec);
19
 int spa_bt_device_release_transports(struct spa_bt_device *device);
20
pipewire-0.3.79.tar.gz/spa/plugins/bluez5/media-sink.c -> pipewire-0.3.80.tar.gz/spa/plugins/bluez5/media-sink.c Changed
21
 
1
@@ -1694,8 +1694,6 @@
2
 
3
    port->info.change_mask |= SPA_PORT_CHANGE_MASK_PARAMS;
4
    if (port->have_format) {
5
-       port->info.change_mask |= SPA_PORT_CHANGE_MASK_FLAGS;
6
-       port->info.flags = SPA_PORT_FLAG_LIVE;
7
        port->info.change_mask |= SPA_PORT_CHANGE_MASK_RATE;
8
        port->info.rate = SPA_FRACTION(1, port->current_format.info.raw.rate);
9
        port->paramsIDX_Format = SPA_PARAM_INFO(SPA_PARAM_Format, SPA_PARAM_INFO_READWRITE);
10
@@ -2078,7 +2076,9 @@
11
    port->info_all = SPA_PORT_CHANGE_MASK_FLAGS |
12
            SPA_PORT_CHANGE_MASK_PARAMS;
13
    port->info = SPA_PORT_INFO_INIT();
14
-   port->info.flags = 0;
15
+   port->info.flags = SPA_PORT_FLAG_LIVE |
16
+              SPA_PORT_FLAG_PHYSICAL |
17
+              SPA_PORT_FLAG_TERMINAL;
18
    port->paramsIDX_EnumFormat = SPA_PARAM_INFO(SPA_PARAM_EnumFormat, SPA_PARAM_INFO_READ);
19
    port->paramsIDX_Meta = SPA_PARAM_INFO(SPA_PARAM_Meta, SPA_PARAM_INFO_READ);
20
    port->paramsIDX_IO = SPA_PARAM_INFO(SPA_PARAM_IO, SPA_PARAM_INFO_READ);
21
pipewire-0.3.79.tar.gz/spa/plugins/bluez5/media-source.c -> pipewire-0.3.80.tar.gz/spa/plugins/bluez5/media-source.c Changed
18
 
1
@@ -1137,8 +1137,6 @@
2
 
3
    port->info.change_mask |= SPA_PORT_CHANGE_MASK_PARAMS;
4
    if (port->have_format) {
5
-       port->info.change_mask |= SPA_PORT_CHANGE_MASK_FLAGS;
6
-       port->info.flags = SPA_PORT_FLAG_LIVE;
7
        port->info.change_mask |= SPA_PORT_CHANGE_MASK_RATE;
8
        port->info.rate = SPA_FRACTION(1, port->current_format.info.raw.rate);
9
        port->paramsIDX_Format = SPA_PARAM_INFO(SPA_PARAM_Format, SPA_PARAM_INFO_READWRITE);
10
@@ -1669,6 +1667,7 @@
11
    port->info = SPA_PORT_INFO_INIT();
12
    port->info.change_mask = SPA_PORT_CHANGE_MASK_FLAGS;
13
    port->info.flags = SPA_PORT_FLAG_LIVE |
14
+              SPA_PORT_FLAG_PHYSICAL |
15
               SPA_PORT_FLAG_TERMINAL;
16
    port->paramsIDX_EnumFormat = SPA_PARAM_INFO(SPA_PARAM_EnumFormat, SPA_PARAM_INFO_READ);
17
    port->paramsIDX_Meta = SPA_PARAM_INFO(SPA_PARAM_Meta, SPA_PARAM_INFO_READ);
18
pipewire-0.3.79.tar.gz/spa/plugins/bluez5/midi-node.c -> pipewire-0.3.80.tar.gz/spa/plugins/bluez5/midi-node.c Changed
12
 
1
@@ -2033,7 +2033,9 @@
2
            SPA_PORT_CHANGE_MASK_PARAMS;
3
        port->info = SPA_PORT_INFO_INIT();
4
        port->info.change_mask = SPA_PORT_CHANGE_MASK_FLAGS;
5
-       port->info.flags = SPA_PORT_FLAG_LIVE;
6
+       port->info.flags = SPA_PORT_FLAG_LIVE |
7
+           SPA_PORT_FLAG_PHYSICAL |
8
+           SPA_PORT_FLAG_TERMINAL;
9
        port->paramsIDX_EnumFormat = SPA_PARAM_INFO(SPA_PARAM_EnumFormat, SPA_PARAM_INFO_READ);
10
        port->paramsIDX_Meta = SPA_PARAM_INFO(SPA_PARAM_Meta, SPA_PARAM_INFO_READ);
11
        port->paramsIDX_IO = SPA_PARAM_INFO(SPA_PARAM_IO, SPA_PARAM_INFO_READ);
12
pipewire-0.3.79.tar.gz/spa/plugins/bluez5/sco-sink.c -> pipewire-0.3.80.tar.gz/spa/plugins/bluez5/sco-sink.c Changed
21
 
1
@@ -1157,8 +1157,6 @@
2
 
3
    port->info.change_mask |= SPA_PORT_CHANGE_MASK_PARAMS;
4
    if (port->have_format) {
5
-       port->info.change_mask |= SPA_PORT_CHANGE_MASK_FLAGS;
6
-       port->info.flags = SPA_PORT_FLAG_LIVE;
7
        port->info.change_mask |= SPA_PORT_CHANGE_MASK_RATE;
8
        port->info.rate = SPA_FRACTION(1, port->current_format.info.raw.rate);
9
        port->paramsIDX_Format = SPA_PARAM_INFO(SPA_PARAM_Format, SPA_PARAM_INFO_READWRITE);
10
@@ -1499,7 +1497,9 @@
11
    port->info_all = SPA_PORT_CHANGE_MASK_FLAGS |
12
            SPA_PORT_CHANGE_MASK_PARAMS;
13
    port->info = SPA_PORT_INFO_INIT();
14
-   port->info.flags = 0;
15
+   port->info.flags = SPA_PORT_FLAG_LIVE |
16
+              SPA_PORT_FLAG_PHYSICAL |
17
+              SPA_PORT_FLAG_TERMINAL;
18
    port->paramsIDX_EnumFormat = SPA_PARAM_INFO(SPA_PARAM_EnumFormat, SPA_PARAM_INFO_READ);
19
    port->paramsIDX_Meta = SPA_PARAM_INFO(SPA_PARAM_Meta, SPA_PARAM_INFO_READ);
20
    port->paramsIDX_IO = SPA_PARAM_INFO(SPA_PARAM_IO, SPA_PARAM_INFO_READ);
21
pipewire-0.3.79.tar.gz/spa/plugins/bluez5/sco-source.c -> pipewire-0.3.80.tar.gz/spa/plugins/bluez5/sco-source.c Changed
18
 
1
@@ -1135,8 +1135,6 @@
2
 
3
    port->info.change_mask |= SPA_PORT_CHANGE_MASK_PARAMS;
4
    if (port->have_format) {
5
-       port->info.change_mask |= SPA_PORT_CHANGE_MASK_FLAGS;
6
-       port->info.flags = SPA_PORT_FLAG_LIVE;
7
        port->info.change_mask |= SPA_PORT_CHANGE_MASK_RATE;
8
        port->info.rate = SPA_FRACTION(1, port->current_format.info.raw.rate);
9
        port->paramsIDX_Format = SPA_PARAM_INFO(SPA_PARAM_Format, SPA_PARAM_INFO_READWRITE);
10
@@ -1601,6 +1599,7 @@
11
    port->info = SPA_PORT_INFO_INIT();
12
    port->info.change_mask = SPA_PORT_CHANGE_MASK_FLAGS;
13
    port->info.flags = SPA_PORT_FLAG_LIVE |
14
+              SPA_PORT_FLAG_PHYSICAL |
15
               SPA_PORT_FLAG_TERMINAL;
16
    port->paramsIDX_EnumFormat = SPA_PARAM_INFO(SPA_PARAM_EnumFormat, SPA_PARAM_INFO_READ);
17
    port->paramsIDX_Meta = SPA_PARAM_INFO(SPA_PARAM_Meta, SPA_PARAM_INFO_READ);
18
pipewire-0.3.79.tar.gz/spa/plugins/control/mixer.c -> pipewire-0.3.80.tar.gz/spa/plugins/control/mixer.c Changed
102
 
1
@@ -6,9 +6,10 @@
2
 #include <string.h>
3
 #include <stdio.h>
4
 
5
-#include <spa/support/plugin.h>
6
-#include <spa/support/log.h>
7
 #include <spa/support/cpu.h>
8
+#include <spa/support/log.h>
9
+#include <spa/support/loop.h>
10
+#include <spa/support/plugin.h>
11
 #include <spa/utils/list.h>
12
 #include <spa/utils/names.h>
13
 #include <spa/utils/string.h>
14
@@ -59,6 +60,8 @@
15
 
16
    struct spa_log *log;
17
 
18
+   struct spa_loop *data_loop;
19
+
20
    uint64_t info_all;
21
    struct spa_node_info info;
22
    struct spa_param_info params8;
23
@@ -412,6 +415,8 @@
24
 
25
    port = GET_PORT(this, direction, port_id);
26
 
27
+   spa_return_val_if_fail(!this->started || port->io == NULL, -EIO);
28
+
29
    if (format == NULL) {
30
        if (port->have_format) {
31
            port->have_format = false;
32
@@ -489,6 +494,8 @@
33
    spa_log_debug(this->log, NAME " %p: use buffers %d on port %d:%d",
34
            this, n_buffers, direction, port_id);
35
 
36
+   spa_return_val_if_fail(!this->started || port->io == NULL, -EIO);
37
+
38
    clear_buffers(this, port);
39
 
40
    if (n_buffers > 0 && !port->have_format)
41
@@ -517,6 +524,19 @@
42
    return 0;
43
 }
44
 
45
+struct io_info {
46
+   struct port *port;
47
+   void *data;
48
+};
49
+
50
+static int do_port_set_io(struct spa_loop *loop, bool async, uint32_t seq,
51
+       const void *data, size_t size, void *user_data)
52
+{
53
+   struct io_info *info = user_data;
54
+   info->port->io = info->data;
55
+   return 0;
56
+}
57
+
58
 static int
59
 impl_node_port_set_io(void *object,
60
              enum spa_direction direction, uint32_t port_id,
61
@@ -524,18 +544,23 @@
62
 {
63
    struct impl *this = object;
64
    struct port *port;
65
+   struct io_info info;
66
 
67
    spa_return_val_if_fail(this != NULL, -EINVAL);
68
-   spa_return_val_if_fail(CHECK_PORT(this, direction, port_id), -EINVAL);
69
-
70
-   port = GET_PORT(this, direction, port_id);
71
 
72
    spa_log_debug(this->log, NAME " %p: port %d:%d io %d %p/%zd", this,
73
            direction, port_id, id, data, size);
74
 
75
+   spa_return_val_if_fail(CHECK_PORT(this, direction, port_id), -EINVAL);
76
+
77
+   port = GET_PORT(this, direction, port_id);
78
+   info.port = port;
79
+   info.data = data;
80
+
81
    switch (id) {
82
    case SPA_IO_Buffers:
83
-       port->io = data;
84
+       spa_loop_invoke(this->data_loop,
85
+                               do_port_set_io, SPA_ID_INVALID, NULL, 0, true, &info);
86
        break;
87
    default:
88
        return -ENOENT;
89
@@ -793,6 +818,12 @@
90
 
91
    this->log = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_Log);
92
 
93
+   this->data_loop = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_DataLoop);
94
+   if (this->data_loop == NULL) {
95
+       spa_log_error(this->log, "a data loop is needed");
96
+       return -EINVAL;
97
+   }
98
+
99
    spa_hook_list_init(&this->hooks);
100
 
101
    this->node.iface = SPA_INTERFACE_INIT(
102
pipewire-0.3.79.tar.gz/spa/plugins/support/node-driver.c -> pipewire-0.3.80.tar.gz/spa/plugins/support/node-driver.c Changed
10
 
1
@@ -241,7 +241,7 @@
2
 
3
    if ((res = spa_system_timerfd_read(this->data_system,
4
                this->timer_source.fd, &expirations)) < 0) {
5
-       if (res != EAGAIN)
6
+       if (res != -EAGAIN)
7
            spa_log_error(this->log, NAME " %p: timerfd error: %s",
8
                    this, spa_strerror(res));
9
        return;
10
pipewire-0.3.79.tar.gz/spa/plugins/support/null-audio-sink.c -> pipewire-0.3.80.tar.gz/spa/plugins/support/null-audio-sink.c Changed
10
 
1
@@ -273,7 +273,7 @@
2
 
3
    if ((res = spa_system_timerfd_read(this->data_system,
4
                this->timer_source.fd, &expirations)) < 0) {
5
-       if (res != EAGAIN)
6
+       if (res != -EAGAIN)
7
            spa_log_error(this->log, NAME " %p: timerfd error: %s",
8
                    this, spa_strerror(res));
9
        return;
10
pipewire-0.3.79.tar.gz/spa/plugins/v4l2/v4l2-source.c -> pipewire-0.3.80.tar.gz/spa/plugins/v4l2/v4l2-source.c Changed
12
 
1
@@ -729,7 +729,9 @@
2
    case SPA_PARAM_Latency:
3
    {
4
        struct spa_latency_info info;
5
-       if ((res = spa_latency_parse(param, &info)) < 0)
6
+       if (param == NULL)
7
+           info = SPA_LATENCY_INFO(SPA_DIRECTION_REVERSE(direction));
8
+       else if ((res = spa_latency_parse(param, &info)) < 0)
9
            return res;
10
        if (direction == info.direction)
11
            return -EINVAL;
12
pipewire-0.3.79.tar.gz/spa/plugins/v4l2/v4l2-udev.c -> pipewire-0.3.80.tar.gz/spa/plugins/v4l2/v4l2-udev.c Changed
201
 
1
@@ -33,10 +33,9 @@
2
 #define ACTION_DISABLE 2
3
 
4
 struct device {
5
-   struct impl *impl;
6
    uint32_t id;
7
    struct udev_device *dev;
8
-   struct spa_source notify;
9
+   int inotify_wd;
10
    unsigned int accessible:1;
11
    unsigned int ignored:1;
12
    unsigned int emitted:1;
13
@@ -61,79 +60,91 @@
14
         uint32_t n_devices;
15
 
16
    struct spa_source source;
17
+   struct spa_source notify;
18
 };
19
 
20
-static int stop_inotify(struct device *dev);
21
-static int start_inotify(struct device *dev);
22
-
23
-static int impl_udev_open(struct impl *impl)
24
+static int impl_udev_open(struct impl *this)
25
 {
26
-   if (impl->udev == NULL) {
27
-       impl->udev = udev_new();
28
-       if (impl->udev == NULL)
29
+   if (this->udev == NULL) {
30
+       this->udev = udev_new();
31
+       if (this->udev == NULL)
32
            return -ENOMEM;
33
    }
34
    return 0;
35
 }
36
 
37
-static int impl_udev_close(struct impl *impl)
38
+static int impl_udev_close(struct impl *this)
39
 {
40
-   if (impl->udev != NULL)
41
-       udev_unref(impl->udev);
42
-   impl->udev = NULL;
43
+   if (this->udev != NULL)
44
+       udev_unref(this->udev);
45
+   this->udev = NULL;
46
    return 0;
47
 }
48
 
49
-static struct device *add_device(struct impl *impl, uint32_t id, struct udev_device *dev)
50
+static void start_watching_device(struct impl *this, struct device *device)
51
+{
52
+   if (this->notify.fd < 0 || device->inotify_wd >= 0)
53
+       return;
54
+
55
+   char path64;
56
+   snprintf(path, sizeof(path), "/dev/video%" PRIu32, device->id);
57
+
58
+   device->inotify_wd = inotify_add_watch(this->notify.fd, path, IN_ATTRIB);
59
+}
60
+
61
+static void stop_watching_device(struct impl *this, struct device *device)
62
+{
63
+   if (device->inotify_wd < 0)
64
+       return;
65
+
66
+   spa_assert(this->notify.fd >= 0);
67
+
68
+   inotify_rm_watch(this->notify.fd, device->inotify_wd);
69
+   device->inotify_wd = -1;
70
+}
71
+
72
+static struct device *add_device(struct impl *this, uint32_t id, struct udev_device *dev)
73
 {
74
    struct device *device;
75
 
76
-   if (impl->n_devices >= MAX_DEVICES)
77
+   if (this->n_devices >= MAX_DEVICES)
78
        return NULL;
79
-   device = &impl->devicesimpl->n_devices++;
80
+   device = &this->devicesthis->n_devices++;
81
    spa_zero(*device);
82
-   device->impl = impl;
83
-   device->notify.fd = -1;
84
    device->id = id;
85
    udev_device_ref(dev);
86
    device->dev = dev;
87
-   start_inotify(device);
88
+   device->inotify_wd = -1;
89
+
90
+   start_watching_device(this, device);
91
+
92
    return device;
93
 }
94
 
95
-static struct device *find_device(struct impl *impl, uint32_t id)
96
+static struct device *find_device(struct impl *this, uint32_t id)
97
 {
98
    uint32_t i;
99
-   for (i = 0; i < impl->n_devices; i++) {
100
-       if (impl->devicesi.id == id)
101
-           return &impl->devicesi;
102
+   for (i = 0; i < this->n_devices; i++) {
103
+       if (this->devicesi.id == id)
104
+           return &this->devicesi;
105
    }
106
    return NULL;
107
 }
108
 
109
-static void clear_device(struct device *device)
110
+static void remove_device(struct impl *this, struct device *device)
111
 {
112
-   stop_inotify(device);
113
-   if (device->dev)
114
-       udev_device_unref(device->dev);
115
+   device->dev = udev_device_unref(device->dev);
116
+   stop_watching_device(this, device);
117
+   *device = this->devices--this->n_devices;
118
 }
119
 
120
-static void remove_device(struct device *device)
121
+static void clear_devices(struct impl *this)
122
 {
123
-   struct impl *impl = device->impl;
124
-   clear_device(device);
125
-   *device = impl->devices--impl->n_devices;
126
+   while (this->n_devices > 0)
127
+       remove_device(this, &this->devices0);
128
 }
129
 
130
-static void clear_devices(struct impl *impl)
131
-{
132
-        uint32_t i;
133
-   for (i = 0; i < impl->n_devices; i++)
134
-       clear_device(&impl->devicesi);
135
-   impl->n_devices = 0;
136
-}
137
-
138
-static uint32_t get_device_id(struct impl *impl, struct udev_device *dev)
139
+static uint32_t get_device_id(struct impl *this, struct udev_device *dev)
140
 {
141
    const char *str;
142
 
143
@@ -229,9 +240,8 @@
144
    *d = 0;
145
 }
146
 
147
-static int emit_object_info(struct device *device)
148
+static int emit_object_info(struct impl *this, struct device *device)
149
 {
150
-   struct impl *impl = device->impl;
151
    struct spa_device_object_info info;
152
    uint32_t id = device->id;
153
    struct udev_device *dev = device->dev;
154
@@ -331,55 +341,54 @@
155
        itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_DEVICE_CAPABILITIES, str);
156
    }
157
         info.props = &SPA_DICT_INIT(items, n_items);
158
-        spa_device_emit_object_info(&impl->hooks, id, &info);
159
+        spa_device_emit_object_info(&this->hooks, id, &info);
160
    device->emitted = true;
161
 
162
    return 1;
163
 }
164
 
165
-static bool check_access(struct device *device)
166
+static bool check_access(struct impl *this, struct device *device)
167
 {
168
    char path128;
169
 
170
    snprintf(path, sizeof(path), "/dev/video%u", device->id);
171
    device->accessible = access(path, R_OK|W_OK) >= 0;
172
-   spa_log_debug(device->impl->log, "%s accessible:%u", path, device->accessible);
173
+   spa_log_debug(this->log, "%s accessible:%u", path, device->accessible);
174
 
175
    return device->accessible;
176
 }
177
 
178
-static void process_device(struct impl *impl, uint32_t action, struct udev_device *dev)
179
+static void process_device(struct impl *this, uint32_t action, struct udev_device *dev)
180
 {
181
    uint32_t id;
182
    struct device *device;
183
    bool emitted;
184
 
185
-   if ((id = get_device_id(impl, dev)) == SPA_ID_INVALID)
186
+   if ((id = get_device_id(this, dev)) == SPA_ID_INVALID)
187
        return;
188
 
189
-   device = find_device(impl, id);
190
+   device = find_device(this, id);
191
    if (device && device->ignored)
192
        return;
193
 
194
    switch (action) {
195
    case ACTION_ADD:
196
        if (device == NULL)
197
-           device = add_device(impl, id, dev);
198
+           device = add_device(this, id, dev);
199
        if (device == NULL)
200
            return;
201
pipewire-0.3.80.tar.gz/spa/plugins/vulkan/dmabuf.h Added
64
 
1
@@ -0,0 +1,62 @@
2
+// Copyright (c) 2023 The wlroots contributors
3
+//
4
+// Permission is hereby granted, free of charge, to any person obtaining a copy of
5
+// this software and associated documentation files (the "Software"), to deal in
6
+// the Software without restriction, including without limitation the rights to
7
+// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
8
+// of the Software, and to permit persons to whom the Software is furnished to do
9
+// so, subject to the following conditions:
10
+//
11
+// The above copyright notice and this permission notice shall be included in all
12
+// copies or substantial portions of the Software.
13
+//
14
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+// SOFTWARE.
21
+//
22
+// Obtained from https://gitlab.freedesktop.org/wlroots/wlroots/
23
+
24
+/* SPDX-FileCopyrightText: Copyright © 2023 The wlroots contributors */
25
+/* SPDX-License-Identifier: MIT */
26
+
27
+#ifndef RENDER_DMABUF_H
28
+#define RENDER_DMABUF_H
29
+
30
+#include <stdbool.h>
31
+#include <stdint.h>
32
+
33
+#include "spa/support/log.h"
34
+
35
+// Copied from <linux/dma-buf.h> to avoid #ifdef soup
36
+#define DMA_BUF_SYNC_READ      (1 << 0)
37
+#define DMA_BUF_SYNC_WRITE     (2 << 0)
38
+#define DMA_BUF_SYNC_RW        (DMA_BUF_SYNC_READ | DMA_BUF_SYNC_WRITE)
39
+
40
+/**
41
+ * Check whether DMA-BUF import/export from/to sync_file is available.
42
+ *
43
+ * If this function returns true, dmabuf_import_sync_file() is supported.
44
+ */
45
+bool dmabuf_check_sync_file_import_export(struct spa_log *log);
46
+
47
+/**
48
+ * Import a sync_file into a DMA-BUF with DMA_BUF_IOCTL_IMPORT_SYNC_FILE.
49
+ *
50
+ * This can be used to make explicit sync interoperate with implicit sync.
51
+ */
52
+bool dmabuf_import_sync_file(struct spa_log *log, int dmabuf_fd, uint32_t flags, int sync_file_fd);
53
+
54
+/**
55
+ * Export a sync_file from a DMA-BUF with DMA_BUF_IOCTL_EXPORT_SYNC_FILE.
56
+ *
57
+ * The sync_file FD is returned on success, -1 is returned on error.
58
+ *
59
+ * This can be used to make explicit sync interoperate with implicit sync.
60
+ */
61
+int dmabuf_export_sync_file(struct spa_log *log, int dmabuf_fd, uint32_t flags);
62
+
63
+#endif
64
pipewire-0.3.80.tar.gz/spa/plugins/vulkan/dmabuf_fallback.c Added
44
 
1
@@ -0,0 +1,42 @@
2
+// Copyright (c) 2023 The wlroots contributors
3
+//
4
+// Permission is hereby granted, free of charge, to any person obtaining a copy of
5
+// this software and associated documentation files (the "Software"), to deal in
6
+// the Software without restriction, including without limitation the rights to
7
+// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
8
+// of the Software, and to permit persons to whom the Software is furnished to do
9
+// so, subject to the following conditions:
10
+//
11
+// The above copyright notice and this permission notice shall be included in all
12
+// copies or substantial portions of the Software.
13
+//
14
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+// SOFTWARE.
21
+//
22
+// Obtained from https://gitlab.freedesktop.org/wlroots/wlroots/
23
+
24
+/* SPDX-FileCopyrightText: Copyright © 2023 The wlroots contributors */
25
+/* SPDX-License-Identifier: MIT */
26
+
27
+#include <spa/support/log.h>
28
+#include <spa/utils/result.h>
29
+
30
+
31
+bool dmabuf_check_sync_file_import_export(struct spa_log *log) {
32
+   return false;
33
+}
34
+
35
+bool dmabuf_import_sync_file(struct spa_log *log, int dmabuf_fd, uint32_t flags, int sync_file_fd) {
36
+   spa_log_error("DMA-BUF sync_file import IOCTL not available on this system");
37
+   return false;
38
+}
39
+
40
+int dmabuf_export_sync_file(struct spa_log *log, int dmabuf_fd, uint32_t flags) {
41
+   spa_log_error("DMA-BUF sync_file export IOCTL not available on this system");
42
+   return false;
43
+}
44
pipewire-0.3.80.tar.gz/spa/plugins/vulkan/dmabuf_linux.c Added
129
 
1
@@ -0,0 +1,127 @@
2
+// Copyright (c) 2023 The wlroots contributors
3
+//
4
+// Permission is hereby granted, free of charge, to any person obtaining a copy of
5
+// this software and associated documentation files (the "Software"), to deal in
6
+// the Software without restriction, including without limitation the rights to
7
+// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
8
+// of the Software, and to permit persons to whom the Software is furnished to do
9
+// so, subject to the following conditions:
10
+//
11
+// The above copyright notice and this permission notice shall be included in all
12
+// copies or substantial portions of the Software.
13
+//
14
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+// SOFTWARE.
21
+//
22
+// Obtained from https://gitlab.freedesktop.org/wlroots/wlroots/
23
+
24
+/* SPDX-FileCopyrightText: Copyright © 2023 The wlroots contributors */
25
+/* SPDX-License-Identifier: MIT */
26
+
27
+#include <linux/dma-buf.h>
28
+#include <linux/version.h>
29
+#include <stdlib.h>
30
+#include <sys/utsname.h>
31
+#include <xf86drm.h>
32
+#include <errno.h>
33
+
34
+#include <spa/support/log.h>
35
+#include <spa/utils/result.h>
36
+
37
+#include "dmabuf.h"
38
+
39
+bool dmabuf_check_sync_file_import_export(struct spa_log *log) {
40
+   /* Unfortunately there's no better way to check the availability of the
41
+    * IOCTL than to check the kernel version. See the discussion at:
42
+    * https://lore.kernel.org/dri-devel/20220601161303.64797-1-contact@emersion.fr/
43
+    */
44
+
45
+   struct utsname utsname = {0};
46
+   if (uname(&utsname) != 0) {
47
+       spa_log_warn(log, "uname failed");
48
+       return false;
49
+   }
50
+
51
+   if (strcmp(utsname.sysname, "Linux") != 0) {
52
+       return false;
53
+   }
54
+
55
+   // Trim release suffix if any, e.g. "-arch1-1"
56
+   for (size_t i = 0; utsname.releasei != '\0'; i++) {
57
+       char ch = utsname.releasei;
58
+       if ((ch < '0' || ch > '9') && ch != '.') {
59
+           utsname.releasei = '\0';
60
+           break;
61
+       }
62
+   }
63
+
64
+   char *rel = strtok(utsname.release, ".");
65
+   int major = atoi(rel);
66
+
67
+   int minor = 0;
68
+   rel = strtok(NULL, ".");
69
+   if (rel != NULL) {
70
+       minor = atoi(rel);
71
+   }
72
+
73
+   int patch = 0;
74
+   rel = strtok(NULL, ".");
75
+   if (rel != NULL) {
76
+       patch = atoi(rel);
77
+   }
78
+
79
+   return KERNEL_VERSION(major, minor, patch) >= KERNEL_VERSION(5, 20, 0);
80
+}
81
+
82
+// TODO: drop these definitions once widespread
83
+
84
+#if !defined(DMA_BUF_IOCTL_IMPORT_SYNC_FILE)
85
+
86
+struct dma_buf_import_sync_file {
87
+   __u32 flags;
88
+   __s32 fd;
89
+};
90
+
91
+#define DMA_BUF_IOCTL_IMPORT_SYNC_FILE _IOW(DMA_BUF_BASE, 3, struct dma_buf_import_sync_file)
92
+
93
+#endif
94
+
95
+#if !defined(DMA_BUF_IOCTL_EXPORT_SYNC_FILE)
96
+
97
+struct dma_buf_export_sync_file {
98
+   __u32 flags;
99
+   __s32 fd;
100
+};
101
+
102
+#define DMA_BUF_IOCTL_EXPORT_SYNC_FILE _IOWR(DMA_BUF_BASE, 2, struct dma_buf_export_sync_file)
103
+
104
+#endif
105
+
106
+bool dmabuf_import_sync_file(struct spa_log *log, int dmabuf_fd, uint32_t flags, int sync_file_fd) {
107
+   struct dma_buf_import_sync_file data = {
108
+       .flags = flags,
109
+       .fd = sync_file_fd,
110
+   };
111
+   if (drmIoctl(dmabuf_fd, DMA_BUF_IOCTL_IMPORT_SYNC_FILE, &data) != 0) {
112
+       spa_log_error(log, "drmIoctl(IMPORT_SYNC_FILE) failed with %d (%s)", errno, spa_strerror(-errno));
113
+       return false;
114
+   }
115
+   return true;
116
+}
117
+
118
+int dmabuf_export_sync_file(struct spa_log *log, int dmabuf_fd, uint32_t flags) {
119
+   struct dma_buf_export_sync_file data = {
120
+       .flags = flags,
121
+       .fd = -1,
122
+   };
123
+   if (drmIoctl(dmabuf_fd, DMA_BUF_IOCTL_EXPORT_SYNC_FILE, &data) != 0) {
124
+       spa_log_error(log, "drmIoctl(EXPORT_SYNC_FILE) failed with %d (%s)", errno, spa_strerror(-errno));
125
+       return -1;
126
+   }
127
+   return data.fd;
128
+}
129
pipewire-0.3.79.tar.gz/spa/plugins/vulkan/meson.build -> pipewire-0.3.80.tar.gz/spa/plugins/vulkan/meson.build Changed
23
 
1
@@ -2,11 +2,20 @@
2
   'plugin.c',
3
   'vulkan-compute-filter.c',
4
   'vulkan-compute-source.c',
5
+  'vulkan-compute-utils.c',
6
   'vulkan-utils.c'
7
 
8
 
9
+drm = dependency('libdrm')
10
+
11
+if cc.has_header('linux/dma-buf.h') and target_machine.system() == 'linux'
12
+  spa_vulkan_sources += files('dmabuf_linux.c')
13
+else
14
+  spa_vulkan_sources += files('dmabuf_fallback.c')
15
+endif
16
+
17
 spa_vulkan = shared_library('spa-vulkan',
18
   spa_vulkan_sources,
19
-  dependencies :  spa_dep, vulkan_dep, mathlib ,
20
+  dependencies :  spa_dep, vulkan_dep, mathlib, drm ,
21
   install : true,
22
   install_dir : spa_plugindir / 'vulkan')
23
pipewire-0.3.79.tar.gz/spa/plugins/vulkan/vulkan-compute-filter.c -> pipewire-0.3.80.tar.gz/spa/plugins/vulkan/vulkan-compute-filter.c Changed
201
 
1
@@ -23,7 +23,7 @@
2
 #include <spa/param/param.h>
3
 #include <spa/pod/filter.h>
4
 
5
-#include "vulkan-utils.h"
6
+#include "vulkan-compute-utils.h"
7
 
8
 #define NAME "vulkan-compute-filter"
9
 
10
@@ -73,7 +73,7 @@
11
 
12
    bool started;
13
 
14
-   struct vulkan_state state;
15
+   struct vulkan_compute_state state;
16
    struct port port2;
17
 };
18
 
19
@@ -269,6 +269,64 @@
20
    return -ENOTSUP;
21
 }
22
 
23
+static struct spa_pod *build_EnumFormat(uint32_t fmt, const struct vulkan_format_info *fmtInfo, struct spa_pod_builder *builder) {
24
+   struct spa_pod_frame f2;
25
+   uint32_t i, c;
26
+
27
+   spa_pod_builder_push_object(builder, &f0, SPA_TYPE_OBJECT_Format, SPA_PARAM_EnumFormat);
28
+   spa_pod_builder_add(builder, SPA_FORMAT_mediaType, SPA_POD_Id(SPA_MEDIA_TYPE_video), 0);
29
+   spa_pod_builder_add(builder, SPA_FORMAT_mediaSubtype, SPA_POD_Id(SPA_MEDIA_SUBTYPE_dsp), 0);
30
+   spa_pod_builder_add(builder, SPA_FORMAT_VIDEO_format, SPA_POD_Id(fmt), 0);
31
+   if (fmtInfo && fmtInfo->modifierCount > 0) {
32
+       spa_pod_builder_prop(builder, SPA_FORMAT_VIDEO_modifier, SPA_POD_PROP_FLAG_MANDATORY | SPA_POD_PROP_FLAG_DONT_FIXATE);
33
+       spa_pod_builder_push_choice(builder, &f1, SPA_CHOICE_Enum, 0);
34
+       for (i = 0, c = 0; i < fmtInfo->modifierCount; i++) {
35
+           spa_pod_builder_long(builder, fmtInfo->infosi.props.drmFormatModifier);
36
+           if (c++ == 0)
37
+               spa_pod_builder_long(builder, fmtInfo->infosi.props.drmFormatModifier);
38
+       }
39
+       spa_pod_builder_pop(builder, &f1);
40
+   }
41
+   return spa_pod_builder_pop(builder, &f0);
42
+}
43
+
44
+// This function enumerates the available formats in vulkan_state::formats, announcing all formats capable to support DmaBufs
45
+// first and then falling back to those supported with SHM buffers.
46
+static bool find_EnumFormatInfo(struct vulkan_base *s, uint32_t index, uint32_t caps, uint32_t *fmt_idx, bool *has_modifier) {
47
+   int64_t fmtIterator = 0;
48
+   int64_t maxIterator = 0;
49
+   if (caps & VULKAN_BUFFER_TYPE_CAP_SHM)
50
+       maxIterator += s->formatInfoCount;
51
+   if (caps & VULKAN_BUFFER_TYPE_CAP_DMABUF)
52
+       maxIterator += s->formatInfoCount;
53
+   // Count available formats until index underflows, while fmtIterator indexes the current format.
54
+   // Iterate twice over formats first time with modifiers, second time without if both caps are supported.
55
+   while (index < (uint32_t)-1 && fmtIterator < maxIterator) {
56
+       const struct vulkan_format_info *f_info = &s->formatInfosfmtIterator%s->formatInfoCount;
57
+       if (caps & VULKAN_BUFFER_TYPE_CAP_DMABUF && fmtIterator < s->formatInfoCount) {
58
+           // First round, check for modifiers
59
+           if (f_info->modifierCount > 0) {
60
+               index--;
61
+           }
62
+       } else if (caps & VULKAN_BUFFER_TYPE_CAP_SHM) {
63
+           // Second round, every format should be supported.
64
+           index--;
65
+       }
66
+       fmtIterator++;
67
+   }
68
+
69
+   if (index != (uint32_t)-1) {
70
+       // No more formats available
71
+       return false;
72
+   }
73
+   // Undo end of loop increment
74
+   fmtIterator--;
75
+   *fmt_idx = fmtIterator%s->formatInfoCount;
76
+   // Loop finished in first round
77
+   *has_modifier = caps & VULKAN_BUFFER_TYPE_CAP_DMABUF && fmtIterator < s->formatInfoCount;
78
+   return true;
79
+}
80
+
81
 static int port_enum_formats(void *object,
82
                 enum spa_direction direction, uint32_t port_id,
83
                 uint32_t index,
84
@@ -276,17 +334,30 @@
85
                 struct spa_pod **param,
86
                 struct spa_pod_builder *builder)
87
 {
88
-   switch (index) {
89
-   case 0:
90
-       *param = spa_pod_builder_add_object(builder,
91
-           SPA_TYPE_OBJECT_Format, SPA_PARAM_EnumFormat,
92
-           SPA_FORMAT_mediaType,       SPA_POD_Id(SPA_MEDIA_TYPE_video),
93
-           SPA_FORMAT_mediaSubtype,    SPA_POD_Id(SPA_MEDIA_SUBTYPE_dsp),
94
-           SPA_FORMAT_VIDEO_format,    SPA_POD_Id(SPA_VIDEO_FORMAT_DSP_F32));
95
-       break;
96
-   default:
97
-       return 0;
98
+   struct impl *this = object;
99
+
100
+   uint32_t fmt_index;
101
+   bool has_modifier;
102
+   if (this->portport_id.have_format
103
+           && this->portport_id.current_format.info.dsp.flags & SPA_VIDEO_FLAG_MODIFIER
104
+           && this->portport_id.current_format.info.dsp.flags ^ SPA_VIDEO_FLAG_MODIFIER_FIXATION_REQUIRED) {
105
+       if (index == 0) {
106
+           spa_log_info(this->log, "vulkan-compute-filter: enum_formats fixated format idx: %d, format %d, has_modifier 1",
107
+                   index, this->portport_id.current_format.info.dsp.format);
108
+           *param = spa_format_video_dsp_build(builder, SPA_PARAM_EnumFormat, &this->portport_id.current_format.info.dsp);
109
+           return 1;
110
+       }
111
+       if (!find_EnumFormatInfo(&this->state.base, index-1, spa_vulkan_get_buffer_caps(&this->state, direction), &fmt_index, &has_modifier))
112
+           return 0;
113
+   } else {
114
+       if (!find_EnumFormatInfo(&this->state.base, index, spa_vulkan_get_buffer_caps(&this->state, direction), &fmt_index, &has_modifier))
115
+           return 0;
116
    }
117
+
118
+   const struct vulkan_format_info *f_info = &this->state.base.formatInfosfmt_index;
119
+   spa_log_info(this->log, "vulkan-compute-filter: enum_formats idx: %d, format %d, has_modifier %d", index, f_info->spa_format, has_modifier);
120
+   *param = build_EnumFormat(f_info->spa_format, has_modifier ? f_info : NULL, builder);
121
+
122
    return 1;
123
 }
124
 
125
@@ -348,13 +419,25 @@
126
                this->position->video.size.height,
127
                this->position->video.stride);
128
 
129
-       param = spa_pod_builder_add_object(&b,
130
-           SPA_TYPE_OBJECT_ParamBuffers, id,
131
-           SPA_PARAM_BUFFERS_buffers, SPA_POD_CHOICE_RANGE_Int(2, 1, MAX_BUFFERS),
132
-           SPA_PARAM_BUFFERS_blocks,  SPA_POD_Int(1),
133
-           SPA_PARAM_BUFFERS_size,    SPA_POD_Int(this->position->video.stride *
134
-                               this->position->video.size.height),
135
-           SPA_PARAM_BUFFERS_stride,  SPA_POD_Int(this->position->video.stride));
136
+       if (port->current_format.info.dsp.flags & SPA_VIDEO_FLAG_MODIFIER) {
137
+           struct vulkan_modifier_info *mod_info = spa_vulkan_get_modifier_info(&this->state,
138
+               &port->current_format.info.dsp);
139
+           param = spa_pod_builder_add_object(&b,
140
+               SPA_TYPE_OBJECT_ParamBuffers, id,
141
+               SPA_PARAM_BUFFERS_buffers, SPA_POD_CHOICE_RANGE_Int(2, 1, MAX_BUFFERS),
142
+               SPA_PARAM_BUFFERS_blocks,  SPA_POD_Int(mod_info->props.drmFormatModifierPlaneCount),
143
+               SPA_PARAM_BUFFERS_dataType, SPA_POD_CHOICE_FLAGS_Int(1<<SPA_DATA_DmaBuf));
144
+       } else {
145
+           param = spa_pod_builder_add_object(&b,
146
+               SPA_TYPE_OBJECT_ParamBuffers, id,
147
+               SPA_PARAM_BUFFERS_buffers, SPA_POD_CHOICE_RANGE_Int(2, 1, MAX_BUFFERS),
148
+               SPA_PARAM_BUFFERS_blocks,  SPA_POD_Int(1),
149
+               SPA_PARAM_BUFFERS_size,  SPA_POD_Int(this->position->video.stride *
150
+                   this->position->video.size.height),
151
+               SPA_PARAM_BUFFERS_stride,  SPA_POD_Int(this->position->video.stride),
152
+               SPA_PARAM_BUFFERS_dataType, SPA_POD_CHOICE_FLAGS_Int(1<<SPA_DATA_MemPtr));
153
+       }
154
+
155
        break;
156
    }
157
    case SPA_PARAM_Meta:
158
@@ -390,7 +473,7 @@
159
    if (port->n_buffers > 0) {
160
        spa_log_debug(this->log, NAME " %p: clear buffers", this);
161
        spa_vulkan_stop(&this->state);
162
-       spa_vulkan_use_buffers(&this->state, &this->state.streamsport->stream_id, 0, 0, NULL);
163
+       spa_vulkan_use_buffers(&this->state, &this->state.streamsport->stream_id, 0, &port->current_format.info.dsp, 0, NULL);
164
        port->n_buffers = 0;
165
        spa_list_init(&port->empty);
166
        spa_list_init(&port->ready);
167
@@ -428,8 +511,49 @@
168
        this->state.constants.width = this->position->video.size.width;
169
        this->state.constants.height = this->position->video.size.height;
170
 
171
+       bool modifier_fixed = false;
172
+       if (port->direction == SPA_DIRECTION_OUTPUT
173
+               && info.info.dsp.flags & SPA_VIDEO_FLAG_MODIFIER
174
+               && info.info.dsp.flags & SPA_VIDEO_FLAG_MODIFIER_FIXATION_REQUIRED) {
175
+           const struct spa_pod_prop *mod_prop;
176
+           if ((mod_prop = spa_pod_find_prop(format, NULL, SPA_FORMAT_VIDEO_modifier)) == NULL)
177
+               return -EINVAL;
178
+
179
+           const struct spa_pod *mod_pod = &mod_prop->value;
180
+           uint32_t modifierCount = SPA_POD_CHOICE_N_VALUES(mod_pod);
181
+           uint64_t *modifiers = SPA_POD_CHOICE_VALUES(mod_pod);
182
+           if (modifierCount <= 1)
183
+               return -EINVAL;
184
+           // SPA_POD_CHOICE carries the "preferred" value at position 0
185
+           modifierCount -= 1;
186
+           modifiers++;
187
+           uint64_t fixed_modifier;
188
+           if (spa_vulkan_fixate_modifier(&this->state, &this->state.streamsport->stream_id, &info.info.dsp, modifierCount, modifiers, &fixed_modifier) != 0)
189
+               return -EINVAL;
190
+
191
+           spa_log_info(this->log, NAME ": modifier fixated %"PRIu64, fixed_modifier);
192
+
193
+           info.info.dsp.modifier = fixed_modifier;
194
+           info.info.dsp.flags &= ~SPA_VIDEO_FLAG_MODIFIER_FIXATION_REQUIRED;
195
+           modifier_fixed = true;
196
+       }
197
+
198
+       if (info.info.dsp.flags & SPA_VIDEO_FLAG_MODIFIER) {
199
+           port->info.flags |= SPA_PORT_FLAG_CAN_ALLOC_BUFFERS;
200
+       } else {
201
pipewire-0.3.79.tar.gz/spa/plugins/vulkan/vulkan-compute-source.c -> pipewire-0.3.80.tar.gz/spa/plugins/vulkan/vulkan-compute-source.c Changed
201
 
1
@@ -24,7 +24,7 @@
2
 #include <spa/param/param.h>
3
 #include <spa/pod/filter.h>
4
 
5
-#include "vulkan-utils.h"
6
+#include "vulkan-compute-utils.h"
7
 
8
 #define NAME "vulkan-compute-source"
9
 
10
@@ -97,7 +97,7 @@
11
 
12
    uint64_t frame_count;
13
 
14
-   struct vulkan_state state;
15
+   struct vulkan_compute_state state;
16
    struct port port;
17
 };
18
 
19
@@ -503,6 +503,64 @@
20
    return -ENOTSUP;
21
 }
22
 
23
+static struct spa_pod *build_EnumFormat(uint32_t fmt, const struct vulkan_format_info *fmtInfo, struct spa_pod_builder *builder) {
24
+   struct spa_pod_frame f2;
25
+   uint32_t i, c;
26
+
27
+   spa_pod_builder_push_object(builder, &f0, SPA_TYPE_OBJECT_Format, SPA_PARAM_EnumFormat);
28
+   spa_pod_builder_add(builder, SPA_FORMAT_mediaType, SPA_POD_Id(SPA_MEDIA_TYPE_video), 0);
29
+   spa_pod_builder_add(builder, SPA_FORMAT_mediaSubtype, SPA_POD_Id(SPA_MEDIA_SUBTYPE_dsp), 0);
30
+   spa_pod_builder_add(builder, SPA_FORMAT_VIDEO_format, SPA_POD_Id(fmt), 0);
31
+   if (fmtInfo && fmtInfo->modifierCount > 0) {
32
+       spa_pod_builder_prop(builder, SPA_FORMAT_VIDEO_modifier, SPA_POD_PROP_FLAG_MANDATORY | SPA_POD_PROP_FLAG_DONT_FIXATE);
33
+       spa_pod_builder_push_choice(builder, &f1, SPA_CHOICE_Enum, 0);
34
+       for (i = 0, c = 0; i < fmtInfo->modifierCount; i++) {
35
+           spa_pod_builder_long(builder, fmtInfo->infosi.props.drmFormatModifier);
36
+           if (c++ == 0)
37
+               spa_pod_builder_long(builder, fmtInfo->infosi.props.drmFormatModifier);
38
+       }
39
+       spa_pod_builder_pop(builder, &f1);
40
+   }
41
+   return spa_pod_builder_pop(builder, &f0);
42
+}
43
+
44
+// This function enumerates the available formats in vulkan_state::formats, announcing all formats capable to support DmaBufs
45
+// first and then falling back to those supported with SHM buffers.
46
+static bool find_EnumFormatInfo(struct vulkan_base *s, uint32_t index, uint32_t caps, uint32_t *fmt_idx, bool *has_modifier) {
47
+   int64_t fmtIterator = 0;
48
+   int64_t maxIterator = 0;
49
+   if (caps & VULKAN_BUFFER_TYPE_CAP_SHM)
50
+       maxIterator += s->formatInfoCount;
51
+   if (caps & VULKAN_BUFFER_TYPE_CAP_DMABUF)
52
+       maxIterator += s->formatInfoCount;
53
+   // Count available formats until index underflows, while fmtIterator indexes the current format.
54
+   // Iterate twice over formats first time with modifiers, second time without if both caps are supported.
55
+   while (index < (uint32_t)-1 && fmtIterator < maxIterator) {
56
+       const struct vulkan_format_info *f_info = &s->formatInfosfmtIterator%s->formatInfoCount;
57
+       if (caps & VULKAN_BUFFER_TYPE_CAP_DMABUF && fmtIterator < s->formatInfoCount) {
58
+           // First round, check for modifiers
59
+           if (f_info->modifierCount > 0) {
60
+               index--;
61
+           }
62
+       } else if (caps & VULKAN_BUFFER_TYPE_CAP_SHM) {
63
+           // Second round, every format should be supported.
64
+           index--;
65
+       }
66
+       fmtIterator++;
67
+   }
68
+
69
+   if (index != (uint32_t)-1) {
70
+       // No more formats available
71
+       return false;
72
+   }
73
+   // Undo end of loop increment
74
+   fmtIterator--;
75
+   *fmt_idx = fmtIterator%s->formatInfoCount;
76
+   // Loop finished in first round
77
+   *has_modifier = caps & VULKAN_BUFFER_TYPE_CAP_DMABUF && fmtIterator < s->formatInfoCount;
78
+   return true;
79
+}
80
+
81
 static int port_enum_formats(void *object,
82
                 enum spa_direction direction, uint32_t port_id,
83
                 uint32_t index,
84
@@ -510,17 +568,30 @@
85
                 struct spa_pod **param,
86
                 struct spa_pod_builder *builder)
87
 {
88
-   switch (index) {
89
-   case 0:
90
-       *param = spa_pod_builder_add_object(builder,
91
-           SPA_TYPE_OBJECT_Format, SPA_PARAM_EnumFormat,
92
-           SPA_FORMAT_mediaType,       SPA_POD_Id(SPA_MEDIA_TYPE_video),
93
-           SPA_FORMAT_mediaSubtype,    SPA_POD_Id(SPA_MEDIA_SUBTYPE_dsp),
94
-           SPA_FORMAT_VIDEO_format,    SPA_POD_Id(SPA_VIDEO_FORMAT_DSP_F32));
95
-       break;
96
-   default:
97
-       return 0;
98
+   struct impl *this = object;
99
+
100
+   uint32_t fmt_index;
101
+   bool has_modifier;
102
+   if (this->port.have_format
103
+           && this->port.current_format.info.dsp.flags & SPA_VIDEO_FLAG_MODIFIER
104
+           && this->port.current_format.info.dsp.flags ^ SPA_VIDEO_FLAG_MODIFIER_FIXATION_REQUIRED) {
105
+       if (index == 0) {
106
+           spa_log_info(this->log, "vulkan-compute-source: enum_formats fixated format idx: %d, format %d, has_modifier 1",
107
+                   index, this->port.current_format.info.dsp.format);
108
+           *param = spa_format_video_dsp_build(builder, SPA_PARAM_EnumFormat, &this->port.current_format.info.dsp);
109
+           return 1;
110
+       }
111
+       if (!find_EnumFormatInfo(&this->state.base, index-1, spa_vulkan_get_buffer_caps(&this->state, direction), &fmt_index, &has_modifier))
112
+           return 0;
113
+   } else {
114
+       if (!find_EnumFormatInfo(&this->state.base, index, spa_vulkan_get_buffer_caps(&this->state, direction), &fmt_index, &has_modifier))
115
+           return 0;
116
    }
117
+
118
+   const struct vulkan_format_info *f_info = &this->state.base.formatInfosfmt_index;
119
+   spa_log_info(this->log, "vulkan-compute-source: enum_formats idx: %d, format %d, has_modifier %d", index, f_info->spa_format, has_modifier);
120
+   *param = build_EnumFormat(f_info->spa_format, has_modifier ? f_info : NULL, builder);
121
+
122
    return 1;
123
 }
124
 
125
@@ -582,13 +653,26 @@
126
                this->position->video.size.height,
127
                this->position->video.stride);
128
 
129
-       param = spa_pod_builder_add_object(&b,
130
-           SPA_TYPE_OBJECT_ParamBuffers, id,
131
-           SPA_PARAM_BUFFERS_buffers, SPA_POD_CHOICE_RANGE_Int(2, 1, MAX_BUFFERS),
132
-           SPA_PARAM_BUFFERS_blocks,  SPA_POD_Int(1),
133
-           SPA_PARAM_BUFFERS_size,    SPA_POD_Int(this->position->video.stride *
134
-                               this->position->video.size.height),
135
-           SPA_PARAM_BUFFERS_stride,  SPA_POD_Int(this->position->video.stride));
136
+
137
+       if (port->current_format.info.dsp.flags & SPA_VIDEO_FLAG_MODIFIER) {
138
+           struct vulkan_modifier_info *mod_info = spa_vulkan_get_modifier_info(&this->state,
139
+               &port->current_format.info.dsp);
140
+           param = spa_pod_builder_add_object(&b,
141
+               SPA_TYPE_OBJECT_ParamBuffers, id,
142
+               SPA_PARAM_BUFFERS_buffers, SPA_POD_CHOICE_RANGE_Int(2, 1, MAX_BUFFERS),
143
+               SPA_PARAM_BUFFERS_blocks,  SPA_POD_Int(mod_info->props.drmFormatModifierPlaneCount),
144
+               SPA_PARAM_BUFFERS_dataType, SPA_POD_CHOICE_FLAGS_Int(1<<SPA_DATA_DmaBuf));
145
+       } else {
146
+           param = spa_pod_builder_add_object(&b,
147
+               SPA_TYPE_OBJECT_ParamBuffers, id,
148
+               SPA_PARAM_BUFFERS_buffers, SPA_POD_CHOICE_RANGE_Int(2, 1, MAX_BUFFERS),
149
+               SPA_PARAM_BUFFERS_blocks,  SPA_POD_Int(1),
150
+               SPA_PARAM_BUFFERS_size,    SPA_POD_Int(this->position->video.stride *
151
+                   this->position->video.size.height),
152
+               SPA_PARAM_BUFFERS_stride,  SPA_POD_Int(this->position->video.stride),
153
+               SPA_PARAM_BUFFERS_dataType, SPA_POD_CHOICE_FLAGS_Int(1<<SPA_DATA_MemPtr));
154
+       }
155
+
156
        break;
157
    }
158
    case SPA_PARAM_Meta:
159
@@ -623,7 +707,7 @@
160
 {
161
    if (port->n_buffers > 0) {
162
        spa_log_debug(this->log, NAME " %p: clear buffers", this);
163
-       spa_vulkan_use_buffers(&this->state, &this->state.streams0, 0, 0, NULL);
164
+       spa_vulkan_use_buffers(&this->state, &this->state.streams0, 0, &port->current_format.info.dsp, 0, NULL);
165
        port->n_buffers = 0;
166
        spa_list_init(&port->empty);
167
        spa_list_init(&port->ready);
168
@@ -662,9 +746,50 @@
169
        this->state.constants.width = this->position->video.size.width;
170
        this->state.constants.height = this->position->video.size.height;
171
 
172
+       bool modifier_fixed = false;
173
+       if (info.info.dsp.flags & SPA_VIDEO_FLAG_MODIFIER
174
+               && info.info.dsp.flags & SPA_VIDEO_FLAG_MODIFIER_FIXATION_REQUIRED) {
175
+           const struct spa_pod_prop *mod_prop;
176
+           if ((mod_prop = spa_pod_find_prop(format, NULL, SPA_FORMAT_VIDEO_modifier)) == NULL)
177
+               return -EINVAL;
178
+
179
+           const struct spa_pod *mod_pod = &mod_prop->value;
180
+           uint32_t modifierCount = SPA_POD_CHOICE_N_VALUES(mod_pod);
181
+           uint64_t *modifiers = SPA_POD_CHOICE_VALUES(mod_pod);
182
+           if (modifierCount <= 1)
183
+               return -EINVAL;
184
+           // SPA_POD_CHOICE carries the "preferred" value at position 0
185
+           modifierCount -= 1;
186
+           modifiers++;
187
+
188
+           uint64_t fixed_modifier;
189
+           if (spa_vulkan_fixate_modifier(&this->state, &this->state.streams0, &info.info.dsp, modifierCount, modifiers, &fixed_modifier) != 0)
190
+               return -EINVAL;
191
+
192
+           spa_log_info(this->log, NAME ": modifier fixated %"PRIu64, fixed_modifier);
193
+
194
+           info.info.dsp.modifier = fixed_modifier;
195
+           info.info.dsp.flags &= ~SPA_VIDEO_FLAG_MODIFIER_FIXATION_REQUIRED;
196
+           modifier_fixed = true;
197
+       }
198
+
199
+       if (info.info.dsp.flags & SPA_VIDEO_FLAG_MODIFIER) {
200
+           port->info.flags |= SPA_PORT_FLAG_CAN_ALLOC_BUFFERS;
201
pipewire-0.3.80.tar.gz/spa/plugins/vulkan/vulkan-compute-utils.c Added
201
 
1
@@ -0,0 +1,675 @@
2
+/* Spa */
3
+/* SPDX-FileCopyrightText: Copyright © 2019 Wim Taymans */
4
+/* SPDX-License-Identifier: MIT */
5
+
6
+#include <vulkan/vulkan.h>
7
+
8
+#include <unistd.h>
9
+#include <sys/types.h>
10
+#include <sys/stat.h>
11
+#include <sys/mman.h>
12
+#include <fcntl.h>
13
+#include <string.h>
14
+#include <vulkan/vulkan_core.h>
15
+#if !defined(__FreeBSD__) && !defined(__MidnightBSD__)
16
+#include <alloca.h>
17
+#endif
18
+#include <errno.h>
19
+#include <stdio.h>
20
+#include <assert.h>
21
+#include <math.h>
22
+#include <time.h>
23
+
24
+#include <spa/utils/result.h>
25
+#include <spa/utils/string.h>
26
+#include <spa/support/log.h>
27
+#include <spa/debug/mem.h>
28
+
29
+#include "vulkan-compute-utils.h"
30
+#include "vulkan-utils.h"
31
+
32
+#define VULKAN_INSTANCE_FUNCTION(name)                     \
33
+   PFN_##name name = (PFN_##name)vkGetInstanceProcAddr(s->base.instance, #name)
34
+
35
+static int createFence(struct vulkan_compute_state *s) {
36
+   VkFenceCreateInfo createInfo = {
37
+       .sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO,
38
+       .flags = 0,
39
+   };
40
+   VK_CHECK_RESULT(vkCreateFence(s->base.device, &createInfo, NULL, &s->fence));
41
+
42
+   return 0;
43
+};
44
+
45
+static int createDescriptors(struct vulkan_compute_state *s)
46
+{
47
+   uint32_t i;
48
+
49
+   VkDescriptorPoolSize descriptorPoolSizes2 = {
50
+       {
51
+           .type = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE,
52
+           .descriptorCount = 1,
53
+       },
54
+       {
55
+           .type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
56
+           .descriptorCount = s->n_streams - 1,
57
+       },
58
+   };
59
+   const VkDescriptorPoolCreateInfo descriptorPoolCreateInfo = {
60
+       .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO,
61
+       .maxSets = s->n_streams,
62
+       .poolSizeCount = s->n_streams > 1 ? 2 : 1,
63
+       .pPoolSizes = descriptorPoolSizes,
64
+   };
65
+
66
+        VK_CHECK_RESULT(vkCreateDescriptorPool(s->base.device,
67
+               &descriptorPoolCreateInfo, NULL,
68
+               &s->descriptorPool));
69
+
70
+   VkDescriptorSetLayoutBinding descriptorSetLayoutBindings->n_streams;
71
+   descriptorSetLayoutBinding0 = (VkDescriptorSetLayoutBinding) {
72
+       .binding = 0,
73
+       .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE,
74
+       .descriptorCount = 1,
75
+       .stageFlags = VK_SHADER_STAGE_COMPUTE_BIT
76
+   };
77
+   for (i = 1; i < s->n_streams; i++) {
78
+       descriptorSetLayoutBindingi = (VkDescriptorSetLayoutBinding) {
79
+           .binding = i,
80
+           .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
81
+           .descriptorCount = 1,
82
+           .stageFlags = VK_SHADER_STAGE_COMPUTE_BIT
83
+       };
84
+   };
85
+   const VkDescriptorSetLayoutCreateInfo descriptorSetLayoutCreateInfo = {
86
+       .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
87
+       .bindingCount = s->n_streams,
88
+       .pBindings = descriptorSetLayoutBinding
89
+   };
90
+   VK_CHECK_RESULT(vkCreateDescriptorSetLayout(s->base.device,
91
+               &descriptorSetLayoutCreateInfo, NULL,
92
+               &s->descriptorSetLayout));
93
+
94
+   const VkDescriptorSetAllocateInfo descriptorSetAllocateInfo = {
95
+       .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO,
96
+       .descriptorPool = s->descriptorPool,
97
+       .descriptorSetCount = 1,
98
+       .pSetLayouts = &s->descriptorSetLayout
99
+   };
100
+
101
+   VK_CHECK_RESULT(vkAllocateDescriptorSets(s->base.device,
102
+               &descriptorSetAllocateInfo,
103
+               &s->descriptorSet));
104
+
105
+   const VkSamplerCreateInfo samplerInfo = {
106
+       .sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
107
+       .magFilter = VK_FILTER_LINEAR,
108
+       .minFilter = VK_FILTER_LINEAR,
109
+       .mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR,
110
+       .addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,
111
+       .addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,
112
+       .addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,
113
+       .borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_BLACK,
114
+       .unnormalizedCoordinates = VK_FALSE,
115
+       .compareEnable = VK_FALSE,
116
+       .compareOp = VK_COMPARE_OP_ALWAYS,
117
+       .mipLodBias = 0.0f,
118
+       .minLod = 0,
119
+       .maxLod = 5,
120
+   };
121
+   VK_CHECK_RESULT(vkCreateSampler(s->base.device, &samplerInfo, NULL, &s->sampler));
122
+
123
+   return 0;
124
+}
125
+
126
+static int updateDescriptors(struct vulkan_compute_state *s)
127
+{
128
+   uint32_t i;
129
+   VkDescriptorImageInfo descriptorImageInfos->n_streams;
130
+   VkWriteDescriptorSet writeDescriptorSets->n_streams;
131
+   uint32_t descriptorSetLen = 0;
132
+
133
+   for (i = 0; i < s->n_streams; i++) {
134
+       struct vulkan_stream *p = &s->streamsi;
135
+
136
+       if (p->current_buffer_id == p->pending_buffer_id ||
137
+           p->pending_buffer_id == SPA_ID_INVALID)
138
+           continue;
139
+
140
+       p->current_buffer_id = p->pending_buffer_id;
141
+       p->busy_buffer_id = p->current_buffer_id;
142
+       p->pending_buffer_id = SPA_ID_INVALID;
143
+
144
+       descriptorImageInfodescriptorSetLen = (VkDescriptorImageInfo) {
145
+           .sampler = s->sampler,
146
+           .imageView = p->buffersp->current_buffer_id.view,
147
+           .imageLayout = VK_IMAGE_LAYOUT_GENERAL,
148
+       };
149
+       writeDescriptorSetdescriptorSetLen = (VkWriteDescriptorSet) {
150
+           .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
151
+           .dstSet = s->descriptorSet,
152
+           .dstBinding = i,
153
+           .descriptorCount = 1,
154
+           .descriptorType = i == 0 ?
155
+               VK_DESCRIPTOR_TYPE_STORAGE_IMAGE :
156
+               VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
157
+           .pImageInfo = &descriptorImageInfoi,
158
+       };
159
+       descriptorSetLen++;
160
+   }
161
+   vkUpdateDescriptorSets(s->base.device, descriptorSetLen,
162
+               writeDescriptorSet, 0, NULL);
163
+
164
+   return 0;
165
+}
166
+
167
+static VkShaderModule createShaderModule(struct vulkan_compute_state *s, const char* shaderFile)
168
+{
169
+   VkShaderModule shaderModule = VK_NULL_HANDLE;
170
+   VkResult result;
171
+   void *data;
172
+   int fd;
173
+   struct stat stat;
174
+
175
+   if ((fd = open(shaderFile, 0, O_RDONLY)) == -1) {
176
+       spa_log_error(s->log, "can't open %s: %m", shaderFile);
177
+       return VK_NULL_HANDLE;
178
+   }
179
+   if (fstat(fd, &stat) < 0) {
180
+       spa_log_error(s->log, "can't stat %s: %m", shaderFile);
181
+       close(fd);
182
+       return VK_NULL_HANDLE;
183
+   }
184
+
185
+   data = mmap(NULL, stat.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
186
+
187
+   const VkShaderModuleCreateInfo shaderModuleCreateInfo = {
188
+       .sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO,
189
+       .codeSize = stat.st_size,
190
+       .pCode = data,
191
+   };
192
+   result = vkCreateShaderModule(s->base.device,
193
+           &shaderModuleCreateInfo, 0, &shaderModule);
194
+
195
+   munmap(data, stat.st_size);
196
+   close(fd);
197
+
198
+   if (result != VK_SUCCESS) {
199
+       spa_log_error(s->log, "can't create shader %s: %m", shaderFile);
200
+       return VK_NULL_HANDLE;
201
pipewire-0.3.80.tar.gz/spa/plugins/vulkan/vulkan-compute-utils.h Added
82
 
1
@@ -0,0 +1,80 @@
2
+/* Spa */
3
+/* SPDX-FileCopyrightText: Copyright © 2019 Wim Taymans */
4
+/* SPDX-License-Identifier: MIT */
5
+
6
+#include <vulkan/vulkan.h>
7
+
8
+#include <spa/buffer/buffer.h>
9
+#include <spa/param/video/format.h>
10
+#include <spa/node/node.h>
11
+
12
+#include "vulkan-utils.h"
13
+
14
+#define MAX_STREAMS 2
15
+#define WORKGROUP_SIZE 32
16
+
17
+struct pixel {
18
+   float r, g, b, a;
19
+};
20
+
21
+struct push_constants {
22
+       float time;
23
+       int frame;
24
+       int width;
25
+       int height;
26
+};
27
+
28
+
29
+struct vulkan_compute_state {
30
+   struct spa_log *log;
31
+
32
+   struct push_constants constants;
33
+
34
+   struct vulkan_base base;
35
+
36
+   VkPipeline pipeline;
37
+   VkPipelineLayout pipelineLayout;
38
+   const char *shaderName;
39
+   VkShaderModule computeShaderModule;
40
+
41
+   VkCommandPool commandPool;
42
+   VkCommandBuffer commandBuffer;
43
+
44
+   VkFence fence;
45
+   VkSemaphore pipelineSemaphore;
46
+   unsigned int initialized:1;
47
+   unsigned int prepared:1;
48
+   unsigned int started:1;
49
+
50
+   VkDescriptorPool descriptorPool;
51
+   VkDescriptorSetLayout descriptorSetLayout;
52
+
53
+   VkSampler sampler;
54
+
55
+   uint32_t n_streams;
56
+   VkDescriptorSet descriptorSet;
57
+   struct vulkan_stream streamsMAX_STREAMS;
58
+};
59
+
60
+int spa_vulkan_init_stream(struct vulkan_compute_state *s, struct vulkan_stream *stream, enum spa_direction,
61
+       struct spa_dict *props);
62
+
63
+int spa_vulkan_fixate_modifier(struct vulkan_compute_state *s, struct vulkan_stream *p, struct spa_video_info_dsp *dsp_info,
64
+       uint32_t modifierCount, uint64_t *modifiers, uint64_t *modifier);
65
+int spa_vulkan_prepare(struct vulkan_compute_state *s);
66
+int spa_vulkan_use_buffers(struct vulkan_compute_state *s, struct vulkan_stream *stream, uint32_t flags,
67
+       struct spa_video_info_dsp *dsp_info, uint32_t n_buffers, struct spa_buffer **buffers);
68
+int spa_vulkan_unprepare(struct vulkan_compute_state *s);
69
+
70
+int spa_vulkan_start(struct vulkan_compute_state *s);
71
+int spa_vulkan_stop(struct vulkan_compute_state *s);
72
+int spa_vulkan_ready(struct vulkan_compute_state *s);
73
+int spa_vulkan_process(struct vulkan_compute_state *s);
74
+int spa_vulkan_cleanup(struct vulkan_compute_state *s);
75
+
76
+int spa_vulkan_get_buffer_caps(struct vulkan_compute_state *s, enum spa_direction direction);
77
+struct vulkan_modifier_info *spa_vulkan_get_modifier_info(struct vulkan_compute_state *s,
78
+       struct spa_video_info_dsp *dsp_info);
79
+
80
+int spa_vulkan_init(struct vulkan_compute_state *s);
81
+void spa_vulkan_deinit(struct vulkan_compute_state *s);
82
pipewire-0.3.80.tar.gz/spa/plugins/vulkan/vulkan-types.h Added
77
 
1
@@ -0,0 +1,75 @@
2
+#pragma once
3
+
4
+#include <vulkan/vulkan.h>
5
+
6
+#include <spa/buffer/buffer.h>
7
+#include <spa/node/node.h>
8
+
9
+#define MAX_BUFFERS 16
10
+#define DMABUF_MAX_PLANES 1
11
+
12
+enum buffer_type_caps {
13
+   VULKAN_BUFFER_TYPE_CAP_SHM = 1<<0,
14
+   VULKAN_BUFFER_TYPE_CAP_DMABUF = 1<<1,
15
+};
16
+
17
+struct vulkan_modifier_info {
18
+   VkDrmFormatModifierPropertiesEXT props;
19
+   VkExtent2D max_extent;
20
+};
21
+
22
+struct vulkan_format_info {
23
+   uint32_t spa_format;
24
+   VkFormat vk_format;
25
+   uint32_t modifierCount;
26
+   struct vulkan_modifier_info *infos;
27
+};
28
+
29
+struct vulkan_buffer {
30
+   int fd;
31
+   VkImage image;
32
+   VkImageView view;
33
+   VkDeviceMemory memory;
34
+   VkSemaphore foreign_semaphore;
35
+};
36
+
37
+struct vulkan_stream {
38
+   enum spa_direction direction;
39
+
40
+   uint32_t pending_buffer_id;
41
+   uint32_t current_buffer_id;
42
+   uint32_t busy_buffer_id;
43
+   uint32_t ready_buffer_id;
44
+
45
+   struct vulkan_buffer buffersMAX_BUFFERS;
46
+   struct spa_buffer *spa_buffersMAX_BUFFERS;
47
+   uint32_t n_buffers;
48
+};
49
+
50
+struct vulkan_base_info {
51
+   uint32_t queueFlags;
52
+
53
+   struct {
54
+       uint32_t formatCount;
55
+       uint32_t *formats;
56
+   } formatInfo;
57
+};
58
+
59
+struct vulkan_base {
60
+   struct spa_log *log;
61
+
62
+   VkInstance instance;
63
+
64
+   VkPhysicalDevice physicalDevice;
65
+
66
+   VkQueue queue;
67
+   uint32_t queueFamilyIndex;
68
+   VkDevice device;
69
+
70
+   uint32_t formatInfoCount;
71
+   struct vulkan_format_info *formatInfos;
72
+
73
+   bool implicit_sync_interop;
74
+
75
+   unsigned int initialized:1;
76
+};
77
pipewire-0.3.79.tar.gz/spa/plugins/vulkan/vulkan-utils.c -> pipewire-0.3.80.tar.gz/spa/plugins/vulkan/vulkan-utils.c Changed
201
 
1
@@ -1,3 +1,7 @@
2
+/* Spa */
3
+/* SPDX-FileCopyrightText: Copyright © 2019 Wim Taymans */
4
+/* SPDX-License-Identifier: MIT */
5
+
6
 #include <vulkan/vulkan.h>
7
 
8
 #include <unistd.h>
9
@@ -6,6 +10,7 @@
10
 #include <sys/mman.h>
11
 #include <fcntl.h>
12
 #include <string.h>
13
+#include <poll.h>
14
 #if !defined(__FreeBSD__) && !defined(__MidnightBSD__)
15
 #include <alloca.h>
16
 #endif
17
@@ -17,10 +22,12 @@
18
 
19
 #include <spa/utils/result.h>
20
 #include <spa/utils/string.h>
21
+#include <spa/param/video/format.h>
22
 #include <spa/support/log.h>
23
 #include <spa/debug/mem.h>
24
 
25
 #include "vulkan-utils.h"
26
+#include "dmabuf.h"
27
 
28
 //#define ENABLE_VALIDATION
29
 
30
@@ -91,23 +98,14 @@
31
    }
32
 }
33
 
34
-#define VK_CHECK_RESULT(f)                             \
35
-{                                          \
36
-   VkResult _result = (f);                             \
37
-   int _r = -vkresult_to_errno(_result);                       \
38
-   if (_result != VK_SUCCESS) {                            \
39
-       spa_log_error(s->log, "error: %d (%d %s)", _result, _r, spa_strerror(_r));  \
40
-       return _r;                              \
41
-   }                                       \
42
-}
43
-#define CHECK(f)                                   \
44
-{                                          \
45
-   int _res = (f);                                 \
46
-   if (_res < 0)                                   \
47
-       return _res;                                \
48
-}
49
+static struct {
50
+   VkFormat format;
51
+   uint32_t id;
52
+} vk_video_format_convs = {
53
+   { VK_FORMAT_R32G32B32A32_SFLOAT, SPA_VIDEO_FORMAT_RGBA_F32 },
54
+};
55
 
56
-static int createInstance(struct vulkan_state *s)
57
+static int createInstance(struct vulkan_base *s)
58
 {
59
    static const VkApplicationInfo applicationInfo = {
60
        .sType = VK_STRUCTURE_TYPE_APPLICATION_INFO,
61
@@ -154,7 +152,24 @@
62
    return 0;
63
 }
64
 
65
-static uint32_t getComputeQueueFamilyIndex(struct vulkan_state *s)
66
+static int findPhysicalDevice(struct vulkan_base *s)
67
+{
68
+   uint32_t deviceCount;
69
+        VkPhysicalDevice *devices;
70
+
71
+   vkEnumeratePhysicalDevices(s->instance, &deviceCount, NULL);
72
+   if (deviceCount == 0)
73
+       return -ENODEV;
74
+
75
+   devices = alloca(deviceCount * sizeof(VkPhysicalDevice));
76
+        vkEnumeratePhysicalDevices(s->instance, &deviceCount, devices);
77
+
78
+   s->physicalDevice = devices0;
79
+
80
+   return 0;
81
+}
82
+
83
+static int getComputeQueueFamilyIndex(struct vulkan_base *s, uint32_t queueFlags, uint32_t *queueFamilyIndex)
84
 {
85
    uint32_t i, queueFamilyCount;
86
    VkQueueFamilyProperties *queueFamilies;
87
@@ -167,283 +182,245 @@
88
    for (i = 0; i < queueFamilyCount; i++) {
89
        VkQueueFamilyProperties props = queueFamiliesi;
90
 
91
-       if (props.queueCount > 0 && (props.queueFlags & VK_QUEUE_COMPUTE_BIT))
92
+       if (props.queueCount > 0 && ((props.queueFlags & queueFlags) == queueFlags))
93
            break;
94
    }
95
    if (i == queueFamilyCount)
96
        return -ENODEV;
97
 
98
-   return i;
99
-}
100
-
101
-static int findPhysicalDevice(struct vulkan_state *s)
102
-{
103
-   uint32_t deviceCount;
104
-        VkPhysicalDevice *devices;
105
-
106
-   vkEnumeratePhysicalDevices(s->instance, &deviceCount, NULL);
107
-   if (deviceCount == 0)
108
-       return -ENODEV;
109
-
110
-   devices = alloca(deviceCount * sizeof(VkPhysicalDevice));
111
-        vkEnumeratePhysicalDevices(s->instance, &deviceCount, devices);
112
-
113
-   s->physicalDevice = devices0;
114
-
115
-   s->queueFamilyIndex = getComputeQueueFamilyIndex(s);
116
-
117
+   *queueFamilyIndex = i;
118
    return 0;
119
 }
120
 
121
-static int createDevice(struct vulkan_state *s)
122
+static int createDevice(struct vulkan_base *s, struct vulkan_base_info *info)
123
 {
124
 
125
+   CHECK(getComputeQueueFamilyIndex(s, info->queueFlags, &s->queueFamilyIndex));
126
+
127
    const VkDeviceQueueCreateInfo queueCreateInfo = {
128
        .sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,
129
        .queueFamilyIndex = s->queueFamilyIndex,
130
        .queueCount = 1,
131
        .pQueuePriorities = (const float) { 1.0f }
132
    };
133
+   const VkPhysicalDeviceSynchronization2FeaturesKHR sync2_features = {
134
+       .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SYNCHRONIZATION_2_FEATURES_KHR,
135
+       .synchronization2 = VK_TRUE,
136
+   };
137
    static const char * const extensions = {
138
        VK_KHR_EXTERNAL_MEMORY_EXTENSION_NAME,
139
-       VK_KHR_EXTERNAL_MEMORY_FD_EXTENSION_NAME
140
+       VK_KHR_EXTERNAL_MEMORY_FD_EXTENSION_NAME,
141
+       VK_KHR_IMAGE_FORMAT_LIST_EXTENSION_NAME,
142
+       VK_KHR_EXTERNAL_SEMAPHORE_FD_EXTENSION_NAME,
143
+       VK_KHR_SYNCHRONIZATION_2_EXTENSION_NAME,
144
+       VK_EXT_IMAGE_DRM_FORMAT_MODIFIER_EXTENSION_NAME
145
    };
146
    const VkDeviceCreateInfo deviceCreateInfo = {
147
        .sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,
148
        .queueCreateInfoCount = 1,
149
        .pQueueCreateInfos = &queueCreateInfo,
150
-       .enabledExtensionCount = 2,
151
+       .enabledExtensionCount = SPA_N_ELEMENTS(extensions),
152
        .ppEnabledExtensionNames = extensions,
153
+       .pNext = &sync2_features,
154
    };
155
 
156
    VK_CHECK_RESULT(vkCreateDevice(s->physicalDevice, &deviceCreateInfo, NULL, &s->device));
157
 
158
    vkGetDeviceQueue(s->device, s->queueFamilyIndex, 0, &s->queue);
159
 
160
-   static const VkFenceCreateInfo fenceCreateInfo = {
161
-       .sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO,
162
-       .flags = 0,
163
-   };
164
-   VK_CHECK_RESULT(vkCreateFence(s->device, &fenceCreateInfo, NULL, &s->fence));
165
-
166
    return 0;
167
 }
168
 
169
-static uint32_t findMemoryType(struct vulkan_state *s,
170
-       uint32_t memoryTypeBits, VkMemoryPropertyFlags properties)
171
+static int queryFormatInfo(struct vulkan_base *s, struct vulkan_base_info *info)
172
 {
173
-   uint32_t i;
174
-   VkPhysicalDeviceMemoryProperties memoryProperties;
175
+   if (s->formatInfos)
176
+       return 0;
177
 
178
-   vkGetPhysicalDeviceMemoryProperties(s->physicalDevice, &memoryProperties);
179
+   s->formatInfos = calloc(info->formatInfo.formatCount, sizeof(struct vulkan_format_info));
180
+   if (!s->formatInfos)
181
+       return -ENOMEM;
182
 
183
-   for (i = 0; i < memoryProperties.memoryTypeCount; i++) {
184
-       if ((memoryTypeBits & (1 << i)) &&
185
-           ((memoryProperties.memoryTypesi.propertyFlags & properties) == properties))
186
-           return i;
187
-   }
188
-   return -1;
189
-}
190
+   for (uint32_t i = 0; i < info->formatInfo.formatCount; i++) {
191
+       VkFormat format = vulkan_id_to_vkformat(info->formatInfo.formatsi);
192
+       if (format == VK_FORMAT_UNDEFINED)
193
+           continue;
194
+       struct vulkan_format_info *f_info = &s->formatInfoss->formatInfoCount++;
195
+       f_info->spa_format = info->formatInfo.formatsi;
196
+       f_info->vk_format = format;
197
 
198
-static int createDescriptors(struct vulkan_state *s)
199
-{
200
-   uint32_t i;
201
pipewire-0.3.79.tar.gz/spa/plugins/vulkan/vulkan-utils.h -> pipewire-0.3.80.tar.gz/spa/plugins/vulkan/vulkan-utils.h Changed
172
 
1
@@ -1,86 +1,105 @@
2
+/* Spa */
3
+/* SPDX-FileCopyrightText: Copyright © 2019 Wim Taymans */
4
+/* SPDX-License-Identifier: MIT */
5
+
6
+#pragma once
7
+
8
 #include <vulkan/vulkan.h>
9
 
10
 #include <spa/buffer/buffer.h>
11
 #include <spa/node/node.h>
12
 
13
-#define MAX_STREAMS 2
14
-#define MAX_BUFFERS 16
15
-#define WORKGROUP_SIZE 32
16
-
17
-struct pixel {
18
-   float r, g, b, a;
19
+#include "vulkan-types.h"
20
+
21
+#define VK_CHECK_RESULT(f)                             \
22
+{                                          \
23
+   VkResult _result = (f);                             \
24
+   int _r = -vulkan_vkresult_to_errno(_result);                        \
25
+   if (_result != VK_SUCCESS) {                            \
26
+       spa_log_error(s->log, "error: %d (%d %s)", _result, _r, spa_strerror(_r));  \
27
+       return _r;                              \
28
+   }                                       \
29
+}
30
+#define VK_CHECK_RESULT_WITH_CLEANUP(f, c)                     \
31
+{                                          \
32
+   VkResult _result = (f);                             \
33
+   int _r = -vkresult_to_errno(_result);                       \
34
+   if (_result != VK_SUCCESS) {                            \
35
+       spa_log_error(s->log, "error: %d (%d %s)", _result, _r, spa_strerror(_r));  \
36
+       (c);                                    \
37
+       return _r;                              \
38
+   }                                       \
39
+}
40
+#define VK_CHECK_RESULT_LOOP(f)                                \
41
+{                                          \
42
+   VkResult _result = (f);                             \
43
+   int _r = -vkresult_to_errno(_result);                       \
44
+   if (_result != VK_SUCCESS) {                            \
45
+       spa_log_error(s->log, "error: %d (%d %s)", _result, _r, spa_strerror(_r));  \
46
+       continue;                               \
47
+   }                                       \
48
+}
49
+#define CHECK(f)                                   \
50
+{                                          \
51
+   int _res = (f);                                 \
52
+   if (_res < 0)                                   \
53
+       return _res;                                \
54
+}
55
+
56
+struct vulkan_read_pixels_info {
57
+   struct spa_rectangle size;
58
+   void *data;
59
+   uint32_t offset;
60
+   uint32_t stride;
61
+   uint32_t bytes_per_pixel;
62
 };
63
 
64
-struct push_constants {
65
-   float time;
66
-   int frame;
67
-   int width;
68
-   int height;
69
+struct dmabuf_fixation_info {
70
+   VkFormat format;
71
+   uint64_t modifierCount;
72
+   uint64_t *modifiers;
73
+   struct spa_rectangle size;
74
+   VkImageUsageFlags usage;
75
 };
76
 
77
-struct vulkan_buffer {
78
-   int fd;
79
-   VkImage image;
80
-   VkImageView view;
81
-   VkDeviceMemory memory;
82
-};
83
-
84
-struct vulkan_stream {
85
-   enum spa_direction direction;
86
-
87
-   uint32_t pending_buffer_id;
88
-   uint32_t current_buffer_id;
89
-   uint32_t busy_buffer_id;
90
-   uint32_t ready_buffer_id;
91
-
92
-   struct vulkan_buffer buffersMAX_BUFFERS;
93
-   uint32_t n_buffers;
94
+struct external_buffer_info {
95
+   VkFormat format;
96
+   uint64_t modifier;
97
+   struct spa_rectangle size;
98
+   VkImageUsageFlags usage;
99
+   struct spa_buffer *spa_buf;
100
 };
101
 
102
-struct vulkan_state {
103
-   struct spa_log *log;
104
-
105
-   struct push_constants constants;
106
-
107
-   VkInstance instance;
108
+int vulkan_read_pixels(struct vulkan_base *s, struct vulkan_read_pixels_info *info, struct vulkan_buffer *vk_buf);
109
 
110
-   VkPhysicalDevice physicalDevice;
111
-   VkDevice device;
112
+int vulkan_sync_foreign_dmabuf(struct vulkan_base *s, struct vulkan_buffer *vk_buf);
113
+bool vulkan_sync_export_dmabuf(struct vulkan_base *s, struct vulkan_buffer *vk_buf, int sync_file_fd);
114
 
115
-   VkPipeline pipeline;
116
-   VkPipelineLayout pipelineLayout;
117
-   const char *shaderName;
118
-   VkShaderModule computeShaderModule;
119
+int vulkan_fixate_modifier(struct vulkan_base *s, struct dmabuf_fixation_info *info, uint64_t *modifier);
120
+int vulkan_create_dmabuf(struct vulkan_base *s, struct external_buffer_info *info, struct vulkan_buffer *vk_buf);
121
+int vulkan_import_dmabuf(struct vulkan_base *s, struct external_buffer_info *info, struct vulkan_buffer *vk_buf);
122
+int vulkan_import_memptr(struct vulkan_base *s, struct external_buffer_info *info, struct vulkan_buffer *vk_buf);
123
 
124
-   VkCommandPool commandPool;
125
-   VkCommandBuffer commandBuffer;
126
+int vulkan_commandPool_create(struct vulkan_base *s, VkCommandPool *commandPool);
127
+int vulkan_commandBuffer_create(struct vulkan_base *s, VkCommandPool commandPool, VkCommandBuffer *commandBuffer);
128
 
129
-   VkQueue queue;
130
-   uint32_t queueFamilyIndex;
131
-   VkFence fence;
132
-   unsigned int prepared:1;
133
-   unsigned int started:1;
134
+uint32_t vulkan_memoryType_find(struct vulkan_base *s,
135
+       uint32_t memoryTypeBits, VkMemoryPropertyFlags properties);
136
+struct vulkan_format_info *vulkan_formatInfo_find(struct vulkan_base *s, VkFormat format);
137
+struct vulkan_modifier_info *vulkan_modifierInfo_find(struct vulkan_base *s, VkFormat format, uint64_t modifier);
138
 
139
-   VkDescriptorPool descriptorPool;
140
-   VkDescriptorSetLayout descriptorSetLayout;
141
+void vulkan_buffer_clear(struct vulkan_base *s, struct vulkan_buffer *buffer);
142
 
143
-   VkSampler sampler;
144
+int vulkan_stream_init(struct vulkan_stream *stream, enum spa_direction direction,
145
+       struct spa_dict *props);
146
 
147
-   uint32_t n_streams;
148
-   VkDescriptorSet descriptorSet;
149
-   struct vulkan_stream streamsMAX_STREAMS;
150
-};
151
+uint32_t vulkan_vkformat_to_id(VkFormat vkFormat);
152
+VkFormat vulkan_id_to_vkformat(uint32_t id);
153
 
154
-int spa_vulkan_init_stream(struct vulkan_state *s, struct vulkan_stream *stream, enum spa_direction,
155
-       struct spa_dict *props);
156
+int vulkan_vkresult_to_errno(VkResult result);
157
 
158
-int spa_vulkan_prepare(struct vulkan_state *s);
159
-int spa_vulkan_use_buffers(struct vulkan_state *s, struct vulkan_stream *stream, uint32_t flags,
160
-       uint32_t n_buffers, struct spa_buffer **buffers);
161
-int spa_vulkan_unprepare(struct vulkan_state *s);
162
+int vulkan_wait_fence(struct vulkan_base *s, VkFence fence);
163
+int vulkan_wait_idle(struct vulkan_base *s);
164
 
165
-int spa_vulkan_start(struct vulkan_state *s);
166
-int spa_vulkan_stop(struct vulkan_state *s);
167
-int spa_vulkan_ready(struct vulkan_state *s);
168
-int spa_vulkan_process(struct vulkan_state *s);
169
-int spa_vulkan_cleanup(struct vulkan_state *s);
170
+int vulkan_base_init(struct vulkan_base *s, struct vulkan_base_info *info);
171
+void vulkan_base_deinit(struct vulkan_base *s);
172
pipewire-0.3.79.tar.gz/src/daemon/meson.build -> pipewire-0.3.80.tar.gz/src/daemon/meson.build Changed
40
 
1
@@ -27,6 +27,8 @@
2
 build_wp = 'wireplumber' in get_option('session-managers')
3
 default_sm = get_option('session-managers').get(0, '')
4
 
5
+build_vk = get_option('vulkan').enabled()
6
+
7
 summary({'Build media-session': build_ms,
8
          'Build wireplumber': build_wp,
9
          'Default session-manager': default_sm},
10
@@ -75,6 +77,10 @@
11
  'pipewire-aes67.conf',
12
 
13
 
14
+if build_vk
15
+  conf_files += 'pipewire-vulkan.conf'
16
+endif
17
+
18
 foreach c : conf_files
19
   configure_file(input : '@0@.in'.format(c),
20
                  output : c,
21
@@ -107,7 +113,17 @@
22
 
23
 ln = find_program('ln')
24
 
25
-foreach alias : 'pipewire-pulse', 'pipewire-avb', 'pipewire-aes67'
26
+pipewire_aliases = 
27
+  'pipewire-pulse',
28
+  'pipewire-avb',
29
+  'pipewire-aes67',
30
+
31
+
32
+if build_vk
33
+ pipewire_aliases += 'pipewire-vulkan'
34
+endif
35
+
36
+foreach alias : pipewire_aliases
37
   custom_target(
38
     alias,
39
     build_by_default: true,
40
pipewire-0.3.79.tar.gz/src/daemon/minimal.conf.in -> pipewire-0.3.80.tar.gz/src/daemon/minimal.conf.in Changed
15
 
1
@@ -144,11 +144,11 @@
2
     # Creates an object from a PipeWire factory with the given parameters.
3
     # If nofail is given, errors are ignored (and no object is created).
4
     #
5
-    #{ factory = spa-node-factory   args = { factory.name = videotestsrc node.name = videotestsrc Spa:Pod:Object:Param:Props:patternType = 1 } }
6
+    #{ factory = spa-node-factory   args = { factory.name = videotestsrc node.name = videotestsrc node.description = videotestsrc Spa:Pod:Object:Param:Props:patternType = 1 } }
7
     #{ factory = spa-device-factory args = { factory.name = api.jack.device foo=bar } flags =  nofail  }
8
     #{ factory = spa-device-factory args = { factory.name = api.alsa.enum.udev } }
9
     #{ factory = spa-node-factory   args = { factory.name = api.alsa.seq.bridge node.name = Internal-MIDI-Bridge } }
10
-    #{ factory = adapter            args = { factory.name = audiotestsrc node.name = my-test } }
11
+    #{ factory = adapter            args = { factory.name = audiotestsrc node.name = my-test node.description = audiotestsrc } }
12
     #{ factory = spa-node-factory   args = { factory.name = api.vulkan.compute.source node.name = my-compute-source } }
13
 
14
     # Make a default metadata store
15
pipewire-0.3.80.tar.gz/src/daemon/pipewire-vulkan.conf.in Added
100
 
1
@@ -0,0 +1,98 @@
2
+# Config file for PipeWire version "0.3.77" #
3
+#
4
+# This config file should start the vulkan-compute-source/filter as proxied
5
+# clients
6
+#
7
+
8
+context.properties = {
9
+    ## Configure properties in the system.
10
+    #library.name.system                   = support/libspa-support
11
+    #context.data-loop.library.name.system = support/libspa-support
12
+    #support.dbus                          = true
13
+    #link.max-buffers                      = 64
14
+    #link.max-buffers                       = 16                       # version < 3 clients can't handle more
15
+    #mem.warn-mlock                        = false
16
+    #mem.allow-mlock                       = true
17
+    #mem.mlock-all                         = false
18
+    #clock.power-of-two-quantum            = true
19
+    #log.level                             = 4
20
+    #cpu.zero.denormals                    = false
21
+
22
+    #default.clock.quantum-limit = 8192
23
+}
24
+
25
+context.spa-libs = {
26
+    #<factory-name regex> = <library-name>
27
+    #
28
+    # Used to find spa factory names. It maps an spa factory name
29
+    # regular expression to a library name that should contain
30
+    # that factory.
31
+    #
32
+    api.vulkan.*    = vulkan/libspa-vulkan
33
+    support.*       = support/libspa-support
34
+}
35
+
36
+context.modules = 
37
+    #{ name = <module-name>
38
+    #    ( args  = { <key> = <value> ... } )
39
+    #    ( flags =  ( ifexists ) ( nofail )  )
40
+    #    ( condition =  { <key> = <value> ... } ...  )
41
+    #}
42
+    #
43
+    # Loads a module with the given parameters.
44
+    # If ifexists is given, the module is ignored when it is not found.
45
+    # If nofail is given, module initialization failures are ignored.
46
+    # If condition is given, the module is loaded only when the context
47
+    # properties all match the match rules.
48
+    #
49
+
50
+    # Uses realtime scheduling to boost the audio thread priorities. This uses
51
+    # RTKit if the user doesn't have permission to use regular realtime
52
+    # scheduling.
53
+    { name = libpipewire-module-rt
54
+        args = {
55
+            nice.level    = -11
56
+            #rt.prio      = 88
57
+            #rt.time.soft = -1
58
+            #rt.time.hard = -1
59
+        }
60
+        flags =  ifexists nofail 
61
+    }
62
+
63
+    # The native communication protocol.
64
+    { name = libpipewire-module-protocol-native }
65
+
66
+    # Creates a factory for making nodes that run in the
67
+    # context of the PipeWire server.
68
+    { name = libpipewire-module-spa-node-factory }
69
+
70
+    # Allows creating nodes that run in the context of the
71
+    # client. Is used by all clients that want to provide
72
+    # data to PipeWire.
73
+    { name = libpipewire-module-client-node }
74
+
75
+    # Makes a factory for wrapping nodes in an adapter with a
76
+    # converter and resampler.
77
+    { name = libpipewire-module-adapter }
78
+
79
+
80
+context.objects = 
81
+    #{ factory = <factory-name>
82
+    #    ( args  = { <key> = <value> ... } )
83
+    #    ( flags =  ( nofail )  )
84
+    #    ( condition =  { <key> = <value> ... } ...  )
85
+    #}
86
+    #
87
+    # Creates an object from a PipeWire factory with the given parameters.
88
+    # If nofail is given, errors are ignored (and no object is created).
89
+    # If condition is given, the object is created only when the context properties
90
+    # all match the match rules.
91
+    #
92
+    #{ factory = spa-node-factory   args = { factory.name = videotestsrc node.name = videotestsrc Spa:Pod:Object:Param:Props:patternType = 1 } }
93
+    #{ factory = spa-device-factory args = { factory.name = api.jack.device foo=bar } flags =  nofail  }
94
+    #{ factory = spa-device-factory args = { factory.name = api.alsa.enum.udev } }
95
+    #{ factory = spa-node-factory   args = { factory.name = api.alsa.seq.bridge node.name = Internal-MIDI-Bridge } }
96
+    #{ factory = adapter            args = { factory.name = audiotestsrc node.name = my-test } }
97
+    { factory = spa-node-factory   args = { factory.name = api.vulkan.compute.source node.name = vulkan-compute-source object.export = true } }
98
+    { factory = spa-node-factory   args = { factory.name = api.vulkan.compute.filter node.name = vulkan-compute-filter object.export = true } }
99
+
100
pipewire-0.3.79.tar.gz/src/daemon/pipewire.conf.in -> pipewire-0.3.80.tar.gz/src/daemon/pipewire.conf.in Changed
15
 
1
@@ -193,11 +193,11 @@
2
     # If condition is given, the object is created only when the context properties
3
     # all match the match rules.
4
     #
5
-    #{ factory = spa-node-factory   args = { factory.name = videotestsrc node.name = videotestsrc Spa:Pod:Object:Param:Props:patternType = 1 } }
6
+    #{ factory = spa-node-factory   args = { factory.name = videotestsrc node.name = videotestsrc node.description = videotestsrc Spa:Pod:Object:Param:Props:patternType = 1 } }
7
     #{ factory = spa-device-factory args = { factory.name = api.jack.device foo=bar } flags =  nofail  }
8
     #{ factory = spa-device-factory args = { factory.name = api.alsa.enum.udev } }
9
     #{ factory = spa-node-factory   args = { factory.name = api.alsa.seq.bridge node.name = Internal-MIDI-Bridge } }
10
-    #{ factory = adapter            args = { factory.name = audiotestsrc node.name = my-test } }
11
+    #{ factory = adapter            args = { factory.name = audiotestsrc node.name = my-test node.description = audiotestsrc } }
12
     #{ factory = spa-node-factory   args = { factory.name = api.vulkan.compute.source node.name = my-compute-source } }
13
 
14
     # A default dummy driver. This handles nodes marked with the "node.always-driver"
15
pipewire-0.3.79.tar.gz/src/examples/video-play.c -> pipewire-0.3.80.tar.gz/src/examples/video-play.c Changed
49
 
1
@@ -14,8 +14,10 @@
2
 
3
 #include <spa/utils/result.h>
4
 #include <spa/param/video/format-utils.h>
5
+#include <spa/param/tag-utils.h>
6
 #include <spa/param/props.h>
7
 #include <spa/debug/format.h>
8
+#include <spa/debug/pod.h>
9
 
10
 #include <pipewire/pipewire.h>
11
 
12
@@ -278,6 +280,10 @@
13
    void *d;
14
    int32_t mult, size;
15
 
16
+   if (param != NULL && id == SPA_PARAM_Tag) {
17
+       spa_debug_pod(0, NULL, param);
18
+       return;
19
+   }
20
    /* NULL means to clear the format */
21
    if (param == NULL || id != SPA_PARAM_Format)
22
        return;
23
@@ -422,7 +428,7 @@
24
 int main(int argc, char *argv)
25
 {
26
    struct data data = { 0, };
27
-   const struct spa_pod *params2;
28
+   const struct spa_pod *params3;
29
    uint8_t buffer1024;
30
    struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buffer, sizeof(buffer));
31
    struct pw_properties *props;
32
@@ -478,6 +484,16 @@
33
     * object to the stack. */
34
    n_params = build_format(&data, &b, params);
35
 
36
+   {
37
+       struct spa_pod_frame f;
38
+       struct spa_dict_item items1;
39
+       /* send a tag, input tags travel upstream */
40
+       spa_tag_build_start(&b, &f, SPA_PARAM_Tag, SPA_DIRECTION_INPUT);
41
+       items0 = SPA_DICT_ITEM_INIT("my-tag-other-key", "my-special-other-tag-value");
42
+       spa_tag_build_add_dict(&b, &SPA_DICT_INIT(items, 1));
43
+       paramsn_params++ = spa_tag_build_end(&b, &f);
44
+   }
45
+
46
    /* now connect the stream, we need a direction (input/output),
47
     * an optional target node to connect to, some flags and parameters
48
     */
49
pipewire-0.3.79.tar.gz/src/examples/video-src.c -> pipewire-0.3.80.tar.gz/src/examples/video-src.c Changed
56
 
1
@@ -14,6 +14,8 @@
2
 #include <math.h>
3
 
4
 #include <spa/param/video/format-utils.h>
5
+#include <spa/param/tag-utils.h>
6
+#include <spa/debug/pod.h>
7
 
8
 #include <pipewire/pipewire.h>
9
 
10
@@ -212,6 +214,10 @@
11
    struct spa_pod_builder b = SPA_POD_BUILDER_INIT(params_buffer, sizeof(params_buffer));
12
    const struct spa_pod *params5;
13
 
14
+   if (param != NULL && id == SPA_PARAM_Tag) {
15
+       spa_debug_pod(0, NULL, param);
16
+       return;
17
+   }
18
    if (param == NULL || id != SPA_PARAM_Format)
19
        return;
20
 
21
@@ -276,7 +282,7 @@
22
 int main(int argc, char *argv)
23
 {
24
    struct data data = { 0, };
25
-   const struct spa_pod *params1;
26
+   const struct spa_pod *params2;
27
    uint8_t buffer1024;
28
    struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buffer, sizeof(buffer));
29
 
30
@@ -314,6 +320,16 @@
31
                        &SPA_RECTANGLE(4096, 4096)),
32
        SPA_FORMAT_VIDEO_framerate, SPA_POD_Fraction(&SPA_FRACTION(25, 1)));
33
 
34
+   {
35
+       struct spa_pod_frame f;
36
+       struct spa_dict_item items1;
37
+       /* send a tag, output tags travel downstream */
38
+       spa_tag_build_start(&b, &f, SPA_PARAM_Tag, SPA_DIRECTION_OUTPUT);
39
+       items0 = SPA_DICT_ITEM_INIT("my-tag-key", "my-special-tag-value");
40
+       spa_tag_build_add_dict(&b, &SPA_DICT_INIT(items, 1));
41
+       params1 = spa_tag_build_end(&b, &f);
42
+   }
43
+
44
    pw_stream_add_listener(data.stream,
45
                   &data.stream_listener,
46
                   &stream_events,
47
@@ -324,7 +340,7 @@
48
              PW_ID_ANY,
49
              PW_STREAM_FLAG_DRIVER |
50
              PW_STREAM_FLAG_MAP_BUFFERS,
51
-             params, 1);
52
+             params, 2);
53
 
54
    pw_main_loop_run(data.loop);
55
 
56
pipewire-0.3.79.tar.gz/src/modules/module-combine-stream.c -> pipewire-0.3.80.tar.gz/src/modules/module-combine-stream.c Changed
10
 
1
@@ -697,7 +697,7 @@
2
        update_delay(s->impl);
3
        break;
4
    case SPA_PARAM_Latency:
5
-       if (!param) {
6
+       if (param == NULL) {
7
            s->have_latency = false;
8
        } else if (spa_latency_parse(param, &latency) == 0 &&
9
                latency.direction == get_combine_direction(s->impl)) {
10
pipewire-0.3.79.tar.gz/src/modules/module-echo-cancel.c -> pipewire-0.3.80.tar.gz/src/modules/module-echo-cancel.c Changed
19
 
1
@@ -604,7 +604,7 @@
2
    struct spa_pod_builder b;
3
    const struct spa_pod *params1;
4
 
5
-   if (spa_latency_parse(param, &latency) < 0)
6
+   if (param == NULL || spa_latency_parse(param, &latency) < 0)
7
        return;
8
 
9
    spa_pod_builder_init(&b, buffer, sizeof(buffer));
10
@@ -767,7 +767,7 @@
11
    struct spa_pod_builder b;
12
    const struct spa_pod *params1;
13
 
14
-   if (spa_latency_parse(param, &latency) < 0)
15
+   if (param == NULL || spa_latency_parse(param, &latency) < 0)
16
        return;
17
 
18
    spa_pod_builder_init(&b, buffer, sizeof(buffer));
19
pipewire-0.3.79.tar.gz/src/modules/module-example-filter.c -> pipewire-0.3.80.tar.gz/src/modules/module-example-filter.c Changed
10
 
1
@@ -227,7 +227,7 @@
2
    struct spa_pod_builder b;
3
    const struct spa_pod *params1;
4
 
5
-   if (spa_latency_parse(param, &latency) < 0)
6
+   if (param == NULL || spa_latency_parse(param, &latency) < 0)
7
        return;
8
 
9
    *info = latency;
10
pipewire-0.3.79.tar.gz/src/modules/module-ffado-driver.c -> pipewire-0.3.80.tar.gz/src/modules/module-ffado-driver.c Changed
10
 
1
@@ -417,7 +417,7 @@
2
    bool update = false;
3
    enum spa_direction direction = port->direction;
4
 
5
-   if (spa_latency_parse(param, &latency) < 0)
6
+   if (param == NULL || spa_latency_parse(param, &latency) < 0)
7
        return;
8
 
9
    if (spa_latency_info_compare(&port->latencydirection, &latency)) {
10
pipewire-0.3.79.tar.gz/src/modules/module-filter-chain.c -> pipewire-0.3.80.tar.gz/src/modules/module-filter-chain.c Changed
50
 
1
@@ -20,6 +20,7 @@
2
 #include <spa/utils/json.h>
3
 #include <spa/support/cpu.h>
4
 #include <spa/param/latency-utils.h>
5
+#include <spa/param/tag-utils.h>
6
 #include <spa/pod/dynamic.h>
7
 #include <spa/debug/types.h>
8
 
9
@@ -1109,7 +1110,7 @@
10
    struct spa_pod_builder b;
11
    const struct spa_pod *params1;
12
 
13
-   if (spa_latency_parse(param, &latency) < 0)
14
+   if (param == NULL || spa_latency_parse(param, &latency) < 0)
15
        return;
16
 
17
    spa_pod_builder_init(&b, buffer, sizeof(buffer));
18
@@ -1121,6 +1122,21 @@
19
        pw_stream_update_params(impl->playback, params, 1);
20
 }
21
 
22
+static void param_tag_changed(struct impl *impl, const struct spa_pod *param)
23
+{
24
+   struct spa_tag_info tag;
25
+   const struct spa_pod *params1 = { param };
26
+   void *state = NULL;
27
+
28
+   if (param == 0 || spa_tag_parse(param, &tag, &state) < 0)
29
+       return;
30
+
31
+   if (tag.direction == SPA_DIRECTION_INPUT)
32
+       pw_stream_update_params(impl->capture, params, 1);
33
+   else
34
+       pw_stream_update_params(impl->playback, params, 1);
35
+}
36
+
37
 static void state_changed(void *data, enum pw_stream_state old,
38
        enum pw_stream_state state, const char *error)
39
 {
40
@@ -1208,6 +1224,9 @@
41
    case SPA_PARAM_Latency:
42
        param_latency_changed(impl, param);
43
        break;
44
+   case SPA_PARAM_Tag:
45
+       param_tag_changed(impl, param);
46
+       break;
47
    }
48
    return;
49
 
50
pipewire-0.3.79.tar.gz/src/modules/module-filter-chain/builtin_plugin.c -> pipewire-0.3.80.tar.gz/src/modules/module-filter-chain/builtin_plugin.c Changed
10
 
1
@@ -921,7 +921,7 @@
2
                pw_log_error("convolver:filename requires a string or an array");
3
                return NULL;
4
            } else {
5
-               filenamesi = strdup(v);
6
+               filenames0 = strdup(v);
7
            }
8
        }
9
        else if (spa_streq(key, "offset")) {
10
pipewire-0.3.79.tar.gz/src/modules/module-jack-tunnel.c -> pipewire-0.3.80.tar.gz/src/modules/module-jack-tunnel.c Changed
10
 
1
@@ -410,7 +410,7 @@
2
    bool update = false;
3
    enum spa_direction direction = port->direction;
4
 
5
-   if (spa_latency_parse(param, &latency) < 0)
6
+   if (param == NULL || spa_latency_parse(param, &latency) < 0)
7
        return;
8
 
9
    if (spa_latency_info_compare(&port->latencydirection, &latency)) {
10
pipewire-0.3.79.tar.gz/src/modules/module-loopback.c -> pipewire-0.3.80.tar.gz/src/modules/module-loopback.c Changed
76
 
1
@@ -171,13 +171,11 @@
2
    struct pw_stream *capture;
3
    struct spa_hook capture_listener;
4
    struct spa_audio_info_raw capture_info;
5
-   struct spa_latency_info capture_latency;
6
 
7
    struct pw_properties *playback_props;
8
    struct pw_stream *playback;
9
    struct spa_hook playback_listener;
10
    struct spa_audio_info_raw playback_info;
11
-   struct spa_latency_info playback_latency;
12
 
13
    unsigned int do_disconnect:1;
14
    unsigned int recalc_delay:1;
15
@@ -317,18 +315,16 @@
16
 }
17
 
18
 static void param_latency_changed(struct impl *impl, const struct spa_pod *param,
19
-       struct spa_latency_info *info, struct pw_stream *other)
20
+       struct pw_stream *other)
21
 {
22
    struct spa_latency_info latency;
23
    uint8_t buffer1024;
24
    struct spa_pod_builder b;
25
    const struct spa_pod *params1;
26
 
27
-   if (spa_latency_parse(param, &latency) < 0)
28
+   if (param == NULL || spa_latency_parse(param, &latency) < 0)
29
        return;
30
 
31
-   *info = latency;
32
-
33
    spa_pod_builder_init(&b, buffer, sizeof(buffer));
34
    params0 = spa_latency_build(&b, SPA_PARAM_Latency, &latency);
35
    pw_stream_update_params(other, params, 1);
36
@@ -336,6 +332,15 @@
37
    impl->recalc_delay = true;
38
 }
39
 
40
+static void param_tag_changed(struct impl *impl, const struct spa_pod *param,
41
+       struct pw_stream *other)
42
+{
43
+   const struct spa_pod *params1 = { param };
44
+   if (param == NULL)
45
+       return;
46
+   pw_stream_update_params(other, params, 1);
47
+}
48
+
49
 static void recalculate_buffer(struct impl *impl)
50
 {
51
    if (impl->target_delay > 0.0f) {
52
@@ -414,7 +419,10 @@
53
        break;
54
    }
55
    case SPA_PARAM_Latency:
56
-       param_latency_changed(impl, param, &impl->capture_latency, impl->playback);
57
+       param_latency_changed(impl, param, impl->playback);
58
+       break;
59
+   case SPA_PARAM_Tag:
60
+       param_tag_changed(impl, param, impl->playback);
61
        break;
62
    }
63
 }
64
@@ -453,7 +461,10 @@
65
 
66
    switch (id) {
67
    case SPA_PARAM_Latency:
68
-       param_latency_changed(impl, param, &impl->playback_latency, impl->capture);
69
+       param_latency_changed(impl, param, impl->capture);
70
+       break;
71
+   case SPA_PARAM_Tag:
72
+       param_tag_changed(impl, param, impl->capture);
73
        break;
74
    }
75
 }
76
pipewire-0.3.79.tar.gz/src/modules/module-netjack2-driver.c -> pipewire-0.3.80.tar.gz/src/modules/module-netjack2-driver.c Changed
10
 
1
@@ -371,7 +371,7 @@
2
    bool update = false;
3
    enum spa_direction direction = port->direction;
4
 
5
-   if (spa_latency_parse(param, &latency) < 0)
6
+   if (param == NULL || spa_latency_parse(param, &latency) < 0)
7
        return;
8
 
9
    if (spa_latency_info_compare(&port->latencydirection, &latency)) {
10
pipewire-0.3.79.tar.gz/src/modules/module-netjack2-manager.c -> pipewire-0.3.80.tar.gz/src/modules/module-netjack2-manager.c Changed
10
 
1
@@ -493,7 +493,7 @@
2
    bool update = false;
3
    enum spa_direction direction = port->direction;
4
 
5
-   if (spa_latency_parse(param, &latency) < 0)
6
+   if (param == NULL || spa_latency_parse(param, &latency) < 0)
7
        return;
8
 
9
    if (spa_latency_info_compare(&port->latencydirection, &latency)) {
10
pipewire-0.3.79.tar.gz/src/modules/module-pipe-tunnel.c -> pipewire-0.3.80.tar.gz/src/modules/module-pipe-tunnel.c Changed
201
 
1
@@ -111,6 +111,9 @@
2
 #define DEFAULT_CHANNELS 2
3
 #define DEFAULT_POSITION " FL FR "
4
 
5
+#define RINGBUFFER_SIZE        (1u << 22)
6
+#define RINGBUFFER_MASK        (RINGBUFFER_SIZE-1)
7
+
8
 PW_LOG_TOPIC_STATIC(mod_topic, "mod." NAME);
9
 #define PW_LOG_TOPIC_DEFAULT mod_topic
10
 
11
@@ -137,6 +140,7 @@
12
 
13
 struct impl {
14
    struct pw_context *context;
15
+   struct pw_loop *data_loop;
16
 
17
 #define MODE_PLAYBACK  0
18
 #define MODE_CAPTURE   1
19
@@ -156,6 +160,8 @@
20
    char *filename;
21
    unsigned int unlink_fifo;
22
    int fd;
23
+   struct spa_source *socket;
24
+   struct spa_source *timer;
25
 
26
    struct pw_properties *stream_props;
27
    enum pw_direction direction;
28
@@ -165,10 +171,78 @@
29
    uint32_t frame_size;
30
 
31
    unsigned int do_disconnect:1;
32
-   uint32_t leftover_count;
33
-   uint8_t *leftover;
34
+   unsigned int driving:1;
35
+   unsigned int have_sync:1;
36
+
37
+   struct spa_ringbuffer ring;
38
+   void *buffer;
39
+   uint32_t target_buffer;
40
+
41
+   struct spa_io_rate_match *rate_match;
42
+   struct spa_io_position *position;
43
+
44
+   struct spa_dll dll;
45
+   float max_error;
46
+   float corr;
47
+
48
+   uint64_t next_time;
49
 };
50
 
51
+static uint64_t get_time_ns(struct impl *impl)
52
+{
53
+   struct timespec now;
54
+   if (spa_system_clock_gettime(impl->data_loop->system, CLOCK_MONOTONIC, &now) < 0)
55
+       return 0;
56
+   return SPA_TIMESPEC_TO_NSEC(&now);
57
+}
58
+
59
+static int set_timeout(struct impl *impl, uint64_t time)
60
+{
61
+   struct timespec timeout, interval;
62
+   timeout.tv_sec = time / SPA_NSEC_PER_SEC;
63
+   timeout.tv_nsec = time % SPA_NSEC_PER_SEC;
64
+   interval.tv_sec = 0;
65
+   interval.tv_nsec = 0;
66
+   pw_loop_update_timer(impl->data_loop,
67
+                                impl->timer, &timeout, &interval, true);
68
+   return 0;
69
+}
70
+
71
+static void on_timeout(void *d, uint64_t expirations)
72
+{
73
+   struct impl *impl = d;
74
+   uint64_t duration, current_time;
75
+   uint32_t rate, index;
76
+   int32_t avail;
77
+   struct spa_io_position *pos = impl->position;
78
+
79
+   if (SPA_LIKELY(pos)) {
80
+       duration = pos->clock.target_duration;
81
+       rate = pos->clock.target_rate.denom;
82
+   } else {
83
+       duration = 1024;
84
+       rate = 48000;
85
+   }
86
+   pw_log_debug("timeout %"PRIu64, duration);
87
+
88
+   current_time = impl->next_time;
89
+   impl->next_time += duration / impl->corr * 1e9 / rate;
90
+   avail = spa_ringbuffer_get_read_index(&impl->ring, &index);
91
+
92
+   if (SPA_LIKELY(pos)) {
93
+                pos->clock.nsec = current_time;
94
+                pos->clock.rate = pos->clock.target_rate;
95
+                pos->clock.position += pos->clock.duration;
96
+                pos->clock.duration = pos->clock.target_duration;
97
+                pos->clock.delay = SPA_SCALE32_UP(avail, rate, impl->info.rate);
98
+                pos->clock.rate_diff = impl->corr;
99
+                pos->clock.next_nsec = impl->next_time;
100
+        }
101
+   set_timeout(impl, impl->next_time);
102
+
103
+   pw_stream_trigger_process(impl->stream);
104
+}
105
+
106
 static void stream_destroy(void *d)
107
 {
108
    struct impl *impl = d;
109
@@ -186,8 +260,20 @@
110
        pw_impl_module_schedule_destroy(impl->module);
111
        break;
112
    case PW_STREAM_STATE_PAUSED:
113
+       if (impl->direction == PW_DIRECTION_OUTPUT) {
114
+           pw_loop_update_io(impl->data_loop, impl->socket, 0);
115
+           set_timeout(impl, 0);
116
+       }
117
        break;
118
    case PW_STREAM_STATE_STREAMING:
119
+       if (impl->direction == PW_DIRECTION_OUTPUT) {
120
+           pw_loop_update_io(impl->data_loop, impl->socket, SPA_IO_IN);
121
+           impl->driving = pw_stream_is_driving(impl->stream);
122
+           if (impl->driving) {
123
+               impl->next_time = get_time_ns(impl);
124
+               set_timeout(impl, impl->next_time);
125
+           }
126
+       }
127
        break;
128
    default:
129
        break;
130
@@ -221,9 +307,12 @@
131
                    continue;
132
                } else if (errno == EAGAIN || errno == EWOULDBLOCK) {
133
                    /* Don't continue writing */
134
+                   pw_log_debug("pipe (%s) overrun: %m",
135
+                           impl->filename);
136
                    break;
137
                } else {
138
-                   pw_log_warn("Failed to write to pipe sink");
139
+                   pw_log_warn("Failed to write to pipe (%s): %m",
140
+                           impl->filename);
141
                }
142
            }
143
            offs += written;
144
@@ -233,53 +322,95 @@
145
    pw_stream_queue_buffer(impl->stream, buf);
146
 }
147
 
148
+static void update_rate(struct impl *impl, uint32_t filled)
149
+{
150
+   float error;
151
+
152
+   if (impl->rate_match == NULL)
153
+       return;
154
+
155
+   error = (float)impl->target_buffer - (float)(filled);
156
+   error = SPA_CLAMP(error, -impl->max_error, impl->max_error);
157
+
158
+   impl->corr = spa_dll_update(&impl->dll, error);
159
+   pw_log_debug("error:%f corr:%f current:%u target:%u",
160
+           error, impl->corr, filled, impl->target_buffer);
161
+
162
+   if (!impl->driving) {
163
+       SPA_FLAG_SET(impl->rate_match->flags, SPA_IO_RATE_MATCH_FLAG_ACTIVE);
164
+       impl->rate_match->rate = 1.0f / impl->corr;
165
+   }
166
+}
167
+
168
 static void capture_stream_process(void *data)
169
 {
170
    struct impl *impl = data;
171
    struct pw_buffer *buf;
172
-   struct spa_data *d;
173
-   uint32_t req;
174
-   ssize_t nread;
175
+   struct spa_data *bd;
176
+   uint32_t req, index, size;
177
+   int32_t avail;
178
 
179
    if ((buf = pw_stream_dequeue_buffer(impl->stream)) == NULL) {
180
-       pw_log_debug("out of buffers: %m");
181
+       pw_log_warn("out of buffers: %m");
182
        return;
183
    }
184
 
185
-   d = &buf->buffer->datas0;
186
+   bd = &buf->buffer->datas0;
187
 
188
    if ((req = buf->requested * impl->frame_size) == 0)
189
        req = 4096 * impl->frame_size;
190
 
191
-   req = SPA_MIN(req, d->maxsize);
192
+   size = SPA_MIN(req, bd->maxsize);
193
+   size = SPA_ROUND_DOWN(size, impl->frame_size);
194
 
195
-   d->chunk->offset = 0;
196
-   d->chunk->stride = impl->frame_size;
197
-   d->chunk->size = SPA_MIN(req, impl->leftover_count);
198
-   memcpy(d->data, impl->leftover, d->chunk->size);
199
-   req -= d->chunk->size;
200
+   avail = spa_ringbuffer_get_read_index(&impl->ring, &index);
201
pipewire-0.3.79.tar.gz/src/modules/module-protocol-pulse/manager.c -> pipewire-0.3.80.tar.gz/src/modules/module-protocol-pulse/manager.c Changed
28
 
1
@@ -318,7 +318,7 @@
2
            case SPA_PARAM_EnumRoute:
3
                changed++;
4
                break;
5
-           case SPA_PARAM_Route:
6
+           default:
7
                break;
8
            }
9
            add_param(&o->pending_list, info->paramsi.seq, id, NULL);
10
@@ -435,7 +435,16 @@
11
                continue;
12
            info->paramsi.user = 0;
13
 
14
-           changed++;
15
+           switch (id) {
16
+           case SPA_PARAM_Props:
17
+           case SPA_PARAM_PropInfo:
18
+           case SPA_PARAM_Format:
19
+           case SPA_PARAM_EnumFormat:
20
+               changed++;
21
+               break;
22
+           default:
23
+               break;
24
+           }
25
            add_param(&o->pending_list, info->paramsi.seq, id, NULL);
26
            if (!(info->paramsi.flags & SPA_PARAM_INFO_READ))
27
                continue;
28
pipewire-0.3.79.tar.gz/src/modules/module-protocol-pulse/module.c -> pipewire-0.3.80.tar.gz/src/modules/module-protocol-pulse/module.c Changed
76
 
1
@@ -164,6 +164,28 @@
2
    }
3
 }
4
 
5
+static bool find_key(const char * const keys, const char *key)
6
+{
7
+   for (int i = 0; keysi != NULL; i++)
8
+       if (spa_streq(keysi, key))
9
+           return true;
10
+   return false;
11
+}
12
+
13
+static int module_args_check(struct pw_properties *props, const char * const valid_args)
14
+{
15
+   if (valid_args != NULL) {
16
+       const struct spa_dict_item *it;
17
+       spa_dict_for_each(it, &props->dict) {
18
+           if (!find_key(valid_args, it->key)) {
19
+               pw_log_warn("'%s' is not a valid module argument key", it->key);
20
+               return -EINVAL;
21
+           }
22
+       }
23
+   }
24
+   return 0;
25
+}
26
+
27
 int module_args_to_audioinfo_keys(struct impl *impl, struct pw_properties *props,
28
        const char *key_format, const char *key_rate,
29
        const char *key_channels, const char *key_channel_map,
30
@@ -299,6 +321,7 @@
31
 {
32
    const struct module_info *info;
33
    struct module *module;
34
+   int res;
35
 
36
    info = find_module_info(name);
37
    if (info == NULL) {
38
@@ -321,19 +344,19 @@
39
        return NULL;
40
 
41
    module->props = pw_properties_new(NULL, NULL);
42
-   if (module->props == NULL) {
43
-       module_free(module);
44
-       return NULL;
45
-   }
46
+   if (module->props == NULL)
47
+       goto error_free;
48
 
49
    if (args)
50
        module_args_add_props(module->props, args);
51
+   if ((res = module_args_check(module->props, info->valid_args)) < 0) {
52
+       errno = -res;
53
+       goto error_free;
54
+   }
55
 
56
-   int res = module->info->prepare(module);
57
-   if (res < 0) {
58
-       module_free(module);
59
+   if ((res = module->info->prepare(module)) < 0) {
60
        errno = -res;
61
-       return NULL;
62
+       goto error_free;
63
    }
64
 
65
    module->index = pw_map_insert_new(&impl->modules, module);
66
@@ -346,4 +369,9 @@
67
    module->index |= MODULE_FLAG;
68
 
69
    return module;
70
+
71
+error_free:
72
+   module_free(module);
73
+   return NULL;
74
+
75
 }
76
pipewire-0.3.79.tar.gz/src/modules/module-protocol-pulse/module.h -> pipewire-0.3.80.tar.gz/src/modules/module-protocol-pulse/module.h Changed
9
 
1
@@ -23,6 +23,7 @@
2
    int (*load) (struct module *module);
3
    int (*unload) (struct module *module);
4
 
5
+   const char* const *valid_args;
6
    const struct spa_dict *properties;
7
    size_t data_size;
8
 };
9
pipewire-0.3.79.tar.gz/src/modules/module-protocol-pulse/modules/module-pipe-sink.c -> pipewire-0.3.80.tar.gz/src/modules/module-protocol-pulse/modules/module-pipe-sink.c Changed
116
 
1
@@ -26,7 +26,7 @@
2
    struct pw_impl_module *mod;
3
 
4
    struct pw_properties *global_props;
5
-   struct pw_properties *capture_props;
6
+   struct pw_properties *stream_props;
7
 };
8
 
9
 static void module_destroy(void *data)
10
@@ -49,7 +49,7 @@
11
    char *args;
12
    size_t size;
13
 
14
-   pw_properties_setf(data->capture_props, "pulse.module.id",
15
+   pw_properties_setf(data->stream_props, "pulse.module.id",
16
            "%u", module->index);
17
 
18
    if ((f = open_memstream(&args, &size)) == NULL)
19
@@ -58,7 +58,7 @@
20
    fprintf(f, "{");
21
    pw_properties_serialize_dict(f, &data->global_props->dict, 0);
22
    fprintf(f, " \"stream.props\": {");
23
-   pw_properties_serialize_dict(f, &data->capture_props->dict, 0);
24
+   pw_properties_serialize_dict(f, &data->stream_props->dict, 0);
25
    fprintf(f, " } }");
26
    fclose(f);
27
 
28
@@ -86,7 +86,7 @@
29
        pw_impl_module_destroy(d->mod);
30
        d->mod = NULL;
31
    }
32
-   pw_properties_free(d->capture_props);
33
+   pw_properties_free(d->stream_props);
34
    pw_properties_free(d->global_props);
35
    return 0;
36
 }
37
@@ -100,7 +100,8 @@
38
                "format=<sample format> "
39
                "rate=<sample rate> "
40
                "channels=<number of channels> "
41
-               "channel_map=<channel map> " },
42
+               "channel_map=<channel map> "
43
+               "use_system_clock_for_timing=<yes or no> " },
44
    { PW_KEY_MODULE_VERSION, PACKAGE_VERSION },
45
 };
46
 
47
@@ -108,16 +109,17 @@
48
 {
49
    struct module_pipesink_data * const d = module->user_data;
50
    struct pw_properties * const props = module->props;
51
-   struct pw_properties *global_props = NULL, *capture_props = NULL;
52
+   struct pw_properties *global_props = NULL, *stream_props = NULL;
53
    struct spa_audio_info_raw info = { 0 };
54
    const char *str;
55
+   bool use_system_clock;
56
    int res = 0;
57
 
58
    PW_LOG_TOPIC_INIT(mod_topic);
59
 
60
    global_props = pw_properties_new(NULL, NULL);
61
-   capture_props = pw_properties_new(NULL, NULL);
62
-   if (!global_props || !capture_props) {
63
+   stream_props = pw_properties_new(NULL, NULL);
64
+   if (!global_props || !stream_props) {
65
        res = -EINVAL;
66
        goto out;
67
    }
68
@@ -133,31 +135,39 @@
69
    audioinfo_to_properties(&info, global_props);
70
 
71
    if ((str = pw_properties_get(props, "sink_name")) != NULL) {
72
-       pw_properties_set(capture_props, PW_KEY_NODE_NAME, str);
73
+       pw_properties_set(stream_props, PW_KEY_NODE_NAME, str);
74
        pw_properties_set(props, "sink_name", NULL);
75
    }
76
    if ((str = pw_properties_get(props, "sink_properties")) != NULL)
77
-       module_args_add_props(capture_props, str);
78
+       module_args_add_props(stream_props, str);
79
 
80
    if ((str = pw_properties_get(props, "file")) != NULL) {
81
        pw_properties_set(global_props, "pipe.filename", str);
82
        pw_properties_set(props, "file", NULL);
83
    }
84
-   if ((str = pw_properties_get(capture_props, PW_KEY_DEVICE_ICON_NAME)) == NULL)
85
-       pw_properties_set(capture_props, PW_KEY_DEVICE_ICON_NAME,
86
+   str = pw_properties_get(props, "use_system_clock_for_timing");
87
+   use_system_clock = str ? module_args_parse_bool(str) : false;
88
+
89
+   if ((str = pw_properties_get(stream_props, PW_KEY_NODE_GROUP)) == NULL) {
90
+       if (use_system_clock)
91
+           pw_properties_set(stream_props, PW_KEY_NODE_GROUP,
92
+               "pipewire.dummy");
93
+   }
94
+   if ((str = pw_properties_get(stream_props, PW_KEY_DEVICE_ICON_NAME)) == NULL)
95
+       pw_properties_set(stream_props, PW_KEY_DEVICE_ICON_NAME,
96
                "audio-card");
97
-   if ((str = pw_properties_get(capture_props, PW_KEY_NODE_NAME)) == NULL)
98
-       pw_properties_set(capture_props, PW_KEY_NODE_NAME,
99
+   if ((str = pw_properties_get(stream_props, PW_KEY_NODE_NAME)) == NULL)
100
+       pw_properties_set(stream_props, PW_KEY_NODE_NAME,
101
                "fifo_output");
102
 
103
    d->module = module;
104
    d->global_props = global_props;
105
-   d->capture_props = capture_props;
106
+   d->stream_props = stream_props;
107
 
108
    return 0;
109
 out:
110
    pw_properties_free(global_props);
111
-   pw_properties_free(capture_props);
112
+   pw_properties_free(stream_props);
113
    return res;
114
 }
115
 
116
pipewire-0.3.79.tar.gz/src/modules/module-protocol-pulse/modules/module-pipe-source.c -> pipewire-0.3.80.tar.gz/src/modules/module-protocol-pulse/modules/module-pipe-source.c Changed
102
 
1
@@ -26,7 +26,7 @@
2
    struct pw_impl_module *mod;
3
 
4
    struct pw_properties *global_props;
5
-   struct pw_properties *playback_props;
6
+   struct pw_properties *stream_props;
7
 };
8
 
9
 static void module_destroy(void *data)
10
@@ -49,7 +49,7 @@
11
    char *args;
12
    size_t size;
13
 
14
-   pw_properties_setf(data->playback_props, "pulse.module.id",
15
+   pw_properties_setf(data->stream_props, "pulse.module.id",
16
            "%u", module->index);
17
 
18
    if ((f = open_memstream(&args, &size)) == NULL)
19
@@ -58,7 +58,7 @@
20
    fprintf(f, "{");
21
    pw_properties_serialize_dict(f, &data->global_props->dict, 0);
22
    fprintf(f, " \"stream.props\": {");
23
-   pw_properties_serialize_dict(f, &data->playback_props->dict, 0);
24
+   pw_properties_serialize_dict(f, &data->stream_props->dict, 0);
25
    fprintf(f, " } }");
26
    fclose(f);
27
 
28
@@ -86,7 +86,7 @@
29
        pw_impl_module_destroy(d->mod);
30
        d->mod = NULL;
31
    }
32
-   pw_properties_free(d->playback_props);
33
+   pw_properties_free(d->stream_props);
34
    pw_properties_free(d->global_props);
35
    return 0;
36
 }
37
@@ -108,7 +108,7 @@
38
 {
39
    struct module_pipesrc_data * const d = module->user_data;
40
    struct pw_properties * const props = module->props;
41
-   struct pw_properties *global_props = NULL, *playback_props = NULL;
42
+   struct pw_properties *global_props = NULL, *stream_props = NULL;
43
    struct spa_audio_info_raw info = { 0 };
44
    const char *str;
45
    int res = 0;
46
@@ -116,8 +116,8 @@
47
    PW_LOG_TOPIC_INIT(mod_topic);
48
 
49
    global_props = pw_properties_new(NULL, NULL);
50
-   playback_props = pw_properties_new(NULL, NULL);
51
-   if (!global_props || !playback_props) {
52
+   stream_props = pw_properties_new(NULL, NULL);
53
+   if (!global_props || !stream_props) {
54
        res = -errno;
55
        goto out;
56
    }
57
@@ -133,31 +133,36 @@
58
    audioinfo_to_properties(&info, global_props);
59
 
60
    if ((str = pw_properties_get(props, "source_name")) != NULL) {
61
-       pw_properties_set(playback_props, PW_KEY_NODE_NAME, str);
62
+       pw_properties_set(stream_props, PW_KEY_NODE_NAME, str);
63
        pw_properties_set(props, "source_name", NULL);
64
    }
65
    if ((str = pw_properties_get(props, "source_properties")) != NULL)
66
-       module_args_add_props(playback_props, str);
67
+       module_args_add_props(stream_props, str);
68
 
69
    if ((str = pw_properties_get(props, "file")) != NULL) {
70
        pw_properties_set(global_props, "pipe.filename", str);
71
        pw_properties_set(props, "file", NULL);
72
    }
73
-   if ((str = pw_properties_get(playback_props, PW_KEY_DEVICE_ICON_NAME)) == NULL)
74
-       pw_properties_set(playback_props, PW_KEY_DEVICE_ICON_NAME,
75
+   if ((str = pw_properties_get(stream_props, PW_KEY_DEVICE_ICON_NAME)) == NULL)
76
+       pw_properties_set(stream_props, PW_KEY_DEVICE_ICON_NAME,
77
                "audio-input-microphone");
78
-   if ((str = pw_properties_get(playback_props, PW_KEY_NODE_NAME)) == NULL)
79
-       pw_properties_set(playback_props, PW_KEY_NODE_NAME,
80
+   if ((str = pw_properties_get(stream_props, PW_KEY_NODE_NAME)) == NULL)
81
+       pw_properties_set(stream_props, PW_KEY_NODE_NAME,
82
                "fifo_input");
83
 
84
+   if ((str = pw_properties_get(stream_props, PW_KEY_NODE_DRIVER)) == NULL)
85
+       pw_properties_set(stream_props, PW_KEY_NODE_DRIVER, "true");
86
+   if ((str = pw_properties_get(stream_props, PW_KEY_PRIORITY_DRIVER)) == NULL)
87
+       pw_properties_set(stream_props, PW_KEY_PRIORITY_DRIVER, "50000");
88
+
89
    d->module = module;
90
-   d->playback_props = playback_props;
91
+   d->stream_props = stream_props;
92
    d->global_props = global_props;
93
 
94
    return 0;
95
 out:
96
    pw_properties_free(global_props);
97
-   pw_properties_free(playback_props);
98
+   pw_properties_free(stream_props);
99
    return res;
100
 }
101
 
102
pipewire-0.3.79.tar.gz/src/modules/module-protocol-pulse/modules/module-roc-sink.c -> pipewire-0.3.80.tar.gz/src/modules/module-protocol-pulse/modules/module-roc-sink.c Changed
25
 
1
@@ -89,6 +89,15 @@
2
    return 0;
3
 }
4
 
5
+static const char* const valid_args = {
6
+   "sink_name",
7
+   "sink_properties",
8
+   "fec_code",
9
+   "remote_ip",
10
+   "remote_source_port",
11
+   "remote_repair_port",
12
+   NULL
13
+};
14
 static const struct spa_dict_item module_roc_sink_info = {
15
    { PW_KEY_MODULE_AUTHOR, "Sanchayan Maity <sanchayan@asymptotic.io>" },
16
    { PW_KEY_MODULE_DESCRIPTION, "roc sink" },
17
@@ -169,6 +178,7 @@
18
 
19
 DEFINE_MODULE_INFO(module_roc_sink) = {
20
    .name = "module-roc-sink",
21
+   .valid_args = valid_args,
22
    .prepare = module_roc_sink_prepare,
23
    .load = module_roc_sink_load,
24
    .unload = module_roc_sink_unload,
25
pipewire-0.3.79.tar.gz/src/modules/module-protocol-pulse/modules/module-roc-source.c -> pipewire-0.3.80.tar.gz/src/modules/module-protocol-pulse/modules/module-roc-source.c Changed
28
 
1
@@ -89,6 +89,18 @@
2
    return 0;
3
 }
4
 
5
+static const char* const valid_args = {
6
+   "source_name",
7
+   "source_properties",
8
+   "resampler_profile",
9
+   "fec_code",
10
+   "sess_latency_msec",
11
+   "local_ip",
12
+   "local_source_port",
13
+   "local_repair_port",
14
+   NULL
15
+};
16
+
17
 static const struct spa_dict_item module_roc_source_info = {
18
    { PW_KEY_MODULE_AUTHOR, "Sanchayan Maity <sanchayan@asymptotic.io>" },
19
    { PW_KEY_MODULE_DESCRIPTION, "roc source" },
20
@@ -178,6 +190,7 @@
21
 
22
 DEFINE_MODULE_INFO(module_roc_source) = {
23
    .name = "module-roc-source",
24
+   .valid_args = valid_args,
25
    .prepare = module_roc_source_prepare,
26
    .load = module_roc_source_load,
27
    .unload = module_roc_source_unload,
28
pipewire-0.3.79.tar.gz/src/modules/module-protocol-pulse/pulse-server.c -> pipewire-0.3.80.tar.gz/src/modules/module-protocol-pulse/pulse-server.c Changed
15
 
1
@@ -2505,8 +2505,12 @@
2
                allow_monitor = true;
3
            }
4
        }
5
-   } else if (index == SPA_ID_INVALID)
6
+   } else if (index != SPA_ID_INVALID) {
7
+       if (!sink)
8
+           allow_monitor = true;
9
+   } else {
10
        return NULL;
11
+   }
12
 
13
 
14
    spa_zero(sel);
15
pipewire-0.3.79.tar.gz/src/modules/module-protocol-pulse/stream.c -> pipewire-0.3.80.tar.gz/src/modules/module-protocol-pulse/stream.c Changed
17
 
1
@@ -216,11 +216,11 @@
2
    struct client *client = stream->client;
3
    struct impl *impl = client->impl;
4
    struct message *reply;
5
-   int missed;
6
+   int suppressed;
7
 
8
-   if ((missed = spa_ratelimit_test(&impl->rate_limit, stream->timestamp)) >= 0) {
9
-       pw_log_info("%s: UNDERFLOW channel:%u offset:%" PRIi64" (%d missed)",
10
-               client->name, stream->channel, offset, missed);
11
+   if ((suppressed = spa_ratelimit_test(&impl->rate_limit, stream->timestamp)) >= 0) {
12
+       pw_log_info("%s: UNDERFLOW channel:%u offset:%" PRIi64" (%d suppressed)",
13
+               client->name, stream->channel, offset, suppressed);
14
    }
15
 
16
    reply = message_alloc(impl, -1, 0);
17
pipewire-0.3.79.tar.gz/src/modules/module-pulse-tunnel.c -> pipewire-0.3.80.tar.gz/src/modules/module-pulse-tunnel.c Changed
67
 
1
@@ -393,23 +393,26 @@
2
        req = 4096 * impl->frame_size;
3
 
4
    size = SPA_MIN(bd->maxsize, req);
5
+   size = SPA_ROUND_DOWN(size, impl->frame_size);
6
 
7
    avail = spa_ringbuffer_get_read_index(&impl->ring, &index);
8
-   if (avail < (int32_t)size) {
9
+   if (avail < (int32_t)size)
10
        memset(bd->data, 0, size);
11
-   } else {
12
-       if (avail > (int32_t)RINGBUFFER_SIZE) {
13
-           avail = impl->target_buffer;
14
-           index += avail - impl->target_buffer;
15
-       } else {
16
-           update_rate(impl, avail / impl->frame_size);
17
-       }
18
+   if (avail > (int32_t)RINGBUFFER_SIZE) {
19
+       index += avail - impl->target_buffer;
20
+       avail = impl->target_buffer;
21
+   }
22
+   if (avail > 0) {
23
+       avail = SPA_ROUND_DOWN(avail, impl->frame_size);
24
+       update_rate(impl, avail / impl->frame_size);
25
+
26
+       avail = SPA_MIN(size, (uint32_t)avail);
27
        spa_ringbuffer_read_data(&impl->ring,
28
                impl->buffer, RINGBUFFER_SIZE,
29
                index & RINGBUFFER_MASK,
30
-               bd->data, size);
31
+               bd->data, avail);
32
 
33
-       index += size;
34
+       index += avail;
35
        spa_ringbuffer_read_update(&impl->ring, index);
36
    }
37
    bd->chunk->offset = 0;
38
@@ -669,22 +672,22 @@
39
 {
40
    struct impl *impl = userdata;
41
    struct timespec ts;
42
-   int missed;
43
+   int suppressed;
44
 
45
    clock_gettime(CLOCK_MONOTONIC, &ts);
46
-   if ((missed = spa_ratelimit_test(&impl->rate_limit, SPA_TIMESPEC_TO_NSEC(&ts))) >= 0)
47
-       pw_log_warn("underflow (%d missed)", missed);
48
+   if ((suppressed = spa_ratelimit_test(&impl->rate_limit, SPA_TIMESPEC_TO_NSEC(&ts))) >= 0)
49
+       pw_log_warn("underflow (%d suppressed)", suppressed);
50
    impl->resync = true;
51
 }
52
 static void stream_overflow_cb(pa_stream *s, void *userdata)
53
 {
54
    struct impl *impl = userdata;
55
    struct timespec ts;
56
-   int missed;
57
+   int suppressed;
58
 
59
    clock_gettime(CLOCK_MONOTONIC, &ts);
60
-   if ((missed = spa_ratelimit_test(&impl->rate_limit, SPA_TIMESPEC_TO_NSEC(&ts))) >= 0)
61
-       pw_log_warn("overflow (%d missed)", missed);
62
+   if ((suppressed = spa_ratelimit_test(&impl->rate_limit, SPA_TIMESPEC_TO_NSEC(&ts))) >= 0)
63
+       pw_log_warn("overflow (%d suppressed)", suppressed);
64
    impl->resync = true;
65
 }
66
 
67
pipewire-0.3.79.tar.gz/src/modules/module-rtp-source.c -> pipewire-0.3.80.tar.gz/src/modules/module-rtp-source.c Changed
10
 
1
@@ -40,7 +40,7 @@
2
  * The `rtp-source` module creates a PipeWire source that receives audio
3
  * and midi RTP packets.
4
  *
5
- * This module is usually loaded from the \page page_module_rtp_sap so that the
6
+ * This module is usually loaded from the \ref page_module_rtp_sap so that the
7
  * source.ip and source.port and format parameters matches that of the sender.
8
  *
9
  * ## Module Options
10
pipewire-0.3.79.tar.gz/src/pipewire/context.c -> pipewire-0.3.80.tar.gz/src/pipewire/context.c Changed
38
 
1
@@ -807,7 +807,7 @@
2
            spa_list_for_each(l, &p->links, input_link) {
3
                t = l->output->node;
4
 
5
-               if (!t->active || !l->prepared || (!t->driving && t->runnable))
6
+               if (!t->active || !l->prepared || (!t->driving && SPA_FLAG_IS_SET(t->checked, 1u<<direction)))
7
                    continue;
8
 
9
                pw_log_debug("  peer %p: '%s'", t, t->name);
10
@@ -820,7 +820,7 @@
11
            spa_list_for_each(l, &p->links, output_link) {
12
                t = l->input->node;
13
 
14
-               if (!t->active || !l->prepared || (!t->driving && t->runnable))
15
+               if (!t->active || !l->prepared || (!t->driving && SPA_FLAG_IS_SET(t->checked, 1u<<direction)))
16
                    continue;
17
 
18
                pw_log_debug("  peer %p: '%s'", t, t->name);
19
@@ -837,7 +837,7 @@
20
    if (node->link_group != NULL) {
21
        spa_list_for_each(t, nodes, sort_link) {
22
            if (t->exported || !t->active ||
23
-               SPA_FLAG_IS_SET(t->checked,  1u<<direction))
24
+               SPA_FLAG_IS_SET(t->checked, 1u<<direction))
25
                continue;
26
            if (!spa_streq(t->link_group, node->link_group))
27
                continue;
28
@@ -1496,6 +1496,9 @@
29
                n->rt.position->clock.rate = n->target_rate;
30
            }
31
            n->target_pending = false;
32
+       } else {
33
+           n->target_quantum = n->rt.position->clock.target_duration;
34
+           n->target_rate = n->rt.position->clock.target_rate;
35
        }
36
 
37
        pw_log_debug("%p: driver %p running:%d runnable:%d quantum:%u '%s'",
38
pipewire-0.3.79.tar.gz/src/pipewire/filter.c -> pipewire-0.3.80.tar.gz/src/pipewire/filter.c Changed
63
 
1
@@ -83,7 +83,8 @@
2
 #define PORT_Format    3
3
 #define PORT_Buffers   4
4
 #define PORT_Latency   5
5
-#define N_PORT_PARAMS  6
6
+#define PORT_Tag   6
7
+#define N_PORT_PARAMS  7
8
    struct spa_param_info paramsN_PORT_PARAMS;
9
 
10
    struct spa_io_buffers *io;
11
@@ -189,15 +190,17 @@
12
        return PORT_Buffers;
13
    case SPA_PARAM_Latency:
14
        return PORT_Latency;
15
+   case SPA_PARAM_Tag:
16
+       return PORT_Tag;
17
    default:
18
        return -1;
19
    }
20
 }
21
 
22
-static void fix_datatype(const struct spa_pod *param)
23
+static void fix_datatype(struct spa_pod *param)
24
 {
25
    const struct spa_pod_prop *pod_param;
26
-   const struct spa_pod *vals;
27
+   struct spa_pod *vals;
28
    uint32_t dataType, n_vals, choice;
29
 
30
    pod_param = spa_pod_find_prop(param, NULL, SPA_PARAM_BUFFERS_dataType);
31
@@ -237,11 +240,6 @@
32
    if (p == NULL)
33
        return NULL;
34
 
35
-   if (id == SPA_PARAM_Buffers && port != NULL &&
36
-       SPA_FLAG_IS_SET(port->flags, PW_FILTER_PORT_FLAG_MAP_BUFFERS) &&
37
-       port->direction == SPA_DIRECTION_INPUT)
38
-       fix_datatype(param);
39
-
40
    if (id == SPA_PARAM_ProcessLatency && port == NULL)
41
        spa_process_latency_parse(param, &impl->process_latency);
42
 
43
@@ -251,6 +249,11 @@
44
    memcpy(p->param, param, SPA_POD_SIZE(param));
45
    SPA_POD_OBJECT_ID(p->param) = id;
46
 
47
+   if (id == SPA_PARAM_Buffers && port != NULL &&
48
+       SPA_FLAG_IS_SET(port->flags, PW_FILTER_PORT_FLAG_MAP_BUFFERS) &&
49
+       port->direction == SPA_DIRECTION_INPUT)
50
+       fix_datatype(p->param);
51
+
52
    pw_log_debug("%p: port %p param id %d (%s)", impl, p, id,
53
            spa_debug_type_find_name(spa_type_param, id));
54
 
55
@@ -1849,6 +1852,7 @@
56
    p->paramsPORT_Format = SPA_PARAM_INFO(SPA_PARAM_Format, SPA_PARAM_INFO_WRITE);
57
    p->paramsPORT_Buffers = SPA_PARAM_INFO(SPA_PARAM_Buffers, 0);
58
    p->paramsPORT_Latency = SPA_PARAM_INFO(SPA_PARAM_Latency, SPA_PARAM_INFO_WRITE);
59
+   p->paramsPORT_Tag = SPA_PARAM_INFO(SPA_PARAM_Tag, SPA_PARAM_INFO_WRITE);
60
    p->info.params = p->params;
61
    p->info.n_params = N_PORT_PARAMS;
62
 
63
pipewire-0.3.79.tar.gz/src/pipewire/impl-link.c -> pipewire-0.3.80.tar.gz/src/pipewire/impl-link.c Changed
70
 
1
@@ -774,6 +774,7 @@
2
    pw_impl_port_emit_link_removed(this->input, this);
3
 
4
    pw_impl_port_recalc_latency(this->input);
5
+   pw_impl_port_recalc_tag(this->input);
6
 
7
    if ((res = pw_impl_port_use_buffers(port, mix, 0, NULL, 0)) < 0) {
8
        pw_log_warn("%p: port %p clear error %s", this, port, spa_strerror(res));
9
@@ -803,6 +804,7 @@
10
    pw_impl_port_emit_link_removed(this->output, this);
11
 
12
    pw_impl_port_recalc_latency(this->output);
13
+   pw_impl_port_recalc_tag(this->output);
14
 
15
    /* we don't clear output buffers when the link goes away. They will get
16
     * cleared when the node goes to suspend */
17
@@ -988,6 +990,14 @@
18
        pw_impl_port_recalc_latency(this->output);
19
 }
20
 
21
+static void input_port_tag_changed(void *data)
22
+{
23
+   struct impl *impl = data;
24
+   struct pw_impl_link *this = &impl->this;
25
+   if (!this->feedback)
26
+       pw_impl_port_recalc_tag(this->output);
27
+}
28
+
29
 static void output_port_latency_changed(void *data)
30
 {
31
    struct impl *impl = data;
32
@@ -996,11 +1006,20 @@
33
        pw_impl_port_recalc_latency(this->input);
34
 }
35
 
36
+static void output_port_tag_changed(void *data)
37
+{
38
+   struct impl *impl = data;
39
+   struct pw_impl_link *this = &impl->this;
40
+   if (!this->feedback)
41
+       pw_impl_port_recalc_tag(this->input);
42
+}
43
+
44
 static const struct pw_impl_port_events input_port_events = {
45
    PW_VERSION_IMPL_PORT_EVENTS,
46
    .param_changed = input_port_param_changed,
47
    .state_changed = input_port_state_changed,
48
    .latency_changed = input_port_latency_changed,
49
+   .tag_changed = input_port_tag_changed,
50
 };
51
 
52
 static const struct pw_impl_port_events output_port_events = {
53
@@ -1008,6 +1027,7 @@
54
    .param_changed = output_port_param_changed,
55
    .state_changed = output_port_state_changed,
56
    .latency_changed = output_port_latency_changed,
57
+   .tag_changed = output_port_tag_changed,
58
 };
59
 
60
 static void node_result(struct impl *impl, void *obj,
61
@@ -1395,6 +1415,8 @@
62
 
63
    pw_impl_port_recalc_latency(output);
64
    pw_impl_port_recalc_latency(input);
65
+   pw_impl_port_recalc_tag(output);
66
+   pw_impl_port_recalc_tag(input);
67
 
68
    if (impl->onode != impl->inode)
69
        this->peer = pw_node_peer_ref(impl->onode, impl->inode);
70
pipewire-0.3.79.tar.gz/src/pipewire/impl-node.c -> pipewire-0.3.80.tar.gz/src/pipewire/impl-node.c Changed
95
 
1
@@ -1105,9 +1105,9 @@
2
    struct pw_node_activation *na = driver->rt.target.activation;
3
    struct spa_io_clock *cl = &na->position.clock;
4
    enum spa_log_level level = SPA_LOG_LEVEL_DEBUG;
5
-   int missed;
6
+   int suppressed;
7
 
8
-   if ((missed = spa_ratelimit_test(&driver->rt.rate_limit, nsec)) >= 0)
9
+   if ((suppressed = spa_ratelimit_test(&driver->rt.rate_limit, nsec)) >= 0)
10
        level = SPA_LOG_LEVEL_INFO;
11
 
12
    spa_list_for_each(t, &driver->rt.target_list, link) {
13
@@ -1121,11 +1121,11 @@
14
            a->status == PW_NODE_ACTIVATION_AWAKE) {
15
            update_xrun_stats(a, nsec / 1000, 0);
16
 
17
-           pw_log(level, "(%s-%u) client too slow! rate:%u/%u pos:%"PRIu64" status:%s (%u missed)",
18
+           pw_log(level, "(%s-%u) client too slow! rate:%u/%u pos:%"PRIu64" status:%s (%u suppressed)",
19
                t->name, t->id,
20
                (uint32_t)(cl->rate.num * cl->duration), cl->rate.denom,
21
                cl->position, str_status(a->status),
22
-               missed);
23
+               suppressed);
24
        }
25
        pw_log_debug("(%s-%u) state:%p pending:%d/%d s:%"PRIu64" a:%"PRIu64" f:%"PRIu64
26
                " waiting:%"PRIu64" process:%"PRIu64" status:%s sync:%d",
27
@@ -1900,11 +1900,11 @@
28
    struct pw_node_activation *da = this->rt.driver_target.activation;
29
    struct spa_system *data_system = this->data_system;
30
    uint64_t nsec = get_time_ns(data_system);
31
-   int missed;
32
+   int suppressed;
33
 
34
    update_xrun_stats(a, trigger, delay);
35
 
36
-   if ((missed = spa_ratelimit_test(&this->rt.rate_limit, nsec)) >= 0) {
37
+   if ((suppressed = spa_ratelimit_test(&this->rt.rate_limit, nsec)) >= 0) {
38
        struct spa_fraction rate;
39
        if (da) {
40
            struct spa_io_clock *cl = &da->position.clock;
41
@@ -1914,11 +1914,11 @@
42
            rate = SPA_FRACTION(0,0);
43
        }
44
        pw_log_info("(%s-%d) XRun! rate:%u/%u count:%u time:%"PRIu64
45
-               " delay:%"PRIu64" max:%"PRIu64" (%d missed)",
46
+               " delay:%"PRIu64" max:%"PRIu64" (%d suppressed)",
47
                this->name, this->info.id,
48
                rate.num, rate.denom, a->xrun_count,
49
                trigger, delay, a->max_delay,
50
-               missed);
51
+               suppressed);
52
    }
53
 
54
    pw_impl_node_rt_emit_xrun(this);
55
@@ -2133,7 +2133,6 @@
56
            uint32_t id, uint32_t index, uint32_t next,
57
            struct spa_pod *param);
58
    int seq;
59
-   uint32_t count;
60
    unsigned int cache:1;
61
 };
62
 
63
@@ -2147,11 +2146,8 @@
64
        const struct spa_result_node_params *r = result;
65
        if (d->seq == seq) {
66
            d->callback(d->data, seq, r->id, r->index, r->next, r->param);
67
-           if (d->cache) {
68
-               if (d->count++ == 0)
69
-                   pw_param_add(&impl->pending_list, seq, r->id, NULL);
70
+           if (d->cache)
71
                pw_param_add(&impl->pending_list, seq, r->id, r->param);
72
-           }
73
        }
74
        break;
75
    }
76
@@ -2172,7 +2168,7 @@
77
 {
78
    int res;
79
    struct impl *impl = SPA_CONTAINER_OF(node, struct impl, this);
80
-   struct result_node_params_data user_data = { impl, data, callback, seq, 0, false };
81
+   struct result_node_params_data user_data = { impl, data, callback, seq, false };
82
    struct spa_hook listener;
83
    struct spa_param_info *pi;
84
    static const struct spa_node_events node_events = {
85
@@ -2226,6 +2222,9 @@
86
        user_data.cache = impl->cache_params &&
87
            (filter == NULL && index == 0 && max == UINT32_MAX);
88
 
89
+       if (user_data.cache)
90
+           pw_param_add(&impl->pending_list, seq, param_id, NULL);
91
+
92
        spa_zero(listener);
93
        spa_node_add_listener(node->node, &listener, &node_events, &user_data);
94
        res = spa_node_enum_params(node->node, seq,
95
pipewire-0.3.79.tar.gz/src/pipewire/impl-port.c -> pipewire-0.3.80.tar.gz/src/pipewire/impl-port.c Changed
201
 
1
@@ -9,6 +9,7 @@
2
 
3
 #include <spa/pod/parser.h>
4
 #include <spa/param/audio/format-utils.h>
5
+#include <spa/param/tag-utils.h>
6
 #include <spa/node/utils.h>
7
 #include <spa/utils/names.h>
8
 #include <spa/utils/string.h>
9
@@ -16,6 +17,7 @@
10
 #include <spa/debug/types.h>
11
 #include <spa/pod/filter.h>
12
 #include <spa/pod/dynamic.h>
13
+#include <spa/debug/pod.h>
14
 
15
 #include "pipewire/impl.h"
16
 #include "pipewire/private.h"
17
@@ -444,7 +446,7 @@
18
    struct pw_impl_port *this = data;
19
    struct spa_latency_info latency;
20
 
21
-   if (id != SPA_PARAM_Latency)
22
+   if (id != SPA_PARAM_Latency || param == NULL)
23
        return -EINVAL;
24
 
25
    if (spa_latency_parse(param, &latency) < 0)
26
@@ -464,6 +466,37 @@
27
 
28
    return 0;
29
 }
30
+static int process_tag_param(void *data, int seq,
31
+       uint32_t id, uint32_t index, uint32_t next, struct spa_pod *param)
32
+{
33
+   struct pw_impl_port *this = data;
34
+   struct spa_tag_info info;
35
+   struct spa_pod *old;
36
+   void *state = NULL;
37
+
38
+   if (id != SPA_PARAM_Tag || param == NULL)
39
+       return -EINVAL;
40
+   if (spa_tag_parse(param, &info, &state) < 0)
41
+       return 0;
42
+
43
+   old = this->taginfo.direction;
44
+
45
+   if (spa_tag_compare(old, param) == 0)
46
+       return 0;
47
+
48
+   pw_log_debug("port %p: got %s tag %p", this,
49
+           pw_direction_as_string(info.direction), param);
50
+   if (param)
51
+       pw_log_pod(SPA_LOG_LEVEL_DEBUG, param);
52
+
53
+   free(old);
54
+   this->taginfo.direction = spa_pod_copy(param);
55
+
56
+   if (info.direction == this->direction)
57
+       pw_impl_port_emit_tag_changed(this);
58
+
59
+   return 0;
60
+}
61
 
62
 static void update_info(struct pw_impl_port *port, const struct spa_port_info *info)
63
 {
64
@@ -514,6 +547,13 @@
65
                    pw_impl_port_for_each_param(port, 0, id, 0, UINT32_MAX,
66
                            NULL, process_latency_param, port);
67
                break;
68
+           case SPA_PARAM_Tag:
69
+               port->have_tag_param =
70
+                   SPA_FLAG_IS_SET(info->paramsi.flags, SPA_PARAM_INFO_WRITE);
71
+               if (port->node != NULL)
72
+                   pw_impl_port_for_each_param(port, 0, id, 0, UINT32_MAX,
73
+                           NULL, process_tag_param, port);
74
+               break;
75
            default:
76
                break;
77
            }
78
@@ -1061,6 +1101,7 @@
79
 
80
    pw_impl_port_for_each_param(port, 0, SPA_PARAM_IO, 0, 0, NULL, check_param_io, port);
81
    pw_impl_port_for_each_param(port, 0, SPA_PARAM_Latency, 0, 0, NULL, process_latency_param, port);
82
+   pw_impl_port_for_each_param(port, 0, SPA_PARAM_Tag, 0, 0, NULL, process_tag_param, port);
83
 
84
    nprops = pw_impl_node_get_properties(node);
85
    media_class = pw_properties_get(nprops, PW_KEY_MEDIA_CLASS);
86
@@ -1310,6 +1351,8 @@
87
 
88
    pw_param_clear(&impl->param_list, SPA_ID_INVALID);
89
    pw_param_clear(&impl->pending_list, SPA_ID_INVALID);
90
+   free(port->tagSPA_DIRECTION_INPUT);
91
+   free(port->tagSPA_DIRECTION_OUTPUT);
92
 
93
    pw_map_clear(&port->mix_port_map);
94
 
95
@@ -1325,7 +1368,6 @@
96
            uint32_t id, uint32_t index, uint32_t next,
97
            struct spa_pod *param);
98
    int seq;
99
-   uint32_t count;
100
    unsigned int cache:1;
101
 };
102
 
103
@@ -1339,11 +1381,8 @@
104
        const struct spa_result_node_params *r = result;
105
        if (d->seq == seq) {
106
            d->callback(d->data, seq, r->id, r->index, r->next, r->param);
107
-           if (d->cache) {
108
-               if (d->count++ == 0)
109
-                   pw_param_add(&impl->pending_list, seq, r->id, NULL);
110
+           if (d->cache)
111
                pw_param_add(&impl->pending_list, seq, r->id, r->param);
112
-           }
113
        }
114
        break;
115
    }
116
@@ -1365,7 +1404,7 @@
117
    int res;
118
    struct impl *impl = SPA_CONTAINER_OF(port, struct impl, this);
119
    struct pw_impl_node *node = port->node;
120
-   struct result_port_params_data user_data = { impl, data, callback, seq, 0, false };
121
+   struct result_port_params_data user_data = { impl, data, callback, seq, false };
122
    struct spa_hook listener;
123
    struct spa_param_info *pi;
124
    static const struct spa_node_events node_events = {
125
@@ -1419,6 +1458,9 @@
126
        user_data.cache = impl->cache_params &&
127
            (filter == NULL && index == 0 && max == UINT32_MAX);
128
 
129
+       if (user_data.cache)
130
+           pw_param_add(&impl->pending_list, seq, param_id, NULL);
131
+
132
        spa_zero(listener);
133
        spa_node_add_listener(node->node, &listener, &node_events, &user_data);
134
        res = spa_node_port_enum_params(node->node, seq,
135
@@ -1507,6 +1549,7 @@
136
    struct spa_pod_builder b = { 0 };
137
    uint8_t buffer1024;
138
    bool changed;
139
+   int count = 0;
140
 
141
    if (port->destroying)
142
        return 0;
143
@@ -1529,6 +1572,7 @@
144
                    latency.min_quantum, latency.max_quantum,
145
                    latency.min_rate, latency.max_rate,
146
                    latency.min_ns, latency.max_ns);
147
+           count++;
148
        }
149
    } else {
150
        spa_list_for_each(l, &port->links, input_link) {
151
@@ -1544,13 +1588,16 @@
152
                    latency.min_quantum, latency.max_quantum,
153
                    latency.min_rate, latency.max_rate,
154
                    latency.min_ns, latency.max_ns);
155
+           count++;
156
        }
157
    }
158
    spa_latency_info_combine_finish(&latency);
159
 
160
-   current = &port->latencylatency.direction;
161
-
162
-   changed = spa_latency_info_compare(current, &latency) != 0;
163
+   current = port->have_latency ? &port->latencylatency.direction : NULL;
164
+   if (current == NULL)
165
+       changed = count > 0;
166
+   else
167
+       changed = spa_latency_info_compare(current, &latency) != 0;
168
 
169
    pw_log_info("port %d: %s %s latency %f-%f %d-%d %"PRIu64"-%"PRIu64,
170
            port->info.id, changed ? "set" : "keep",
171
@@ -1562,16 +1609,90 @@
172
    if (!changed)
173
        return 0;
174
 
175
-   *current = latency;
176
+   port->latencylatency.direction = latency;
177
+   port->have_latency = count > 0;
178
 
179
    if (!port->have_latency_param)
180
        return 0;
181
 
182
    spa_pod_builder_init(&b, buffer, sizeof(buffer));
183
-   param = spa_latency_build(&b, SPA_PARAM_Latency, &latency);
184
+   param = port->have_latency ? spa_latency_build(&b, SPA_PARAM_Latency, &latency) : NULL;
185
    return pw_impl_port_set_param(port, SPA_PARAM_Latency, 0, param);
186
 }
187
 
188
+int pw_impl_port_recalc_tag(struct pw_impl_port *port)
189
+{
190
+   struct pw_impl_link *l;
191
+   struct pw_impl_port *other;
192
+   struct spa_pod *param, *tag, *old;
193
+   struct spa_pod_dynamic_builder b = { 0 };
194
+   struct spa_pod_frame f;
195
+   struct spa_tag_info info;
196
+   enum spa_direction direction;
197
+   uint8_t buffer1024;
198
+   int count = 0;
199
+   bool changed;
200
+
201
pipewire-0.3.79.tar.gz/src/pipewire/impl-port.h -> pipewire-0.3.80.tar.gz/src/pipewire/impl-port.h Changed
19
 
1
@@ -36,7 +36,7 @@
2
 
3
 /** Port events, use \ref pw_impl_port_add_listener */
4
 struct pw_impl_port_events {
5
-#define PW_VERSION_IMPL_PORT_EVENTS 2
6
+#define PW_VERSION_IMPL_PORT_EVENTS 3
7
    uint32_t version;
8
 
9
    /** The port is destroyed */
10
@@ -72,6 +72,8 @@
11
 
12
    /** latency changed. Since version 2 */
13
    void (*latency_changed) (void *data);
14
+   /** tag changed. Since version 3 */
15
+   void (*tag_changed) (void *data);
16
 };
17
 
18
 /** Create a new port
19
pipewire-0.3.79.tar.gz/src/pipewire/private.h -> pipewire-0.3.80.tar.gz/src/pipewire/private.h Changed
28
 
1
@@ -782,6 +782,7 @@
2
 #define pw_impl_port_emit_control_removed(p,c)     pw_impl_port_emit(p, control_removed, 0, c)
3
 #define pw_impl_port_emit_param_changed(p,i)       pw_impl_port_emit(p, param_changed, 1, i)
4
 #define pw_impl_port_emit_latency_changed(p)       pw_impl_port_emit(p, latency_changed, 2)
5
+#define pw_impl_port_emit_tag_changed(p)       pw_impl_port_emit(p, tag_changed, 3)
6
 
7
 #define PW_IMPL_PORT_IS_CONTROL(port)  SPA_FLAG_MASK((port)->flags, \
8
                        PW_IMPL_PORT_FLAG_BUFFERS|PW_IMPL_PORT_FLAG_CONTROL,\
9
@@ -846,6 +847,10 @@
10
    struct spa_latency_info latency2;   /**< latencies */
11
    unsigned int have_latency_param:1;
12
    unsigned int ignore_latency:1;
13
+   unsigned int have_latency:1;
14
+
15
+   unsigned int have_tag_param:1;
16
+   struct spa_pod *tag2;           /**< tags */
17
 
18
    void *owner_data;       /**< extra owner data */
19
    void *user_data;                /**< extra user data */
20
@@ -1221,6 +1226,7 @@
21
        struct spa_buffer **buffers, uint32_t n_buffers);
22
 
23
 int pw_impl_port_recalc_latency(struct pw_impl_port *port);
24
+int pw_impl_port_recalc_tag(struct pw_impl_port *port);
25
 
26
 /** Change the state of the node */
27
 int pw_impl_node_set_state(struct pw_impl_node *node, enum pw_node_state state);
28
pipewire-0.3.79.tar.gz/src/pipewire/stream.c -> pipewire-0.3.80.tar.gz/src/pipewire/stream.c Changed
62
 
1
@@ -113,7 +113,8 @@
2
 #define PORT_Format    3
3
 #define PORT_Buffers   4
4
 #define PORT_Latency   5
5
-#define N_PORT_PARAMS  6
6
+#define PORT_Tag   6
7
+#define N_PORT_PARAMS  7
8
    struct spa_param_info port_paramsN_PORT_PARAMS;
9
 
10
    struct spa_list param_list;
11
@@ -191,15 +192,17 @@
12
        return PORT_Buffers;
13
    case SPA_PARAM_Latency:
14
        return PORT_Latency;
15
+   case SPA_PARAM_Tag:
16
+       return PORT_Tag;
17
    default:
18
        return -1;
19
    }
20
 }
21
 
22
-static void fix_datatype(const struct spa_pod *param)
23
+static void fix_datatype(struct spa_pod *param)
24
 {
25
    const struct spa_pod_prop *pod_param;
26
-   const struct spa_pod *vals;
27
+   struct spa_pod *vals;
28
    uint32_t dataType, n_vals, choice;
29
 
30
    pod_param = spa_pod_find_prop(param, NULL, SPA_PARAM_BUFFERS_dataType);
31
@@ -239,17 +242,17 @@
32
    if (p == NULL)
33
        return NULL;
34
 
35
-   if (id == SPA_PARAM_Buffers &&
36
-       SPA_FLAG_IS_SET(impl->flags, PW_STREAM_FLAG_MAP_BUFFERS) &&
37
-       impl->direction == SPA_DIRECTION_INPUT)
38
-       fix_datatype(param);
39
-
40
    p->id = id;
41
    p->flags = flags;
42
    p->param = SPA_PTROFF(p, sizeof(struct param), struct spa_pod);
43
    memcpy(p->param, param, SPA_POD_SIZE(param));
44
    SPA_POD_OBJECT_ID(p->param) = id;
45
 
46
+   if (id == SPA_PARAM_Buffers &&
47
+       SPA_FLAG_IS_SET(impl->flags, PW_STREAM_FLAG_MAP_BUFFERS) &&
48
+       impl->direction == SPA_DIRECTION_INPUT)
49
+       fix_datatype(p->param);
50
+
51
    spa_list_append(&impl->param_list, &p->link);
52
 
53
    if ((idx = get_param_index(id)) != -1) {
54
@@ -1960,6 +1963,7 @@
55
    impl->port_paramsPORT_Format = SPA_PARAM_INFO(SPA_PARAM_Format, SPA_PARAM_INFO_WRITE);
56
    impl->port_paramsPORT_Buffers = SPA_PARAM_INFO(SPA_PARAM_Buffers, 0);
57
    impl->port_paramsPORT_Latency = SPA_PARAM_INFO(SPA_PARAM_Latency, SPA_PARAM_INFO_WRITE);
58
+   impl->port_paramsPORT_Tag = SPA_PARAM_INFO(SPA_PARAM_Tag, SPA_PARAM_INFO_WRITE);
59
    impl->port_info.props = &impl->port_props->dict;
60
    impl->port_info.params = impl->port_params;
61
    impl->port_info.n_params = N_PORT_PARAMS;
62
pipewire-0.3.79.tar.gz/src/pipewire/stream.h -> pipewire-0.3.80.tar.gz/src/pipewire/stream.h Changed
12
 
1
@@ -469,9 +469,7 @@
2
 /** Update the param exposed on the stream. */
3
 int
4
 pw_stream_update_params(struct pw_stream *stream,  /**< a \ref pw_stream */
5
-           const struct spa_pod **params,  /**< an array of params. The params should
6
-                             *  ideally contain parameters for doing
7
-                             *  buffer allocation. */
8
+           const struct spa_pod **params,  /**< an array of params. */
9
            uint32_t n_params       /**< number of elements in \a params */);
10
 
11
 /**
12
pipewire-0.3.79.tar.gz/src/tools/pw-cat.c -> pipewire-0.3.80.tar.gz/src/tools/pw-cat.c Changed
118
 
1
@@ -20,6 +20,7 @@
2
 #include <spa/param/audio/layout.h>
3
 #include <spa/param/audio/format-utils.h>
4
 #include <spa/param/audio/type-info.h>
5
+#include <spa/param/tag-utils.h>
6
 #include <spa/param/props.h>
7
 #include <spa/utils/result.h>
8
 #include <spa/utils/string.h>
9
@@ -1569,7 +1570,8 @@
10
 {
11
    struct data data = { 0, };
12
    struct pw_loop *l;
13
-   const struct spa_pod *params1;
14
+   const struct spa_pod *params2;
15
+   uint32_t n_params = 0;
16
    uint8_t buffer1024;
17
    struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buffer, sizeof(buffer));
18
    const char *prog;
19
@@ -1793,19 +1795,6 @@
20
    }
21
    data.filename = argvoptind++;
22
 
23
-   if (pw_properties_get(data.props, PW_KEY_MEDIA_TYPE) == NULL)
24
-       pw_properties_set(data.props, PW_KEY_MEDIA_TYPE, data.media_type);
25
-   if (pw_properties_get(data.props, PW_KEY_MEDIA_CATEGORY) == NULL)
26
-       pw_properties_set(data.props, PW_KEY_MEDIA_CATEGORY, data.media_category);
27
-   if (pw_properties_get(data.props, PW_KEY_MEDIA_ROLE) == NULL)
28
-       pw_properties_set(data.props, PW_KEY_MEDIA_ROLE, data.media_role);
29
-   if (pw_properties_get(data.props, PW_KEY_MEDIA_FILENAME) == NULL)
30
-       pw_properties_set(data.props, PW_KEY_MEDIA_FILENAME, data.filename);
31
-   if (pw_properties_get(data.props, PW_KEY_MEDIA_NAME) == NULL)
32
-       pw_properties_set(data.props, PW_KEY_MEDIA_NAME, data.filename);
33
-   if (pw_properties_get(data.props, PW_KEY_TARGET_OBJECT) == NULL)
34
-       pw_properties_set(data.props, PW_KEY_TARGET_OBJECT, data.target);
35
-
36
    /* make a main loop. If you already have another main loop, you can add
37
     * the fd of this pipewire mainloop to it. */
38
    data.loop = pw_main_loop_new(NULL);
39
@@ -1874,6 +1863,19 @@
40
    }
41
    ret = setup_properties(&data);
42
 
43
+   if (pw_properties_get(data.props, PW_KEY_MEDIA_TYPE) == NULL)
44
+       pw_properties_set(data.props, PW_KEY_MEDIA_TYPE, data.media_type);
45
+   if (pw_properties_get(data.props, PW_KEY_MEDIA_CATEGORY) == NULL)
46
+       pw_properties_set(data.props, PW_KEY_MEDIA_CATEGORY, data.media_category);
47
+   if (pw_properties_get(data.props, PW_KEY_MEDIA_ROLE) == NULL)
48
+       pw_properties_set(data.props, PW_KEY_MEDIA_ROLE, data.media_role);
49
+   if (pw_properties_get(data.props, PW_KEY_MEDIA_FILENAME) == NULL)
50
+       pw_properties_set(data.props, PW_KEY_MEDIA_FILENAME, data.filename);
51
+   if (pw_properties_get(data.props, PW_KEY_MEDIA_NAME) == NULL)
52
+       pw_properties_set(data.props, PW_KEY_MEDIA_NAME, data.filename);
53
+   if (pw_properties_get(data.props, PW_KEY_TARGET_OBJECT) == NULL)
54
+       pw_properties_set(data.props, PW_KEY_TARGET_OBJECT, data.target);
55
+
56
    switch (data.data_type) {
57
 #ifdef HAVE_PW_CAT_FFMPEG_INTEGRATION
58
    case TYPE_ENCODED:
59
@@ -1886,7 +1888,7 @@
60
        ret = av_codec_params_to_audio_info(&data, data.encoded.audio_stream->codecpar, &info);
61
        if (ret < 0)
62
            goto error_bad_file;
63
-       params0 = spa_format_audio_build(&b, SPA_PARAM_EnumFormat, &info);
64
+       paramsn_params++ = spa_format_audio_build(&b, SPA_PARAM_EnumFormat, &info);
65
        break;
66
    }
67
 #endif
68
@@ -1902,11 +1904,11 @@
69
        if (data.channelmap.n_channels)
70
            memcpy(info.position, data.channelmap.channels, data.channels * sizeof(int));
71
 
72
-       params0 = spa_format_audio_raw_build(&b, SPA_PARAM_EnumFormat, &info);
73
+       paramsn_params++ = spa_format_audio_raw_build(&b, SPA_PARAM_EnumFormat, &info);
74
        break;
75
    }
76
    case TYPE_MIDI:
77
-       params0 = spa_pod_builder_add_object(&b,
78
+       paramsn_params++ = spa_pod_builder_add_object(&b,
79
                SPA_TYPE_OBJECT_Format, SPA_PARAM_EnumFormat,
80
                SPA_FORMAT_mediaType,       SPA_POD_Id(SPA_MEDIA_TYPE_application),
81
                SPA_FORMAT_mediaSubtype,    SPA_POD_Id(SPA_MEDIA_SUBTYPE_control));
82
@@ -1928,10 +1930,25 @@
83
            memcpy(info.position, i->info.position,
84
                    info.channels * sizeof(uint32_t));
85
        }
86
-       params0 = spa_format_audio_dsd_build(&b, SPA_PARAM_EnumFormat, &info);
87
+       paramsn_params++ = spa_format_audio_dsd_build(&b, SPA_PARAM_EnumFormat, &info);
88
        break;
89
    }
90
    }
91
+   if (data.mode == mode_playback) {
92
+       struct spa_dict_item items64;
93
+       uint32_t i, n_items = 0;
94
+
95
+       for (i = 0; i < data.props->dict.n_items; i++) {
96
+           if (spa_strstartswith(data.props->dict.itemsi.key, "media."))
97
+               itemsn_items++ = data.props->dict.itemsi;
98
+       }
99
+       if (n_items > 0) {
100
+           struct spa_pod_frame f;
101
+           spa_tag_build_start(&b, &f, SPA_PARAM_Tag, SPA_DIRECTION_OUTPUT);
102
+           spa_tag_build_add_dict(&b, &SPA_DICT_INIT(items, n_items));
103
+           paramsn_params++ = spa_tag_build_end(&b, &f);
104
+       }
105
+   }
106
 
107
    data.stream = pw_stream_new(data.core, prog, data.props);
108
    data.props = NULL;
109
@@ -1955,7 +1972,7 @@
110
              PW_ID_ANY,
111
              flags |
112
              PW_STREAM_FLAG_MAP_BUFFERS,
113
-             params, 1);
114
+             params, n_params);
115
    if (ret < 0) {
116
        fprintf(stderr, "error: failed connect: %s\n", spa_strerror(ret));
117
        goto error_connect_fail;
118
pipewire-0.3.79.tar.gz/src/tools/pw-mon.c -> pipewire-0.3.80.tar.gz/src/tools/pw-mon.c Changed
201
 
1
@@ -55,6 +55,9 @@
2
 
3
    struct spa_list pending_list;
4
    struct spa_list global_list;
5
+
6
+   bool hide_params;
7
+   bool hide_props;
8
 };
9
 
10
 struct proxy_data {
11
@@ -152,7 +155,7 @@
12
    spa_list_append(&data->param_list, &p->link);
13
 }
14
 
15
-static void print_params(struct proxy_data *data, bool use_prefix)
16
+static void print_parameters(struct proxy_data *data, bool use_prefix)
17
 {
18
    struct param *p;
19
 
20
@@ -198,9 +201,12 @@
21
 
22
 #define MARK_CHANGE(f) (!!(print_mark && ((info)->change_mask & (f))))
23
 
24
-static void on_core_info(void *data, const struct pw_core_info *info)
25
+static void on_core_info(void *_data, const struct pw_core_info *info)
26
 {
27
-   bool print_all = true, print_mark = true;
28
+   struct proxy_data *data = _data;
29
+   bool hide_props, print_mark = true;
30
+
31
+   hide_props = data->data->hide_props;
32
 
33
    printf("\ttype: %s\n", PW_TYPE_INTERFACE_Core);
34
    printf("\tcookie: %u\n", info->cookie);
35
@@ -208,22 +214,22 @@
36
    printf("\thost-name: \"%s\"\n", info->host_name);
37
    printf("\tversion: \"%s\"\n", info->version);
38
    printf("\tname: \"%s\"\n", info->name);
39
-   if (print_all) {
40
+   if (!hide_props) {
41
        print_properties(info->props, MARK_CHANGE(PW_CORE_CHANGE_MASK_PROPS));
42
    }
43
 }
44
 
45
 static void module_event_info(void *_data, const struct pw_module_info *info)
46
 {
47
-        struct proxy_data *data = _data;
48
-   bool print_all, print_mark;
49
+   struct proxy_data *data = _data;
50
+   bool hide_props,  print_mark;
51
 
52
-   print_all = true;
53
-        if (data->info == NULL) {
54
+   hide_props = data->data->hide_props;
55
+   if (data->info == NULL) {
56
        printf("added:\n");
57
        print_mark = false;
58
    }
59
-        else {
60
+   else {
61
        printf("changed:\n");
62
        print_mark = true;
63
    }
64
@@ -237,7 +243,7 @@
65
    printf("\tname: \"%s\"\n", info->name);
66
    printf("\tfilename: \"%s\"\n", info->filename);
67
    printf("\targs: \"%s\"\n", info->args);
68
-   if (print_all) {
69
+   if (!hide_props) {
70
        print_properties(info->props, MARK_CHANGE(PW_MODULE_CHANGE_MASK_PROPS));
71
    }
72
 }
73
@@ -250,15 +256,17 @@
74
 static void print_node(struct proxy_data *data)
75
 {
76
    struct pw_node_info *info = data->info;
77
-   bool print_all, print_mark;
78
+   bool hide_params, hide_props, print_mark;
79
 
80
-   print_all = true;
81
-        if (data->first) {
82
+   hide_params = data->data->hide_params;
83
+   hide_props = data->data->hide_props;
84
+
85
+   if (data->first) {
86
        printf("added:\n");
87
        print_mark = false;
88
        data->first = false;
89
    }
90
-        else {
91
+   else {
92
        printf("changed:\n");
93
        print_mark = true;
94
    }
95
@@ -267,8 +275,8 @@
96
    printf("\tpermissions: "PW_PERMISSION_FORMAT"\n",
97
            PW_PERMISSION_ARGS(data->permissions));
98
    printf("\ttype: %s (version %d)\n", data->type, data->version);
99
-   if (print_all) {
100
-       print_params(data, MARK_CHANGE(PW_NODE_CHANGE_MASK_PARAMS));
101
+   if (!hide_params) {
102
+       print_parameters(data, MARK_CHANGE(PW_NODE_CHANGE_MASK_PARAMS));
103
        with_prefix(MARK_CHANGE(PW_NODE_CHANGE_MASK_INPUT_PORTS)) {
104
            printf("\tinput ports: %u/%u\n",
105
                info->n_input_ports, info->max_input_ports);
106
@@ -285,6 +293,9 @@
107
            printf(" \"%s\"\n", info->error);
108
        else
109
            printf("\n");
110
+   }
111
+
112
+   if (!hide_props) {
113
        print_properties(info->props, MARK_CHANGE(PW_NODE_CHANGE_MASK_PROPS));
114
    }
115
 }
116
@@ -323,15 +334,17 @@
117
 static void print_port(struct proxy_data *data)
118
 {
119
    struct pw_port_info *info = data->info;
120
-   bool print_all, print_mark;
121
+   bool hide_params, hide_props, print_mark;
122
+
123
+   hide_params = data->data->hide_params;
124
+   hide_props = data->data->hide_props;
125
 
126
-   print_all = true;
127
-        if (data->first) {
128
+   if (data->first) {
129
        printf("added:\n");
130
        print_mark = false;
131
        data->first = false;
132
    }
133
-        else {
134
+   else {
135
        printf("changed:\n");
136
        print_mark = true;
137
    }
138
@@ -342,8 +355,12 @@
139
    printf("\ttype: %s (version %d)\n", data->type, data->version);
140
 
141
    printf("\tdirection: \"%s\"\n", pw_direction_as_string(info->direction));
142
-   if (print_all) {
143
-       print_params(data, MARK_CHANGE(PW_PORT_CHANGE_MASK_PARAMS));
144
+
145
+   if (!hide_params) {
146
+       print_parameters(data, MARK_CHANGE(PW_PORT_CHANGE_MASK_PARAMS));
147
+   }
148
+
149
+   if (!hide_props) {
150
        print_properties(info->props, MARK_CHANGE(PW_PORT_CHANGE_MASK_PROPS));
151
    }
152
 }
153
@@ -382,14 +399,15 @@
154
 static void factory_event_info(void *_data, const struct pw_factory_info *info)
155
 {
156
         struct proxy_data *data = _data;
157
-   bool print_all, print_mark;
158
+   bool hide_props, print_mark;
159
+
160
+   hide_props = data->data->hide_props;
161
 
162
-   print_all = true;
163
-        if (data->info == NULL) {
164
+   if (data->info == NULL) {
165
        printf("added:\n");
166
        print_mark = false;
167
    }
168
-        else {
169
+   else {
170
        printf("changed:\n");
171
        print_mark = true;
172
    }
173
@@ -403,7 +421,7 @@
174
 
175
    printf("\tname: \"%s\"\n", info->name);
176
    printf("\tobject-type: %s/%d\n", info->type, info->version);
177
-   if (print_all) {
178
+   if (!hide_props) {
179
        print_properties(info->props, MARK_CHANGE(PW_FACTORY_CHANGE_MASK_PROPS));
180
    }
181
 }
182
@@ -416,14 +434,15 @@
183
 static void client_event_info(void *_data, const struct pw_client_info *info)
184
 {
185
         struct proxy_data *data = _data;
186
-   bool print_all, print_mark;
187
+   bool hide_props, print_mark;
188
+
189
+   hide_props = data->data->hide_props;
190
 
191
-   print_all = true;
192
-        if (data->info == NULL) {
193
+   if (data->info == NULL) {
194
        printf("added:\n");
195
        print_mark = false;
196
    }
197
-        else {
198
+   else {
199
        printf("changed:\n");
200
        print_mark = true;
201
pipewire-0.3.79.tar.gz/src/tools/pw-top.c -> pipewire-0.3.80.tar.gz/src/tools/pw-top.c Changed
12
 
1
@@ -721,8 +721,8 @@
2
 {
3
         fprintf(error ? stderr : stdout, "Usage:\n%s options\n\n"
4
        "Options:\n"
5
-       "  -b, --batch-mode              run in non-interactive batch_mode mode\n"
6
-       "  -n, --iterations = NUMBER             exit on maximum iterations NUMBER\n"
7
+       "  -b, --batch-mode              run in non-interactive batch mode\n"
8
+       "  -n, --iterations = NUMBER             exit after NUMBER batch iterations\n"
9
        "  -r, --remote                          Remote daemon name\n"
10
        "\n"
11
        "  -h, --help                            Show this help\n"
12
pipewire-0.3.80.tar.gz/subprojects/webrtc-audio-processing.wrap Added
10
 
1
@@ -0,0 +1,8 @@
2
+wrap-git
3
+directory = webrtc-audio-processing
4
+url = https://gitlab.freedesktop.org/pulseaudio/webrtc-audio-processing.git
5
+push-url = git@gitlab.freedesktop.org:pulseaudio/webrtc-audio-processing.git
6
+revision = v1.3
7
+
8
+provide
9
+dependency_names = webrtc-audio-coding-1, webrtc-audio-processing-1
10
pipewire-0.3.79.tar.gz/test/test-spa-utils.c -> pipewire-0.3.80.tar.gz/test/test-spa-utils.c Changed
11
 
1
@@ -125,7 +125,8 @@
2
    pwtest_int_eq(SPA_TYPE_OBJECT_Profiler, 0x4000a);
3
    pwtest_int_eq(SPA_TYPE_OBJECT_ParamLatency, 0x4000b);
4
    pwtest_int_eq(SPA_TYPE_OBJECT_ParamProcessLatency, 0x4000c);
5
-   pwtest_int_eq(_SPA_TYPE_OBJECT_LAST, 0x4000d);
6
+   pwtest_int_eq(SPA_TYPE_OBJECT_ParamTag, 0x4000d);
7
+   pwtest_int_eq(_SPA_TYPE_OBJECT_LAST, 0x4000e);
8
 
9
    pwtest_int_eq(SPA_TYPE_VENDOR_PipeWire, 0x02000000);
10
    pwtest_int_eq(SPA_TYPE_VENDOR_Other, 0x7f000000);
11