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
244
 
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
+                        l->port_link.dst_serial != p->serial))
202
+                       continue;
203
+                   queue_notify(c, NOTIFY_TYPE_CONNECT, l, 0, NULL);
204
+               }
205
                queue_notify(c, NOTIFY_TYPE_PORTREGISTRATION, p, 0, NULL);
206
+           }
207
        }
208
    }
209
 }
210
@@ -4141,7 +4188,11 @@
211
    pw_thread_loop_lock(c->context.loop);
212
    freeze_callbacks(c);
213
 
214
+   /* reemit buffer_frames */
215
+   c->buffer_frames = 0;
216
+
217
    pw_data_loop_start(c->loop);
218
+   c->active = true;
219
 
220
    if ((res = do_activate(c)) < 0)
221
        goto done;
222
@@ -4149,17 +4200,18 @@
223
    c->activation->pending_new_pos = true;
224
    c->activation->pending_sync = true;
225
 
226
-   c->active = true;
227
-
228
    spa_list_for_each(o, &c->context.objects, link) {
229
        if (o->type != INTERFACE_Port || o->port.port == NULL ||
230
            o->port.port->client != c || !o->port.port->valid)
231
            continue;
232
+       o->registered = 0;
233
        queue_notify(c, NOTIFY_TYPE_PORTREGISTRATION, o, 1, NULL);
234
    }
235
 done:
236
-   if (res < 0)
237
+   if (res < 0) {
238
+       c->active = false;
239
        pw_data_loop_stop(c->loop);
240
+   }
241
 
242
    pw_log_debug("%p: activate result:%d", c, res);
243
    thaw_callbacks(c);
244
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
580
 
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
+       case SPA_COMPRESS_OFFLOAD_DIRECTION_PLAYBACK:
202
+           n_play++;
203
+           emit_node(this, entry->d_name, device_nr, direction, cardinfo, i++);
204
+           break;
205
+       case SPA_COMPRESS_OFFLOAD_DIRECTION_CAPTURE:
206
+           /* TODO: Disabled for now. See the TODO in emit_node() for details. */
207
+#if 0
208
+           n_cap++;
209
+           emit_node(this, entry->d_name, device_nr, direction, cardinfo, i++);
210
+#endif
211
+           break;
212
+       }
213
+   }
214
+
215
+   this->n_capture = n_cap;
216
+   this->n_playback = n_play;
217
+   this->n_nodes = i;
218
+
219
+finish:
220
+   if (snd_dir != NULL)
221
+       closedir(snd_dir);
222
+   if (ctl_handle != NULL)
223
+       snd_ctl_close(ctl_handle);
224
+   return ret;
225
+
226
+errno_error:
227
+   ret = -errno;
228
+   goto finish;
229
+}
230
+
231
+static int emit_info(struct impl *this, bool full)
232
+{
233
+   int err = 0;
234
+   struct spa_dict_item items20;
235
+   uint32_t n_items = 0;
236
+   snd_ctl_t *ctl_hndl;
237
+   snd_ctl_card_info_t *info;
238
+   struct spa_device_info dinfo;
239
+   struct spa_param_info params2;
240
+   char path128;
241
+   char device_name200;
242
+   char device_desc200;
243
+
244
+   spa_log_debug(this->log, "open card %s", this->props.device);
245
+   if ((err = snd_ctl_open(&ctl_hndl, this->props.device, 0)) < 0) {
246
+       spa_log_error(this->log, "can't open control for card %s: %s",
247
+                     this->props.device, snd_strerror(err));
248
+       return err;
249
+   }
250
+
251
+   snd_ctl_card_info_alloca(&info);
252
+   if ((err = snd_ctl_card_info(ctl_hndl, info)) < 0) {
253
+       spa_log_error(this->log, "error hardware info: %s", snd_strerror(err));
254
+       goto finish;
255
+   }
256
+
257
+   dinfo = SPA_DEVICE_INFO_INIT();
258
+
259
+   dinfo.change_mask = SPA_DEVICE_CHANGE_MASK_PROPS;
260
+
261
+   snprintf(path, sizeof(path), "alsa:compressed:%s", snd_ctl_card_info_get_id(info));
262
+   snprintf(device_name, sizeof(device_name), "comprC%u", this->props.card_nr);
263
+   snprintf(device_desc, sizeof(device_desc), "Compress-Offload device (ALSA card %u)", this->props.card_nr);
264
+
265
+   ADD_DICT_ITEM(SPA_KEY_OBJECT_PATH,              path);
266
+   ADD_DICT_ITEM(SPA_KEY_DEVICE_API,               "alsa:compressed");
267
+   ADD_DICT_ITEM(SPA_KEY_DEVICE_NICK,              "alsa:compressed");
268
+   ADD_DICT_ITEM(SPA_KEY_DEVICE_NAME,              device_name);
269
+   ADD_DICT_ITEM(SPA_KEY_DEVICE_DESCRIPTION,       device_desc);
270
+   ADD_DICT_ITEM(SPA_KEY_MEDIA_CLASS,              "Audio/Device");
271
+   ADD_DICT_ITEM(SPA_KEY_API_ALSA_PATH,            (char *)this->props.device);
272
+   ADD_DICT_ITEM(SPA_KEY_API_ALSA_CARD_ID,         snd_ctl_card_info_get_id(info));
273
+   ADD_DICT_ITEM(SPA_KEY_API_ALSA_CARD_COMPONENTS, snd_ctl_card_info_get_components(info));
274
+   ADD_DICT_ITEM(SPA_KEY_API_ALSA_CARD_DRIVER,     snd_ctl_card_info_get_driver(info));
275
+   ADD_DICT_ITEM(SPA_KEY_API_ALSA_CARD_NAME,       snd_ctl_card_info_get_name(info));
276
+   ADD_DICT_ITEM(SPA_KEY_API_ALSA_CARD_LONGNAME,   snd_ctl_card_info_get_longname(info));
277
+   ADD_DICT_ITEM(SPA_KEY_API_ALSA_CARD_MIXERNAME,  snd_ctl_card_info_get_mixername(info));
278
+
279
+   dinfo.props = &SPA_DICT_INIT(items, n_items);
280
+
281
+   dinfo.change_mask |= SPA_DEVICE_CHANGE_MASK_PARAMS;
282
+   params0 = SPA_PARAM_INFO(SPA_PARAM_EnumProfile, SPA_PARAM_INFO_READ);
283
+   params1 = SPA_PARAM_INFO(SPA_PARAM_Profile, SPA_PARAM_INFO_READWRITE);
284
+   dinfo.n_params = SPA_N_ELEMENTS(params);
285
+   dinfo.params = params;
286
+
287
+   spa_device_emit_info(&this->hooks, &dinfo);
288
+
289
+finish:
290
+   spa_log_debug(this->log, "close card %s", this->props.device);
291
+   snd_ctl_close(ctl_hndl);
292
+   return err;
293
+}
294
+
295
+static int impl_add_listener(void *object,
296
+                             struct spa_hook *listener,
297
+                             const struct spa_device_events *events,
298
+                             void *data)
299
+{
300
+   struct impl *this = object;
301
+   struct spa_hook_list save;
302
+
303
+   spa_return_val_if_fail(this != NULL, -EINVAL);
304
+   spa_return_val_if_fail(events != NULL, -EINVAL);
305
+
306
+   spa_hook_list_isolate(&this->hooks, &save, listener, events, data);
307
+
308
+   if (events->info || events->object_info)
309
+       emit_info(this, true);
310
+
311
+   spa_hook_list_join(&this->hooks, &save);
312
+
313
+   return 0;
314
+}
315
+
316
+static int impl_sync(void *object, int seq)
317
+{
318
+   struct impl *this = object;
319
+
320
+   spa_return_val_if_fail(this != NULL, -EINVAL);
321
+
322
+   spa_device_emit_result(&this->hooks, seq, 0, 0, NULL);
323
+
324
+   return 0;
325
+}
326
+
327
+static struct spa_pod *build_profile(struct impl *this, struct spa_pod_builder *b,
328
+                                     uint32_t id, uint32_t index)
329
+{
330
+   struct spa_pod_frame f2;
331
+   const char *name, *desc;
332
+
333
+   switch (index) {
334
+   case 0:
335
+       name = "off";
336
+       desc = "Off";
337
+       break;
338
+   case 1:
339
+       name = "on";
340
+       desc = "On";
341
+       break;
342
+   default:
343
+       errno = EINVAL;
344
+       return NULL;
345
+   }
346
+
347
+   spa_pod_builder_push_object(b, &f0, SPA_TYPE_OBJECT_ParamProfile, id);
348
+   spa_pod_builder_add(b,
349
+       SPA_PARAM_PROFILE_index,   SPA_POD_Int(index),
350
+       SPA_PARAM_PROFILE_name, SPA_POD_String(name),
351
+       SPA_PARAM_PROFILE_description, SPA_POD_String(desc),
352
+       0);
353
+   if (index == 1) {
354
+       spa_pod_builder_prop(b, SPA_PARAM_PROFILE_classes, 0);
355
+       spa_pod_builder_push_struct(b, &f1);
356
+       if (this->n_capture) {
357
+           spa_pod_builder_add_struct(b,
358
+               SPA_POD_String("Audio/Source"),
359
+               SPA_POD_Int(this->n_capture));
360
+       }
361
+       if (this->n_playback) {
362
+           spa_pod_builder_add_struct(b,
363
+               SPA_POD_String("Audio/Sink"),
364
+               SPA_POD_Int(this->n_playback));
365
+       }
366
+       spa_pod_builder_pop(b, &f1);
367
+   }
368
+   return spa_pod_builder_pop(b, &f0);
369
+
370
+}
371
+
372
+static int impl_enum_params(void *object, int seq,
373
+                            uint32_t id, uint32_t start, uint32_t num,
374
+                            const struct spa_pod *filter)
375
+{
376
+   struct impl *this = object;
377
+   struct spa_pod *param;
378
+   struct spa_pod_builder b = { 0 };
379
+   uint8_t buffer1024;
380
+   struct spa_result_device_params result;
381
+   uint32_t count = 0;
382
+
383
+   spa_return_val_if_fail(this != NULL, -EINVAL);
384
+   spa_return_val_if_fail(num != 0, -EINVAL);
385
+
386
+   result.id = id;
387
+   result.next = start;
388
+      next:
389
+   result.index = result.next++;
390
+
391
+   spa_pod_builder_init(&b, buffer, sizeof(buffer));
392
+
393
+   switch (id) {
394
+   case SPA_PARAM_EnumProfile:
395
+   {
396
+       switch (result.index) {
397
+       case 0:
398
+       case 1:
399
+           param = build_profile(this, &b, id, result.index);
400
+           break;
401
+       default:
402
+           return 0;
403
+       }
404
+       break;
405
+   }
406
+   case SPA_PARAM_Profile:
407
+   {
408
+       switch (result.index) {
409
+       case 0:
410
+           param = build_profile(this, &b, id, this->profile);
411
+           break;
412
+       default:
413
+           return 0;
414
+       }
415
+       break;
416
+   }
417
+   default:
418
+       return -ENOENT;
419
+   }
420
+
421
+   if (spa_pod_filter(&b, &result.param, param, filter) < 0)
422
+       goto next;
423
+
424
+   spa_device_emit_result(&this->hooks, seq, 0,
425
+           SPA_RESULT_TYPE_DEVICE_PARAMS, &result);
426
+
427
+   if (++count != num)
428
+       goto next;
429
+
430
+   return 0;
431
+}
432
+
433
+static int impl_set_param(void *object,
434
+                          uint32_t id, uint32_t flags,
435
+                          const struct spa_pod *param)
436
+{
437
+   struct impl *this = object;
438
+   int res;
439
+
440
+   spa_return_val_if_fail(this != NULL, -EINVAL);
441
+
442
+   switch (id) {
443
+   case SPA_PARAM_Profile:
444
+   {
445
+       uint32_t idx;
446
+
447
+       if ((res = spa_pod_parse_object(param,
448
+                                       SPA_TYPE_OBJECT_ParamProfile, NULL,
449
+                                       SPA_PARAM_PROFILE_index, SPA_POD_Int(&idx))) < 0) {
450
+           spa_log_warn(this->log, "can't parse profile");
451
+           spa_debug_log_pod(this->log, SPA_LOG_LEVEL_DEBUG, 0, NULL, param);
452
+           return res;
453
+       }
454
+
455
+       set_profile(this, idx);
456
+       break;
457
+   }
458
+   default:
459
+       return -ENOENT;
460
+   }
461
+   return 0;
462
+}
463
+
464
+static const struct spa_device_methods impl_device = {
465
+   SPA_VERSION_DEVICE_METHODS,
466
+   .add_listener = impl_add_listener,
467
+   .sync = impl_sync,
468
+   .enum_params = impl_enum_params,
469
+   .set_param = impl_set_param,
470
+};
471
+
472
+static int impl_get_interface(struct spa_handle *handle, const char *type, void **interface)
473
+{
474
+   struct impl *this;
475
+
476
+   spa_return_val_if_fail(handle != NULL, -EINVAL);
477
+   spa_return_val_if_fail(interface != NULL, -EINVAL);
478
+
479
+   this = (struct impl *) handle;
480
+
481
+   if (spa_streq(type, SPA_TYPE_INTERFACE_Device))
482
+       *interface = &this->device;
483
+   else
484
+       return -ENOENT;
485
+
486
+   return 0;
487
+}
488
+
489
+static int impl_clear(struct spa_handle *handle)
490
+{
491
+   return 0;
492
+}
493
+
494
+static size_t impl_get_size(const struct spa_handle_factory *factory,
495
+                            const struct spa_dict *params)
496
+{
497
+   return sizeof(struct impl);
498
+}
499
+
500
+static int impl_init(const struct spa_handle_factory *factory,
501
+                     struct spa_handle *handle,
502
+                     const struct spa_dict *info,
503
+                     const struct spa_support *support,
504
+                     uint32_t n_support)
505
+{
506
+   struct impl *this;
507
+
508
+   spa_return_val_if_fail(factory != NULL, -EINVAL);
509
+   spa_return_val_if_fail(handle != NULL, -EINVAL);
510
+
511
+   handle->get_interface = impl_get_interface;
512
+   handle->clear = impl_clear;
513
+
514
+   this = (struct impl *) handle;
515
+
516
+   this->log = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_Log);
517
+   alsa_log_topic_init(this->log);
518
+
519
+   this->device.iface = SPA_INTERFACE_INIT(
520
+           SPA_TYPE_INTERFACE_Device,
521
+           SPA_VERSION_DEVICE,
522
+           &impl_device, this);
523
+   spa_hook_list_init(&this->hooks);
524
+
525
+   reset_props(&this->props);
526
+
527
+   snd_config_update_free_global();
528
+
529
+   if (info) {
530
+       uint32_t i;
531
+       for (i = 0; info && i < info->n_items; i++) {
532
+           const char *k = info->itemsi.key;
533
+           const char *s = info->itemsi.value;
534
+
535
+           if (spa_streq(k, SPA_KEY_API_ALSA_PATH)) {
536
+               snprintf(this->props.device, 64, "%s", s);
537
+               spa_log_debug(this->log, "using ALSA path \"%s\"", this->props.device);
538
+           } else if (spa_streq(k, SPA_KEY_API_ALSA_CARD)) {
539
+               long long card_nr = strtol(s, NULL, 10);
540
+               if ((card_nr >= 0) && (card_nr <= UINT_MAX)) {
541
+                   this->props.card_nr = card_nr;
542
+                   spa_log_debug(this->log, "using ALSA card number %u", this->props.card_nr);
543
+               } else
544
+                   spa_log_warn(this->log, "invalid ALSA card number \"%s\"; using default", s);
545
+           }
546
+       }
547
+   }
548
+
549
+   return 0;
550
+}
551
+
552
+static const struct spa_interface_info impl_interfaces = {
553
+   {SPA_TYPE_INTERFACE_Device,},
554
+};
555
+
556
+static int
557
+impl_enum_interface_info(const struct spa_handle_factory *factory,
558
+            const struct spa_interface_info **info,
559
+            uint32_t *index)
560
+{
561
+   spa_return_val_if_fail(factory != NULL, -EINVAL);
562
+   spa_return_val_if_fail(info != NULL, -EINVAL);
563
+   spa_return_val_if_fail(index != NULL, -EINVAL);
564
+
565
+   if (*index >= SPA_N_ELEMENTS(impl_interfaces))
566
+       return 0;
567
+
568
+   *info = &impl_interfaces(*index)++;
569
+   return 1;
570
+}
571
+
572
+const struct spa_handle_factory spa_alsa_compress_offload_device_factory = {
573
+   SPA_VERSION_HANDLE_FACTORY,
574
+   SPA_NAME_API_ALSA_COMPRESS_OFFLOAD_DEVICE,
575
+   NULL,
576
+   impl_get_size,
577
+   impl_init,
578
+   impl_enum_interface_info,
579
+};
580
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
237
 
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
 
202
-           if ((missed = spa_ratelimit_test(&state->rate_limit, current_time)) >= 0) {
203
+           if ((suppressed = spa_ratelimit_test(&state->rate_limit, current_time)) >= 0) {
204
                spa_log_lev(state->log, lev, "%s: follower delay:%ld target:%ld thr:%u, "
205
-                       "resync (%d missed)", state->props.device, delay,
206
-                       target, state->threshold, missed);
207
+                       "resync (%d suppressed)", state->props.device, delay,
208
+                       target, state->threshold, suppressed);
209
            }
210
 
211
            if (avail < target)
212
@@ -2676,7 +2700,7 @@
213
    struct state *state = source->data;
214
    snd_pcm_uframes_t avail, delay, target;
215
    uint64_t expire, current_time;
216
-   int res, missed;
217
+   int res, suppressed;
218
 
219
    if (SPA_UNLIKELY(state->disable_tsched)) {
220
        /* ALSA poll fds need to be "demangled" to know whether it's a real wakeup */
221
@@ -2748,12 +2772,12 @@
222
    if (!state->disable_tsched &&
223
            (state->next_time > current_time + SPA_NSEC_PER_SEC ||
224
             current_time > state->next_time + SPA_NSEC_PER_SEC)) {
225
-       if ((missed = spa_ratelimit_test(&state->rate_limit, current_time)) >= 0) {
226
+       if ((suppressed = spa_ratelimit_test(&state->rate_limit, current_time)) >= 0) {
227
            spa_log_error(state->log, "%s: impossible timeout %lu %lu %lu %"
228
-                   PRIu64" %"PRIu64" %"PRIi64" %d %"PRIi64" (%d missed)",
229
+                   PRIu64" %"PRIu64" %"PRIi64" %d %"PRIi64" (%d suppressed)",
230
                    state->props.device, avail, delay, target,
231
                    current_time, state->next_time, state->next_time - current_time,
232
-                   state->threshold, state->sample_count, missed);
233
+                   state->threshold, state->sample_count, suppressed);
234
        }
235
        state->next_time = current_time + state->threshold * 1e9 / state->rate;
236
    }
237
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
924
 
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
+
202
+   return errno != 0 ? -errno : num_dev;
203
+}
204
+
205
+static int check_pcm_device_availability(struct impl *this, struct card *card,
206
+                                         int *num_pcm_devices)
207
 {
208
    char pathPATH_MAX;
209
    char buf16;
210
@@ -282,15 +338,16 @@
211
    struct dirent *entry, *entry_pcm;
212
    int res;
213
 
214
-   res = get_num_pcm_devices(device->id);
215
+   res = get_num_pcm_devices(card->card_nr);
216
    if (res < 0) {
217
        spa_log_error(this->log, "Error finding PCM devices for ALSA card %u: %s",
218
-           (unsigned int)device->id, spa_strerror(res));
219
+           card->card_nr, spa_strerror(res));
220
        return res;
221
    }
222
-   *num_pcm = res;
223
+   *num_pcm_devices = res;
224
 
225
-   spa_log_debug(this->log, "card %u has %d pcm device(s)", (unsigned int)device->id, *num_pcm);
226
+   spa_log_debug(this->log, "card %u has %d PCM device(s)",
227
+                 card->card_nr, *num_pcm_devices);
228
 
229
    /*
230
     * Check if some pcm devices of the card are busy.  Check it via /proc, as we
231
@@ -304,25 +361,25 @@
232
 
233
    res = 0;
234
 
235
-   spa_scnprintf(path, sizeof(path), "/proc/asound/card%u", (unsigned int)device->id);
236
+   spa_scnprintf(path, sizeof(path), "/proc/asound/card%u", card->card_nr);
237
 
238
-   spa_autoptr(DIR) card = opendir(path);
239
-   if (card == NULL)
240
+   spa_autoptr(DIR) card_dir = opendir(path);
241
+   if (card_dir == NULL)
242
        goto done;
243
 
244
-   while ((errno = 0, entry = readdir(card)) != NULL) {
245
+   while ((errno = 0, entry = readdir(card_dir)) != NULL) {
246
        if (!(entry->d_type == DT_DIR &&
247
                spa_strstartswith(entry->d_name, "pcm")))
248
            continue;
249
 
250
        spa_scnprintf(path, sizeof(path), "pcmC%uD%s",
251
-               (unsigned int)device->id, entry->d_name+3);
252
+               card->card_nr, entry->d_name+3);
253
        if (check_device_pcm_class(path) < 0)
254
            continue;
255
 
256
        /* Check busy status */
257
        spa_scnprintf(path, sizeof(path), "/proc/asound/card%u/%s",
258
-               (unsigned int)device->id, entry->d_name);
259
+               card->card_nr, entry->d_name);
260
 
261
        spa_autoptr(DIR) pcm = opendir(path);
262
        if (pcm == NULL)
263
@@ -334,7 +391,7 @@
264
                continue;
265
 
266
            spa_scnprintf(path, sizeof(path), "/proc/asound/card%u/%s/%s/status",
267
-                   (unsigned int)device->id, entry->d_name, entry_pcm->d_name);
268
+                   card->card_nr, entry->d_name, entry_pcm->d_name);
269
 
270
            spa_autoptr(FILE) f = fopen(path, "re");
271
            if (f == NULL)
272
@@ -344,12 +401,12 @@
273
 
274
            if (!spa_strstartswith(buf, "closed")) {
275
                spa_log_debug(this->log, "card %u pcm device %s busy",
276
-                       (unsigned int)device->id, entry->d_name);
277
+                       card->card_nr, entry->d_name);
278
                res = -EBUSY;
279
                goto done;
280
            }
281
            spa_log_debug(this->log, "card %u pcm device %s free",
282
-                   (unsigned int)device->id, entry->d_name);
283
+                   card->card_nr, entry->d_name);
284
        }
285
        if (errno != 0)
286
            goto done;
287
@@ -360,158 +417,249 @@
288
 done:
289
    if (errno != 0) {
290
        spa_log_info(this->log, "card %u: failed to find busy status (%s)",
291
-               (unsigned int)device->id, spa_strerror(-errno));
292
+               card->card_nr, spa_strerror(-errno));
293
    }
294
 
295
    return res;
296
 }
297
 
298
-static int emit_object_info(struct impl *this, struct device *device)
299
+static int check_compress_offload_device_availability(struct impl *this, struct card *card,
300
+                                                      int *num_compress_offload_devices)
301
+{
302
+   int res;
303
+
304
+   res = get_num_compress_offload_devices(card->card_nr);
305
+   if (res < 0) {
306
+       spa_log_error(this->log, "Error finding Compress-Offload devices for ALSA card %u: %s",
307
+           card->card_nr, spa_strerror(res));
308
+       return res;
309
+   }
310
+   *num_compress_offload_devices = res;
311
+
312
+   spa_log_debug(this->log, "card %u has %d Compress-Offload device(s)",
313
+                 card->card_nr, *num_compress_offload_devices);
314
+
315
+   return 0;
316
+}
317
+
318
+static int emit_added_object_info(struct impl *this, struct card *card)
319
 {
320
-   struct spa_device_object_info info;
321
-   uint32_t id = device->id;
322
-   struct udev_device *dev = device->dev;
323
+   char path32;
324
+   int res, num_pcm_devices, num_compress_offload_devices;
325
    const char *str;
326
-   char path32, *cn = NULL, *cln = NULL;
327
-   struct spa_dict_item items25;
328
-   uint32_t n_items = 0;
329
-   int res, pcm;
330
+   struct udev_device *udev_device = card->udev_device;
331
 
332
    /*
333
     * inotify close events under /dev/snd must not be emitted, except after setting
334
-    * device->emitted to true. alsalib functions can be used after that.
335
+    * card->emitted to true. alsalib functions can be used after that.
336
     */
337
 
338
-   snprintf(path, sizeof(path), "hw:%u", id);
339
+   snprintf(path, sizeof(path), "hw:%u", card->card_nr);
340
 
341
-   if ((res = check_device_available(this, device, &pcm)) < 0)
342
+   if ((res = check_pcm_device_availability(this, card, &num_pcm_devices)) < 0)
343
+       return res;
344
+   if ((res = check_compress_offload_device_availability(this, card, &num_compress_offload_devices)) < 0)
345
        return res;
346
-   if (pcm == 0) {
347
-       spa_log_debug(this->log, "no pcm devices for %s", path);
348
-       device->ignored = true;
349
+
350
+   if ((num_pcm_devices == 0) && (num_compress_offload_devices == 0)) {
351
+       spa_log_debug(this->log, "no PCM and no Compress-Offload devices for %s", path);
352
+       card->ignored = true;
353
        return -ENODEV;
354
    }
355
 
356
-   spa_log_debug(this->log, "emitting card %s", path);
357
-   device->emitted = true;
358
-
359
-   info = SPA_DEVICE_OBJECT_INFO_INIT();
360
-
361
-   info.type = SPA_TYPE_INTERFACE_Device;
362
-   info.factory_name = this->use_acp ?
363
-       SPA_NAME_API_ALSA_ACP_DEVICE :
364
-       SPA_NAME_API_ALSA_PCM_DEVICE;
365
-   info.change_mask = SPA_DEVICE_OBJECT_CHANGE_MASK_FLAGS |
366
-       SPA_DEVICE_OBJECT_CHANGE_MASK_PROPS;
367
-   info.flags = 0;
368
-
369
-   itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_DEVICE_ENUM_API, "udev");
370
-   itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_DEVICE_API,  "alsa");
371
-   itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_MEDIA_CLASS, "Audio/Device");
372
-   itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_API_ALSA_PATH, path);
373
-   itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_API_ALSA_CARD, path+3);
374
-   if (snd_card_get_name(id, &cn) >= 0)
375
-       itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_API_ALSA_CARD_NAME, cn);
376
-   if (snd_card_get_longname(id, &cln) >= 0)
377
-       itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_API_ALSA_CARD_LONGNAME, cln);
378
-
379
-   if ((str = udev_device_get_property_value(dev, "ACP_NAME")) && *str)
380
-       itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_DEVICE_NAME, str);
381
-
382
-   if ((str = udev_device_get_property_value(dev, "ACP_PROFILE_SET")) && *str)
383
-       itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_DEVICE_PROFILE_SET, str);
384
-
385
-   if ((str = udev_device_get_property_value(dev, "SOUND_CLASS")) && *str)
386
-       itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_DEVICE_CLASS, str);
387
-
388
-   if ((str = udev_device_get_property_value(dev, "USEC_INITIALIZED")) && *str)
389
-       itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_DEVICE_PLUGGED_USEC, str);
390
-
391
-   str = udev_device_get_property_value(dev, "ID_PATH");
392
-   if (!(str && *str))
393
-       str = udev_device_get_syspath(dev);
394
-   if (str && *str) {
395
-       itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_DEVICE_BUS_PATH, str);
396
-   }
397
-   if ((str = udev_device_get_devpath(dev)) && *str) {
398
-       itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_DEVICE_SYSFS_PATH, str);
399
-   }
400
-   if ((str = udev_device_get_property_value(dev, "ID_ID")) && *str) {
401
-       itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_DEVICE_BUS_ID, str);
402
-   }
403
-   if ((str = udev_device_get_property_value(dev, "ID_BUS")) && *str) {
404
-       itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_DEVICE_BUS, str);
405
-   }
406
-   if ((str = udev_device_get_property_value(dev, "SUBSYSTEM")) && *str) {
407
-       itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_DEVICE_SUBSYSTEM, str);
408
-   }
409
-   if ((str = udev_device_get_property_value(dev, "ID_VENDOR_ID")) && *str) {
410
-       int32_t val;
411
-       if (spa_atoi32(str, &val, 16)) {
412
-           char *dec = alloca(12); /* 0xffffffff is max */
413
-           snprintf(dec, 12, "0x%04x", val);
414
-           itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_DEVICE_VENDOR_ID, dec);
415
+   card->emitted = true;
416
+
417
+   if (num_pcm_devices > 0) {
418
+       struct spa_device_object_info info;
419
+       char *cn = NULL, *cln = NULL;
420
+       struct spa_dict_item items25;
421
+       unsigned int n_items = 0;
422
+
423
+       card->pcm_device_id = calc_pcm_device_id(card);
424
+
425
+       spa_log_debug(this->log, "emitting ACP/PCM device interface for card %s; "
426
+                     "using local alsa-udev object ID %" PRIu32, path, card->pcm_device_id);
427
+
428
+       info = SPA_DEVICE_OBJECT_INFO_INIT();
429
+
430
+       info.type = SPA_TYPE_INTERFACE_Device;
431
+       info.factory_name = this->use_acp ?
432
+           SPA_NAME_API_ALSA_ACP_DEVICE :
433
+           SPA_NAME_API_ALSA_PCM_DEVICE;
434
+       info.change_mask = SPA_DEVICE_OBJECT_CHANGE_MASK_FLAGS |
435
+           SPA_DEVICE_OBJECT_CHANGE_MASK_PROPS;
436
+       info.flags = 0;
437
+
438
+       itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_DEVICE_ENUM_API, "udev");
439
+       itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_DEVICE_API,  "alsa");
440
+       itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_MEDIA_CLASS, "Audio/Device");
441
+       itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_API_ALSA_PATH, path);
442
+       itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_API_ALSA_CARD, path+3);
443
+       if (snd_card_get_name(card->card_nr, &cn) >= 0)
444
+           itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_API_ALSA_CARD_NAME, cn);
445
+       if (snd_card_get_longname(card->card_nr, &cln) >= 0)
446
+           itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_API_ALSA_CARD_LONGNAME, cln);
447
+
448
+       if ((str = udev_device_get_property_value(udev_device, "ACP_NAME")) && *str)
449
+           itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_DEVICE_NAME, str);
450
+
451
+       if ((str = udev_device_get_property_value(udev_device, "ACP_PROFILE_SET")) && *str)
452
+           itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_DEVICE_PROFILE_SET, str);
453
+
454
+       if ((str = udev_device_get_property_value(udev_device, "SOUND_CLASS")) && *str)
455
+           itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_DEVICE_CLASS, str);
456
+
457
+       if ((str = udev_device_get_property_value(udev_device, "USEC_INITIALIZED")) && *str)
458
+           itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_DEVICE_PLUGGED_USEC, str);
459
+
460
+       str = udev_device_get_property_value(udev_device, "ID_PATH");
461
+       if (!(str && *str))
462
+           str = udev_device_get_syspath(udev_device);
463
+       if (str && *str) {
464
+           itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_DEVICE_BUS_PATH, str);
465
        }
466
-   }
467
-   str = udev_device_get_property_value(dev, "ID_VENDOR_FROM_DATABASE");
468
-   if (!(str && *str)) {
469
-       str = udev_device_get_property_value(dev, "ID_VENDOR_ENC");
470
+       if ((str = udev_device_get_devpath(udev_device)) && *str) {
471
+           itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_DEVICE_SYSFS_PATH, str);
472
+       }
473
+       if ((str = udev_device_get_property_value(udev_device, "ID_ID")) && *str) {
474
+           itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_DEVICE_BUS_ID, str);
475
+       }
476
+       if ((str = udev_device_get_property_value(udev_device, "ID_BUS")) && *str) {
477
+           itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_DEVICE_BUS, str);
478
+       }
479
+       if ((str = udev_device_get_property_value(udev_device, "SUBSYSTEM")) && *str) {
480
+           itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_DEVICE_SUBSYSTEM, str);
481
+       }
482
+       if ((str = udev_device_get_property_value(udev_device, "ID_VENDOR_ID")) && *str) {
483
+           int32_t val;
484
+           if (spa_atoi32(str, &val, 16)) {
485
+               char *dec = alloca(12); /* 0xffffffff is max */
486
+               snprintf(dec, 12, "0x%04x", val);
487
+               itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_DEVICE_VENDOR_ID, dec);
488
+           }
489
+       }
490
+       str = udev_device_get_property_value(udev_device, "ID_VENDOR_FROM_DATABASE");
491
        if (!(str && *str)) {
492
-           str = udev_device_get_property_value(dev, "ID_VENDOR");
493
-       } else {
494
-           char *t = alloca(strlen(str) + 1);
495
-           unescape(str, t);
496
-           str = t;
497
+           str = udev_device_get_property_value(udev_device, "ID_VENDOR_ENC");
498
+           if (!(str && *str)) {
499
+               str = udev_device_get_property_value(udev_device, "ID_VENDOR");
500
+           } else {
501
+               char *t = alloca(strlen(str) + 1);
502
+               unescape(str, t);
503
+               str = t;
504
+           }
505
        }
506
-   }
507
-   if (str && *str) {
508
-       itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_DEVICE_VENDOR_NAME, str);
509
-   }
510
-   if ((str = udev_device_get_property_value(dev, "ID_MODEL_ID")) && *str) {
511
-       int32_t val;
512
-       if (spa_atoi32(str, &val, 16)) {
513
-           char *dec = alloca(12); /* 0xffffffff is max */
514
-           snprintf(dec, 12, "0x%04x", val);
515
-           itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_DEVICE_PRODUCT_ID, dec);
516
+       if (str && *str) {
517
+           itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_DEVICE_VENDOR_NAME, str);
518
        }
519
-   }
520
-   str = udev_device_get_property_value(dev, "ID_MODEL_FROM_DATABASE");
521
-   if (!(str && *str)) {
522
-       str = udev_device_get_property_value(dev, "ID_MODEL_ENC");
523
+       if ((str = udev_device_get_property_value(udev_device, "ID_MODEL_ID")) && *str) {
524
+           int32_t val;
525
+           if (spa_atoi32(str, &val, 16)) {
526
+               char *dec = alloca(12); /* 0xffffffff is max */
527
+               snprintf(dec, 12, "0x%04x", val);
528
+               itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_DEVICE_PRODUCT_ID, dec);
529
+           }
530
+       }
531
+       str = udev_device_get_property_value(udev_device, "ID_MODEL_FROM_DATABASE");
532
        if (!(str && *str)) {
533
-           str = udev_device_get_property_value(dev, "ID_MODEL");
534
-       } else {
535
-           char *t = alloca(strlen(str) + 1);
536
-           unescape(str, t);
537
-           str = t;
538
+           str = udev_device_get_property_value(udev_device, "ID_MODEL_ENC");
539
+           if (!(str && *str)) {
540
+               str = udev_device_get_property_value(udev_device, "ID_MODEL");
541
+           } else {
542
+               char *t = alloca(strlen(str) + 1);
543
+               unescape(str, t);
544
+               str = t;
545
+           }
546
        }
547
-   }
548
-   if (str && *str)
549
-       itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_DEVICE_PRODUCT_NAME, str);
550
+       if (str && *str)
551
+           itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_DEVICE_PRODUCT_NAME, str);
552
 
553
-   if ((str = udev_device_get_property_value(dev, "ID_SERIAL")) && *str) {
554
-       itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_DEVICE_SERIAL, str);
555
-   }
556
-   if ((str = udev_device_get_property_value(dev, "SOUND_FORM_FACTOR")) && *str) {
557
-       itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_DEVICE_FORM_FACTOR, str);
558
+       if ((str = udev_device_get_property_value(udev_device, "ID_SERIAL")) && *str) {
559
+           itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_DEVICE_SERIAL, str);
560
+       }
561
+       if ((str = udev_device_get_property_value(udev_device, "SOUND_FORM_FACTOR")) && *str) {
562
+           itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_DEVICE_FORM_FACTOR, str);
563
+       }
564
+       info.props = &SPA_DICT_INIT(items, n_items);
565
+
566
+       spa_log_debug(this->log, "interface information:");
567
+       spa_debug_log_dict(this->log, SPA_LOG_LEVEL_DEBUG, 2, info.props);
568
+
569
+       spa_device_emit_object_info(&this->hooks, card->pcm_device_id, &info);
570
+       free(cn);
571
+       free(cln);
572
+   } else {
573
+       card->pcm_device_id = ID_DEVICE_NOT_SUPPORTED;
574
    }
575
-   info.props = &SPA_DICT_INIT(items, n_items);
576
 
577
-   spa_device_emit_object_info(&this->hooks, id, &info);
578
-   free(cn);
579
-   free(cln);
580
+   if (num_compress_offload_devices > 0) {
581
+       struct spa_device_object_info info;
582
+       struct spa_dict_item items11;
583
+       unsigned int n_items = 0;
584
+       char device_name200;
585
+       char device_desc200;
586
+
587
+       card->compress_offload_device_id = calc_compress_offload_device_id(card);
588
+
589
+       spa_log_debug(this->log, "emitting Compress-Offload device interface for card %s; "
590
+                     "using local alsa-udev object ID %" PRIu32, path, card->compress_offload_device_id);
591
+
592
+       info = SPA_DEVICE_OBJECT_INFO_INIT();
593
+
594
+       info.type = SPA_TYPE_INTERFACE_Device;
595
+       info.factory_name = SPA_NAME_API_ALSA_COMPRESS_OFFLOAD_DEVICE;
596
+       info.change_mask = SPA_DEVICE_OBJECT_CHANGE_MASK_FLAGS |
597
+           SPA_DEVICE_OBJECT_CHANGE_MASK_PROPS;
598
+       info.flags = 0;
599
+
600
+       snprintf(device_name, sizeof(device_name), "comprC%u", card->card_nr);
601
+       snprintf(device_desc, sizeof(device_desc), "Compress-Offload device (ALSA card %u)", card->card_nr);
602
+
603
+       itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_DEVICE_ENUM_API, "udev");
604
+       itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_DEVICE_API,  "alsa:compressed");
605
+       itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_DEVICE_NAME, device_name);
606
+       itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_DEVICE_DESCRIPTION, device_desc);
607
+       itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_MEDIA_CLASS, "Audio/Device");
608
+       itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_API_ALSA_PATH, path);
609
+       itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_API_ALSA_CARD, path+3);
610
+
611
+       if ((str = udev_device_get_property_value(udev_device, "USEC_INITIALIZED")) && *str)
612
+           itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_DEVICE_PLUGGED_USEC, str);
613
+
614
+       str = udev_device_get_property_value(udev_device, "ID_PATH");
615
+       if (!(str && *str))
616
+           str = udev_device_get_syspath(udev_device);
617
+       if (str && *str) {
618
+           itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_DEVICE_BUS_PATH, str);
619
+       }
620
+       if ((str = udev_device_get_devpath(udev_device)) && *str) {
621
+           itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_DEVICE_SYSFS_PATH, str);
622
+       }
623
+       if ((str = udev_device_get_property_value(udev_device, "SUBSYSTEM")) && *str) {
624
+           itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_DEVICE_SUBSYSTEM, str);
625
+       }
626
+
627
+       info.props = &SPA_DICT_INIT(items, n_items);
628
+
629
+       spa_log_debug(this->log, "interface information:");
630
+       spa_debug_log_dict(this->log, SPA_LOG_LEVEL_DEBUG, 2, info.props);
631
+
632
+       spa_device_emit_object_info(&this->hooks, card->compress_offload_device_id, &info);
633
+   } else {
634
+       card->compress_offload_device_id = ID_DEVICE_NOT_SUPPORTED;
635
+   }
636
 
637
    return 1;
638
 }
639
 
640
-static bool check_access(struct impl *this, struct device *device)
641
+static bool check_access(struct impl *this, struct card *card)
642
 {
643
-   char path128, prefix32;
644
+   char path128, pcm_prefix32, compr_prefix32;;
645
    spa_autoptr(DIR) snd = NULL;
646
    struct dirent *entry;
647
    bool accessible = false;
648
 
649
-   snprintf(path, sizeof(path), "/dev/snd/controlC%u", device->id);
650
+   snprintf(path, sizeof(path), "/dev/snd/controlC%u", card->card_nr);
651
    if (access(path, R_OK|W_OK) >= 0 && (snd = opendir("/dev/snd"))) {
652
        /*
653
         * It's possible that controlCX is accessible before pcmCX* or
654
@@ -520,10 +668,12 @@
655
         */
656
 
657
        accessible = true;
658
-       spa_scnprintf(prefix, sizeof(prefix), "pcmC%uD", device->id);
659
+       spa_scnprintf(pcm_prefix, sizeof(pcm_prefix), "pcmC%uD", card->card_nr);
660
+       spa_scnprintf(compr_prefix, sizeof(compr_prefix), "comprC%uD", card->card_nr);
661
        while ((entry = readdir(snd)) != NULL) {
662
            if (!(entry->d_type == DT_CHR &&
663
-                   spa_strstartswith(entry->d_name, prefix)))
664
+                 (spa_strstartswith(entry->d_name, pcm_prefix) ||
665
+                  spa_strstartswith(entry->d_name, compr_prefix))))
666
                continue;
667
 
668
            snprintf(path, sizeof(path), "/dev/snd/%.32s", entry->d_name);
669
@@ -534,70 +684,90 @@
670
        }
671
    }
672
 
673
-   if (accessible != device->accessible)
674
+   if (accessible != card->accessible)
675
        spa_log_debug(this->log, "%s accessible:%u", path, accessible);
676
-   device->accessible = accessible;
677
+   card->accessible = accessible;
678
 
679
-   return device->accessible;
680
+   return card->accessible;
681
 }
682
 
683
-static void process_device(struct impl *this, uint32_t action, struct udev_device *dev)
684
+static void process_card(struct impl *this, uint32_t action, struct udev_device *udev_device)
685
 {
686
-   uint32_t id;
687
-   struct device *device;
688
+   unsigned int card_nr;
689
+   struct card *card;
690
    bool emitted;
691
    int res;
692
 
693
-   if ((id = get_card_id(this, dev)) == SPA_ID_INVALID)
694
+   if ((card_nr = get_card_nr(this, udev_device)) == SPA_ID_INVALID)
695
        return;
696
 
697
-   device = find_device(this, id);
698
-   if (device && device->ignored)
699
+   card = find_card(this, card_nr);
700
+   if (card && card->ignored)
701
        return;
702
 
703
    switch (action) {
704
    case ACTION_ADD:
705
-       if (device == NULL)
706
-           device = add_device(this, id, dev);
707
-       if (device == NULL)
708
+       if (card == NULL)
709
+           card = add_card(this, card_nr, udev_device);
710
+       if (card == NULL)
711
            return;
712
-       if (!check_access(this, device))
713
+       if (!check_access(this, card))
714
            return;
715
-       res = emit_object_info(this, device);
716
+       res = emit_added_object_info(this, card);
717
        if (res < 0) {
718
-           if (device->ignored)
719
+           if (card->ignored)
720
                spa_log_info(this->log, "ALSA card %u unavailable (%s): it is ignored",
721
-                       device->id, spa_strerror(res));
722
-           else if (!device->unavailable)
723
+                       card->card_nr, spa_strerror(res));
724
+           else if (!card->unavailable)
725
                spa_log_info(this->log, "ALSA card %u unavailable (%s): wait for it",
726
-                       device->id, spa_strerror(res));
727
+                       card->card_nr, spa_strerror(res));
728
            else
729
                spa_log_debug(this->log, "ALSA card %u still unavailable (%s)",
730
-                       device->id, spa_strerror(res));
731
-           device->unavailable = true;
732
+                       card->card_nr, spa_strerror(res));
733
+           card->unavailable = true;
734
        } else {
735
-           if (device->unavailable)
736
+           if (card->unavailable)
737
                spa_log_info(this->log, "ALSA card %u now available",
738
-                       device->id);
739
-           device->unavailable = false;
740
+                       card->card_nr);
741
+           card->unavailable = false;
742
        }
743
        break;
744
 
745
-   case ACTION_REMOVE:
746
-       if (device == NULL)
747
+   case ACTION_REMOVE: {
748
+       uint32_t pcm_device_id, compress_offload_device_id;
749
+
750
+       if (card == NULL)
751
            return;
752
-       emitted = device->emitted;
753
-       remove_device(this, device);
754
-       if (emitted)
755
-           spa_device_emit_object_info(&this->hooks, id, NULL);
756
+
757
+       emitted = card->emitted;
758
+       pcm_device_id = card->pcm_device_id;
759
+       compress_offload_device_id = card->compress_offload_device_id;
760
+       remove_card(this, card);
761
+
762
+       if (emitted) {
763
+           if (pcm_device_id != ID_DEVICE_NOT_SUPPORTED)
764
+               spa_device_emit_object_info(&this->hooks, pcm_device_id, NULL);
765
+           if (compress_offload_device_id != ID_DEVICE_NOT_SUPPORTED)
766
+               spa_device_emit_object_info(&this->hooks, compress_offload_device_id, NULL);
767
+       }
768
        break;
769
+   }
770
 
771
    case ACTION_DISABLE:
772
-       if (device == NULL)
773
+       if (card == NULL)
774
            return;
775
-       if (device->emitted) {
776
-           device->emitted = false;
777
-           spa_device_emit_object_info(&this->hooks, id, NULL);
778
+       if (card->emitted) {
779
+           uint32_t pcm_device_id, compress_offload_device_id;
780
+
781
+           pcm_device_id = card->pcm_device_id;
782
+           compress_offload_device_id = card->compress_offload_device_id;
783
+
784
+           card->emitted = false;
785
+
786
+           if (pcm_device_id != ID_DEVICE_NOT_SUPPORTED)
787
+               spa_device_emit_object_info(&this->hooks, pcm_device_id, NULL);
788
+           if (compress_offload_device_id != ID_DEVICE_NOT_SUPPORTED)
789
+               spa_device_emit_object_info(&this->hooks, compress_offload_device_id, NULL);
790
        }
791
        break;
792
    }
793
@@ -638,28 +808,28 @@
794
 
795
        for (p = &buf; p < e;
796
             p = SPA_PTROFF(p, sizeof(struct inotify_event) + event->len, void)) {
797
-           unsigned int id;
798
-           struct device *device;
799
+           unsigned int card_nr;
800
+           struct card *card;
801
 
802
            event = (const struct inotify_event *) p;
803
            spa_assert_se(SPA_PTRDIFF(e, p) >= (ptrdiff_t)sizeof(struct inotify_event) &&
804
                          SPA_PTRDIFF(e, p) - sizeof(struct inotify_event) >= event->len &&
805
                          "bad event from kernel");
806
 
807
-           /* Device becomes accessible or not busy */
808
+           /* card becomes accessible or not busy */
809
            if ((event->mask & (IN_ATTRIB | IN_CLOSE_WRITE))) {
810
                bool access;
811
-               if (sscanf(event->name, "controlC%u", &id) != 1 &&
812
-                   sscanf(event->name, "pcmC%uD", &id) != 1)
813
+               if (sscanf(event->name, "controlC%u", &card_nr) != 1 &&
814
+                   sscanf(event->name, "pcmC%uD", &card_nr) != 1)
815
                    continue;
816
-               if ((device = find_device(this, id)) == NULL)
817
+               if ((card = find_card(this, card_nr)) == NULL)
818
                    continue;
819
 
820
-               access = check_access(this, device);
821
-               if (access && !device->emitted)
822
-                   process_device(this, ACTION_ADD, device->dev);
823
-               else if (!access && device->emitted)
824
-                   process_device(this, ACTION_DISABLE, device->dev);
825
+               access = check_access(this, card);
826
+               if (access && !card->emitted)
827
+                   process_card(this, ACTION_ADD, card->udev_device);
828
+               else if (!access && card->emitted)
829
+                   process_card(this, ACTION_DISABLE, card->udev_device);
830
            }
831
            /* /dev/snd/ might have been removed */
832
            if ((event->mask & (IN_DELETE_SELF | IN_MOVE_SELF)))
833
@@ -707,14 +877,14 @@
834
 static void impl_on_fd_events(struct spa_source *source)
835
 {
836
    struct impl *this = source->data;
837
-   struct udev_device *dev;
838
+   struct udev_device *udev_device;
839
    const char *action;
840
 
841
-   dev = udev_monitor_receive_device(this->umonitor);
842
-   if (dev == NULL)
843
+   udev_device = udev_monitor_receive_device(this->umonitor);
844
+   if (udev_device == NULL)
845
        return;
846
 
847
-   if ((action = udev_device_get_action(dev)) == NULL)
848
+   if ((action = udev_device_get_action(udev_device)) == NULL)
849
        action = "change";
850
 
851
    spa_log_debug(this->log, "action %s", action);
852
@@ -722,11 +892,11 @@
853
    start_inotify(this);
854
 
855
    if (spa_streq(action, "change")) {
856
-       process_device(this, ACTION_ADD, dev);
857
+       process_card(this, ACTION_ADD, udev_device);
858
    } else if (spa_streq(action, "remove")) {
859
-       process_device(this, ACTION_REMOVE, dev);
860
+       process_card(this, ACTION_REMOVE, udev_device);
861
    }
862
-   udev_device_unref(dev);
863
+   udev_device_unref(udev_device);
864
 }
865
 
866
 static int start_monitor(struct impl *this)
867
@@ -763,7 +933,7 @@
868
    if (this->umonitor == NULL)
869
        return 0;
870
 
871
-        clear_devices (this);
872
+        clear_cards (this);
873
 
874
    spa_loop_remove_source(this->main_loop, &this->source);
875
    udev_monitor_unref(this->umonitor);
876
@@ -774,10 +944,10 @@
877
    return 0;
878
 }
879
 
880
-static int enum_devices(struct impl *this)
881
+static int enum_cards(struct impl *this)
882
 {
883
    struct udev_enumerate *enumerate;
884
-   struct udev_list_entry *devices;
885
+   struct udev_list_entry *udev_devices;
886
 
887
    enumerate = udev_enumerate_new(this->udev);
888
    if (enumerate == NULL)
889
@@ -786,17 +956,18 @@
890
    udev_enumerate_add_match_subsystem(enumerate, "sound");
891
    udev_enumerate_scan_devices(enumerate);
892
 
893
-   for (devices = udev_enumerate_get_list_entry(enumerate); devices;
894
-           devices = udev_list_entry_get_next(devices)) {
895
-       struct udev_device *dev;
896
+   for (udev_devices = udev_enumerate_get_list_entry(enumerate); udev_devices;
897
+           udev_devices = udev_list_entry_get_next(udev_devices)) {
898
+       struct udev_device *udev_device;
899
 
900
-       dev = udev_device_new_from_syspath(this->udev, udev_list_entry_get_name(devices));
901
-       if (dev == NULL)
902
+       udev_device = udev_device_new_from_syspath(this->udev,
903
+                                                  udev_list_entry_get_name(udev_devices));
904
+       if (udev_device == NULL)
905
            continue;
906
 
907
-       process_device(this, ACTION_ADD, dev);
908
+       process_card(this, ACTION_ADD, udev_device);
909
 
910
-       udev_device_unref(dev);
911
+       udev_device_unref(udev_device);
912
    }
913
    udev_enumerate_unref(enumerate);
914
 
915
@@ -851,7 +1022,7 @@
916
    if ((res = start_monitor(this)) < 0)
917
        return res;
918
 
919
-   if ((res = enum_devices(this)) < 0)
920
+   if ((res = enum_cards(this)) < 0)
921
        return res;
922
 
923
         spa_hook_list_join(&this->hooks, &save);
924
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
331
 
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
-       if (port_id == 0)
202
+       if (port_id == 0) {
203
+           /* handle the converter output port into the follower separately */
204
+           follower_convert_port_info(this, direction, port_id, info);
205
            return;
206
-       else
207
+       } else
208
            /* the monitor ports are exposed */
209
            port_id--;
210
    } else if (info) {
211
@@ -1072,40 +1210,6 @@
212
 
213
    spa_zero(this->info.props);
214
    this->info.change_mask &= ~SPA_NODE_CHANGE_MASK_PROPS;
215
-
216
-}
217
-
218
-static int recalc_latency(struct impl *this, enum spa_direction direction, uint32_t port_id)
219
-{
220
-   struct spa_pod_builder b = { 0 };
221
-   uint8_t buffer1024;
222
-   struct spa_pod *param;
223
-   uint32_t index = 0;
224
-   struct spa_latency_info latency;
225
-   int res;
226
-
227
-   spa_log_debug(this->log, "%p: ", this);
228
-
229
-   if (this->target == this->follower)
230
-       return 0;
231
-
232
-   while (true) {
233
-       spa_pod_builder_init(&b, buffer, sizeof(buffer));
234
-       if ((res = spa_node_port_enum_params_sync(this->follower,
235
-                       direction, port_id, SPA_PARAM_Latency,
236
-                       &index, NULL, &param, &b)) != 1)
237
-           return res;
238
-       if ((res = spa_latency_parse(param, &latency)) < 0)
239
-           return res;
240
-       if (latency.direction == direction)
241
-           break;
242
-   }
243
-   if ((res = spa_node_port_set_param(this->target,
244
-                   SPA_DIRECTION_REVERSE(direction), 0,
245
-                   SPA_PARAM_Latency, 0, param)) < 0)
246
-       return res;
247
-
248
-   return 0;
249
 }
250
 
251
 static void follower_port_info(void *data,
252
@@ -1126,9 +1230,10 @@
253
         SPA_PORT_FLAG_PHYSICAL |
254
         SPA_PORT_FLAG_TERMINAL);
255
 
256
-   spa_log_debug(this->log, "%p: follower port info %s %p %08"PRIx64, this,
257
+   spa_log_debug(this->log, "%p: follower port info %s %p %08"PRIx64" recalc:%u", this,
258
            this->direction == SPA_DIRECTION_INPUT ?
259
-               "Input" : "Output", info, info->change_mask);
260
+               "Input" : "Output", info, info->change_mask,
261
+               this->in_recalc);
262
 
263
    if (info->change_mask & SPA_PORT_CHANGE_MASK_PARAMS) {
264
        for (i = 0; i < info->n_params; i++) {
265
@@ -1144,6 +1249,9 @@
266
            case SPA_PARAM_Latency:
267
                idx = IDX_Latency;
268
                break;
269
+           case SPA_PARAM_Tag:
270
+               idx = IDX_Tag;
271
+               break;
272
            default:
273
                continue;
274
            }
275
@@ -1161,11 +1269,16 @@
276
            if (this->add_listener)
277
                continue;
278
 
279
-           if (idx == IDX_Latency) {
280
-               res = recalc_latency(this, direction, port_id);
281
+           if (idx == IDX_Latency && this->in_recalc == 0) {
282
+               res = recalc_latency(this, this->follower, direction, port_id, this->target);
283
                spa_log_debug(this->log, "latency: %d (%s)", res,
284
                        spa_strerror(res));
285
            }
286
+           if (idx == IDX_Tag && this->in_recalc == 0) {
287
+               res = recalc_tag(this, this->follower, direction, port_id, this->target);
288
+               spa_log_debug(this->log, "tag: %d (%s)", res,
289
+                       spa_strerror(res));
290
+           }
291
            if (idx == IDX_EnumFormat) {
292
                spa_log_debug(this->log, "new formats");
293
                configure_format(this, 0, NULL);
294
@@ -1390,7 +1503,6 @@
295
             const struct spa_pod *param)
296
 {
297
    struct impl *this = object;
298
-   int res;
299
 
300
    spa_return_val_if_fail(this != NULL, -EINVAL);
301
 
302
@@ -1399,18 +1511,8 @@
303
    if (direction != this->direction)
304
        port_id++;
305
 
306
-   if ((res = spa_node_port_set_param(this->target, direction, port_id, id,
307
-           flags, param)) < 0)
308
-       return res;
309
-
310
-   if ((id == SPA_PARAM_Latency) &&
311
-       direction == this->direction) {
312
-       if ((res = spa_node_port_set_param(this->follower, direction, 0, id,
313
-               flags, param)) < 0)
314
-           return res;
315
-   }
316
-
317
-   return res;
318
+   return spa_node_port_set_param(this->target, direction, port_id, id,
319
+           flags, param);
320
 }
321
 
322
 static int
323
@@ -1707,6 +1809,7 @@
324
    this->paramsIDX_PortConfig = SPA_PARAM_INFO(SPA_PARAM_PortConfig, SPA_PARAM_INFO_READWRITE);
325
    this->paramsIDX_Latency = SPA_PARAM_INFO(SPA_PARAM_Latency, SPA_PARAM_INFO_READWRITE);
326
    this->paramsIDX_ProcessLatency = SPA_PARAM_INFO(SPA_PARAM_ProcessLatency, SPA_PARAM_INFO_READWRITE);
327
+   this->paramsIDX_Tag = SPA_PARAM_INFO(SPA_PARAM_Tag, SPA_PARAM_INFO_READWRITE);
328
    this->info.params = this->params;
329
    this->info.n_params = N_NODE_PARAMS;
330
 
331
pipewire-0.3.79.tar.gz/spa/plugins/audioconvert/audioconvert.c -> pipewire-0.3.80.tar.gz/spa/plugins/audioconvert/audioconvert.c Changed
303
 
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
+   if (spa_tag_compare(tag, this->dirother.tag) != 0) {
202
+       free(this->dirother.tag);
203
+       this->dirother.tag = tag ? spa_pod_copy(tag) : NULL;
204
+
205
+       for (i = 0; i < this->dirother.n_ports; i++) {
206
+           oport = GET_PORT(this, other, i);
207
+           oport->info.change_mask |= SPA_PORT_CHANGE_MASK_PARAMS;
208
+           oport->paramsIDX_Tag.user++;
209
+           emit_port_info(this, oport, false);
210
+       }
211
    }
212
    port->info.change_mask |= SPA_PORT_CHANGE_MASK_PARAMS;
213
-   port->paramsIDX_Latency.user++;
214
+   port->paramsIDX_Tag.user++;
215
    emit_port_info(this, port, false);
216
    return 0;
217
 }
218
@@ -2296,6 +2398,8 @@
219
    switch (id) {
220
    case SPA_PARAM_Latency:
221
        return port_set_latency(this, direction, port_id, flags, param);
222
+   case SPA_PARAM_Tag:
223
+       return port_set_tag(this, direction, port_id, flags, param);
224
    case SPA_PARAM_Format:
225
        return port_set_format(this, direction, port_id, flags, param);
226
    default:
227
@@ -2636,7 +2740,7 @@
228
    struct buffer *buf, *out_bufsMAX_PORTS;
229
    struct spa_data *bd;
230
    struct dir *dir;
231
-   int tmp = 0, res = 0, missed;
232
+   int tmp = 0, res = 0, suppressed;
233
    bool in_passthrough, mix_passthrough, resample_passthrough, out_passthrough;
234
    bool in_avail = false, flush_in = false, flush_out = false;
235
    bool draining = false, in_empty = this->out_offset == 0;
236
@@ -2804,9 +2908,9 @@
237
 
238
            buf = peek_buffer(this, port);
239
            if (buf == NULL && port->n_buffers > 0 &&
240
-               (missed = spa_ratelimit_test(&this->rate_limit, current_time)) >= 0) {
241
-               spa_log_warn(this->log, "%p: (%d missed) out of buffers on port %d %d",
242
-                   this, missed, port->id, port->n_buffers);
243
+               (suppressed = spa_ratelimit_test(&this->rate_limit, current_time)) >= 0) {
244
+               spa_log_warn(this->log, "%p: (%d suppressed) out of buffers on port %d %d",
245
+                   this, suppressed, port->id, port->n_buffers);
246
            }
247
        }
248
        out_bufsi = buf;
249
@@ -3123,19 +3227,27 @@
250
    return 0;
251
 }
252
 
253
+static void free_dir(struct dir *dir)
254
+{
255
+   uint32_t i;
256
+   for (i = 0; i < MAX_PORTS; i++)
257
+       free(dir->portsi);
258
+   if (dir->conv.free)
259
+       convert_free(&dir->conv);
260
+   free(dir->tag);
261
+}
262
+
263
 static int impl_clear(struct spa_handle *handle)
264
 {
265
    struct impl *this;
266
-   uint32_t i;
267
 
268
    spa_return_val_if_fail(handle != NULL, -EINVAL);
269
 
270
    this = (struct impl *) handle;
271
 
272
-   for (i = 0; i < MAX_PORTS; i++)
273
-       free(this->dirSPA_DIRECTION_INPUT.portsi);
274
-   for (i = 0; i < MAX_PORTS; i++)
275
-       free(this->dirSPA_DIRECTION_OUTPUT.portsi);
276
+   free_dir(&this->dirSPA_DIRECTION_INPUT);
277
+   free_dir(&this->dirSPA_DIRECTION_OUTPUT);
278
+
279
    free(this->empty);
280
    free(this->scratch);
281
    free(this->tmp0);
282
@@ -3143,10 +3255,6 @@
283
 
284
    if (this->resample.free)
285
        resample_free(&this->resample);
286
-   if (this->dir0.conv.free)
287
-       convert_free(&this->dir0.conv);
288
-   if (this->dir1.conv.free)
289
-       convert_free(&this->dir1.conv);
290
    if (this->wav_file != NULL)
291
        wav_file_close(this->wav_file);
292
    free (this->vol_ramp_sequence);
293
@@ -3258,9 +3366,7 @@
294
    this->props.monitor.n_volumes = this->props.n_channels;
295
 
296
    this->dirSPA_DIRECTION_INPUT.direction = SPA_DIRECTION_INPUT;
297
-   this->dirSPA_DIRECTION_INPUT.latency = SPA_LATENCY_INFO(SPA_DIRECTION_INPUT);
298
    this->dirSPA_DIRECTION_OUTPUT.direction = SPA_DIRECTION_OUTPUT;
299
-   this->dirSPA_DIRECTION_OUTPUT.latency = SPA_LATENCY_INFO(SPA_DIRECTION_OUTPUT);
300
 
301
    this->node.iface = SPA_INTERFACE_INIT(
302
            SPA_TYPE_INTERFACE_Node,
303
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
213
 
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
            }
202
            else if (spa_streq(key, "State")) {
203
                enum spa_bt_transport_state state  = spa_bt_transport_state_from_string(value);
204
@@ -4154,7 +4141,7 @@
205
    }
206
 
207
    for (i = 0; codecsi != NULL; ++i) {
208
-       if (spa_bt_device_supports_media_codec(device, codecsi, true)) {
209
+       if (spa_bt_device_supports_media_codec(device, codecsi, device->connected_profiles)) {
210
            preferred_codec = codecsi;
211
            break;
212
        }
213
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
632
 
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
-       if (!check_access(device))
202
+       if (!check_access(this, device))
203
            return;
204
-       else
205
-           emit_object_info(device);
206
+       emit_object_info(this, device);
207
        break;
208
 
209
    case ACTION_REMOVE:
210
        if (device == NULL)
211
            return;
212
        emitted = device->emitted;
213
-       remove_device(device);
214
+       remove_device(this, device);
215
        if (emitted)
216
-           spa_device_emit_object_info(&impl->hooks, id, NULL);
217
+           spa_device_emit_object_info(&this->hooks, id, NULL);
218
        break;
219
 
220
    case ACTION_DISABLE:
221
@@ -387,40 +396,36 @@
222
            return;
223
        if (device->emitted) {
224
            device->emitted = false;
225
-           spa_device_emit_object_info(&impl->hooks, id, NULL);
226
+           spa_device_emit_object_info(&this->hooks, id, NULL);
227
        }
228
        break;
229
    }
230
 }
231
 
232
-static int stop_inotify(struct device *dev)
233
+static int stop_inotify(struct impl *this)
234
 {
235
-   struct impl *impl = dev->impl;
236
-   if (dev->notify.fd == -1)
237
+   if (this->notify.fd == -1)
238
        return 0;
239
-   spa_log_info(impl->log, "stop inotify for /dev/video%u", dev->id);
240
-   spa_loop_remove_source(impl->main_loop, &dev->notify);
241
-   close(dev->notify.fd);
242
-   dev->notify.fd = -1;
243
+   spa_log_info(this->log, "stop inotify");
244
+
245
+   for (size_t i = 0; i < this->n_devices; i++)
246
+       stop_watching_device(this, &this->devicesi);
247
+
248
+   spa_loop_remove_source(this->main_loop, &this->notify);
249
+   close(this->notify.fd);
250
+   this->notify.fd = -1;
251
    return 0;
252
 }
253
 
254
-
255
 static void impl_on_notify_events(struct spa_source *source)
256
 {
257
-   struct device *dev = source->data;
258
-   struct impl *impl = dev->impl;
259
+   struct impl *this = source->data;
260
    union {
261
        unsigned char namesizeof(struct inotify_event) + NAME_MAX + 1;
262
        struct inotify_event e; /* for appropriate alignment */
263
    } buf;
264
 
265
-   if (source->rmask & (SPA_IO_ERR | SPA_IO_HUP)) {
266
-       spa_log_warn(impl->log, "notify error on /dev/video%u", dev->id);
267
-       stop_inotify(dev);
268
-       return;
269
-   }
270
-   while (source->rmask & SPA_IO_IN) {
271
+   while (true) {
272
        ssize_t len;
273
        const struct inotify_event *event;
274
        void *p, *e;
275
@@ -435,126 +440,131 @@
276
 
277
        for (p = &buf; p < e;
278
            p = SPA_PTROFF(p, sizeof(struct inotify_event) + event->len, void)) {
279
-
280
            event = (const struct inotify_event *) p;
281
 
282
            if ((event->mask & IN_ATTRIB)) {
283
-               bool access;
284
-               access = check_access(dev);
285
-               if (access && !dev->emitted)
286
-                   process_device(impl, ACTION_ADD, dev->dev);
287
-               else if (!access && dev->emitted)
288
-                   process_device(impl, ACTION_DISABLE, dev->dev);
289
+               struct device *device = NULL;
290
+
291
+               for (size_t i = 0; i < this->n_devices; i++) {
292
+                   if (this->devicesi.inotify_wd == event->wd) {
293
+                       device = &this->devicesi;
294
+                       break;
295
+                   }
296
+               }
297
+
298
+               spa_assert(device);
299
+
300
+               bool access = check_access(this, device);
301
+               if (access && !device->emitted)
302
+                   process_device(this, ACTION_ADD, device->dev);
303
+               else if (!access && device->emitted)
304
+                   process_device(this, ACTION_DISABLE, device->dev);
305
            }
306
        }
307
    }
308
 }
309
 
310
-static int start_inotify(struct device *dev)
311
+static int start_inotify(struct impl *this)
312
 {
313
-   struct impl *impl = dev->impl;
314
-   int res, notify_fd;
315
-   char name32;
316
+   int notify_fd;
317
 
318
-   if (dev->notify.fd != -1)
319
+   if (this->notify.fd != -1)
320
        return 0;
321
 
322
    if ((notify_fd = inotify_init1(IN_CLOEXEC | IN_NONBLOCK)) < 0)
323
        return -errno;
324
 
325
-   snprintf(name, sizeof(name), "/dev/video%u", dev->id);
326
+   spa_log_info(this->log, "start inotify");
327
+   this->notify.func = impl_on_notify_events;
328
+   this->notify.data = this;
329
+   this->notify.fd = notify_fd;
330
+   this->notify.mask = SPA_IO_IN | SPA_IO_ERR;
331
 
332
-   res = inotify_add_watch(notify_fd, name, IN_ATTRIB | IN_CLOSE_WRITE);
333
-   if (res < 0) {
334
-       res = -errno;
335
-       close(notify_fd);
336
-
337
-       if (res == -ENOENT) {
338
-           spa_log_debug(impl->log, "%s does not exist yet", name);
339
-           return 0;
340
-       }
341
-       spa_log_error(impl->log, "inotify_add_watch() failed: %s", spa_strerror(res));
342
-       return res;
343
-   }
344
-   spa_log_info(impl->log, "start inotify for %s", name);
345
-   dev->notify.func = impl_on_notify_events;
346
-   dev->notify.data = dev;
347
-   dev->notify.fd = notify_fd;
348
-   dev->notify.mask = SPA_IO_IN | SPA_IO_ERR;
349
+   spa_loop_add_source(this->main_loop, &this->notify);
350
 
351
-   spa_loop_add_source(impl->main_loop, &dev->notify);
352
+   for (size_t i = 0; i < this->n_devices; i++)
353
+       start_watching_device(this, &this->devicesi);
354
 
355
    return 0;
356
 }
357
 
358
 static void impl_on_fd_events(struct spa_source *source)
359
 {
360
-   struct impl *impl = source->data;
361
+   struct impl *this = source->data;
362
    struct udev_device *dev;
363
    const char *action;
364
 
365
-   dev = udev_monitor_receive_device(impl->umonitor);
366
+   dev = udev_monitor_receive_device(this->umonitor);
367
    if (dev == NULL)
368
        return;
369
 
370
    if ((action = udev_device_get_action(dev)) == NULL)
371
        action = "change";
372
 
373
-   spa_log_debug(impl->log, "action %s", action);
374
+   spa_log_debug(this->log, "action %s", action);
375
+
376
+   start_inotify(this);
377
 
378
    if (spa_streq(action, "add") ||
379
        spa_streq(action, "change")) {
380
-       process_device(impl, ACTION_ADD, dev);
381
+       process_device(this, ACTION_ADD, dev);
382
    } else if (spa_streq(action, "remove")) {
383
-       process_device(impl, ACTION_REMOVE, dev);
384
+       process_device(this, ACTION_REMOVE, dev);
385
    }
386
    udev_device_unref(dev);
387
 }
388
 
389
-static int start_monitor(struct impl *impl)
390
+static int start_monitor(struct impl *this)
391
 {
392
-   if (impl->umonitor != NULL)
393
+   int res;
394
+
395
+   if (this->umonitor != NULL)
396
        return 0;
397
 
398
-   impl->umonitor = udev_monitor_new_from_netlink(impl->udev, "udev");
399
-   if (impl->umonitor == NULL)
400
+   this->umonitor = udev_monitor_new_from_netlink(this->udev, "udev");
401
+   if (this->umonitor == NULL)
402
        return -ENOMEM;
403
 
404
-   udev_monitor_filter_add_match_subsystem_devtype(impl->umonitor,
405
+   udev_monitor_filter_add_match_subsystem_devtype(this->umonitor,
406
                            "video4linux", NULL);
407
-   udev_monitor_enable_receiving(impl->umonitor);
408
+   udev_monitor_enable_receiving(this->umonitor);
409
 
410
-   impl->source.func = impl_on_fd_events;
411
-   impl->source.data = impl;
412
-   impl->source.fd = udev_monitor_get_fd(impl->umonitor);
413
-   impl->source.mask = SPA_IO_IN | SPA_IO_ERR;
414
+   this->source.func = impl_on_fd_events;
415
+   this->source.data = this;
416
+   this->source.fd = udev_monitor_get_fd(this->umonitor);
417
+   this->source.mask = SPA_IO_IN | SPA_IO_ERR;
418
 
419
-   spa_log_debug(impl->log, "monitor %p", impl->umonitor);
420
-   spa_loop_add_source(impl->main_loop, &impl->source);
421
+   spa_log_debug(this->log, "monitor %p", this->umonitor);
422
+   spa_loop_add_source(this->main_loop, &this->source);
423
+
424
+   if ((res = start_inotify(this)) < 0)
425
+       return res;
426
 
427
    return 0;
428
 }
429
 
430
-static int stop_monitor(struct impl *impl)
431
+static int stop_monitor(struct impl *this)
432
 {
433
-   if (impl->umonitor == NULL)
434
+   if (this->umonitor == NULL)
435
        return 0;
436
 
437
-   clear_devices(impl);
438
+   clear_devices (this);
439
 
440
-   spa_loop_remove_source(impl->main_loop, &impl->source);
441
-   udev_monitor_unref(impl->umonitor);
442
-   impl->umonitor = NULL;
443
+   spa_loop_remove_source(this->main_loop, &this->source);
444
+   udev_monitor_unref(this->umonitor);
445
+   this->umonitor = NULL;
446
+
447
+   stop_inotify(this);
448
 
449
    return 0;
450
 }
451
 
452
-static int enum_devices(struct impl *impl)
453
+static int enum_devices(struct impl *this)
454
 {
455
    struct udev_enumerate *enumerate;
456
    struct udev_list_entry *devices;
457
 
458
-   enumerate = udev_enumerate_new(impl->udev);
459
+   enumerate = udev_enumerate_new(this->udev);
460
    if (enumerate == NULL)
461
        return -ENOMEM;
462
 
463
@@ -565,11 +575,11 @@
464
            devices = udev_list_entry_get_next(devices)) {
465
        struct udev_device *dev;
466
 
467
-       dev = udev_device_new_from_syspath(impl->udev, udev_list_entry_get_name(devices));
468
+       dev = udev_device_new_from_syspath(this->udev, udev_list_entry_get_name(devices));
469
        if (dev == NULL)
470
            continue;
471
 
472
-       process_device(impl, ACTION_ADD, dev);
473
+       process_device(this, ACTION_ADD, dev);
474
 
475
        udev_device_unref(dev);
476
    }
477
@@ -584,24 +594,24 @@
478
    { SPA_KEY_API_UDEV_MATCH, "video4linux" },
479
 };
480
 
481
-static void emit_device_info(struct impl *impl, bool full)
482
+static void emit_device_info(struct impl *this, bool full)
483
 {
484
-   uint64_t old = full ? impl->info.change_mask : 0;
485
+   uint64_t old = full ? this->info.change_mask : 0;
486
    if (full)
487
-       impl->info.change_mask = impl->info_all;
488
-   if (impl->info.change_mask) {
489
-       impl->info.props = &SPA_DICT_INIT_ARRAY(device_info_items);
490
-       spa_device_emit_info(&impl->hooks, &impl->info);
491
-       impl->info.change_mask = old;
492
+       this->info.change_mask = this->info_all;
493
+   if (this->info.change_mask) {
494
+       this->info.props = &SPA_DICT_INIT_ARRAY(device_info_items);
495
+       spa_device_emit_info(&this->hooks, &this->info);
496
+       this->info.change_mask = old;
497
    }
498
 }
499
 
500
 static void impl_hook_removed(struct spa_hook *hook)
501
 {
502
-   struct impl *impl = hook->priv;
503
-   if (spa_hook_list_is_empty(&impl->hooks)) {
504
-       stop_monitor(impl);
505
-       impl_udev_close(impl);
506
+   struct impl *this = hook->priv;
507
+   if (spa_hook_list_is_empty(&this->hooks)) {
508
+       stop_monitor(this);
509
+       impl_udev_close(this);
510
    }
511
 }
512
 
513
@@ -610,29 +620,29 @@
514
        const struct spa_device_events *events, void *data)
515
 {
516
    int res;
517
-   struct impl *impl = object;
518
+   struct impl *this = object;
519
         struct spa_hook_list save;
520
 
521
-   spa_return_val_if_fail(impl != NULL, -EINVAL);
522
+   spa_return_val_if_fail(this != NULL, -EINVAL);
523
    spa_return_val_if_fail(events != NULL, -EINVAL);
524
 
525
-   if ((res = impl_udev_open(impl)) < 0)
526
+   if ((res = impl_udev_open(this)) < 0)
527
        return res;
528
 
529
-        spa_hook_list_isolate(&impl->hooks, &save, listener, events, data);
530
+        spa_hook_list_isolate(&this->hooks, &save, listener, events, data);
531
 
532
-   emit_device_info(impl, true);
533
+   emit_device_info(this, true);
534
 
535
-   if ((res = enum_devices(impl)) < 0)
536
+   if ((res = enum_devices(this)) < 0)
537
        return res;
538
 
539
-   if ((res = start_monitor(impl)) < 0)
540
+   if ((res = start_monitor(this)) < 0)
541
        return res;
542
 
543
-        spa_hook_list_join(&impl->hooks, &save);
544
+        spa_hook_list_join(&this->hooks, &save);
545
 
546
    listener->removed = impl_hook_removed;
547
-   listener->priv = impl;
548
+   listener->priv = this;
549
 
550
    return 0;
551
 }
552
@@ -644,15 +654,15 @@
553
 
554
 static int impl_get_interface(struct spa_handle *handle, const char *type, void **interface)
555
 {
556
-   struct impl *impl;
557
+   struct impl *this;
558
 
559
    spa_return_val_if_fail(handle != NULL, -EINVAL);
560
    spa_return_val_if_fail(interface != NULL, -EINVAL);
561
 
562
-   impl = (struct impl *) handle;
563
+   this = (struct impl *) handle;
564
 
565
    if (spa_streq(type, SPA_TYPE_INTERFACE_Device))
566
-       *interface = &impl->device;
567
+       *interface = &this->device;
568
    else
569
        return -ENOENT;
570
 
571
@@ -661,9 +671,9 @@
572
 
573
 static int impl_clear(struct spa_handle *handle)
574
 {
575
-   struct impl *impl = (struct impl *) handle;
576
-   stop_monitor(impl);
577
-   impl_udev_close(impl);
578
+   struct impl *this = (struct impl *) handle;
579
+   stop_monitor(this);
580
+   impl_udev_close(this);
581
    return 0;
582
 }
583
 
584
@@ -681,7 +691,7 @@
585
      const struct spa_support *support,
586
      uint32_t n_support)
587
 {
588
-   struct impl *impl;
589
+   struct impl *this;
590
 
591
    spa_return_val_if_fail(factory != NULL, -EINVAL);
592
    spa_return_val_if_fail(handle != NULL, -EINVAL);
593
@@ -689,25 +699,27 @@
594
    handle->get_interface = impl_get_interface;
595
    handle->clear = impl_clear;
596
 
597
-   impl = (struct impl *) handle;
598
-   impl->log = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_Log);
599
-   impl->main_loop = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_Loop);
600
+   this = (struct impl *) handle;
601
+   this->notify.fd = -1;
602
+
603
+   this->log = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_Log);
604
+   this->main_loop = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_Loop);
605
 
606
-   if (impl->main_loop == NULL) {
607
-       spa_log_error(impl->log, "a main-loop is needed");
608
+   if (this->main_loop == NULL) {
609
+       spa_log_error(this->log, "a main-loop is needed");
610
        return -EINVAL;
611
    }
612
-   spa_hook_list_init(&impl->hooks);
613
+   spa_hook_list_init(&this->hooks);
614
 
615
-   impl->device.iface = SPA_INTERFACE_INIT(
616
+   this->device.iface = SPA_INTERFACE_INIT(
617
            SPA_TYPE_INTERFACE_Device,
618
            SPA_VERSION_DEVICE,
619
-           &impl_device, impl);
620
+           &impl_device, this);
621
 
622
-   impl->info = SPA_DEVICE_INFO_INIT();
623
-   impl->info_all = SPA_DEVICE_CHANGE_MASK_FLAGS |
624
+   this->info = SPA_DEVICE_INFO_INIT();
625
+   this->info_all = SPA_DEVICE_CHANGE_MASK_FLAGS |
626
            SPA_DEVICE_CHANGE_MASK_PROPS;
627
-   impl->info.flags = 0;
628
+   this->info.flags = 0;
629
 
630
    return 0;
631
 }
632
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
257
 
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
+           port->info.flags &= ~SPA_PORT_FLAG_CAN_ALLOC_BUFFERS;
202
+       }
203
+       port->info.change_mask |= SPA_PORT_CHANGE_MASK_FLAGS;
204
+
205
        port->current_format = info;
206
        port->have_format = true;
207
+
208
+       if (modifier_fixed) {
209
+           port->info.change_mask |= SPA_PORT_CHANGE_MASK_PARAMS;
210
+           port->params0.flags ^= SPA_PARAM_INFO_SERIAL;
211
+           emit_port_info(this, port, false);
212
+           return 0;
213
+       }
214
    }
215
 
216
    port->info.change_mask |= SPA_PORT_CHANGE_MASK_PARAMS;
217
@@ -504,7 +628,7 @@
218
        spa_log_info(this->log, "%p: %d:%d add buffer %p", port, direction, port_id, b);
219
        spa_list_append(&port->empty, &b->link);
220
    }
221
-   spa_vulkan_use_buffers(&this->state, &this->state.streamsport->stream_id, flags, n_buffers, buffers);
222
+   spa_vulkan_use_buffers(&this->state, &this->state.streamsport->stream_id, flags, &port->current_format.info.dsp, n_buffers, buffers);
223
    port->n_buffers = n_buffers;
224
 
225
    return 0;
226
@@ -650,6 +774,13 @@
227
 
228
 static int impl_clear(struct spa_handle *handle)
229
 {
230
+   struct impl *this;
231
+
232
+   spa_return_val_if_fail(handle != NULL, -EINVAL);
233
+
234
+   this = (struct impl *) handle;
235
+
236
+   spa_vulkan_deinit(&this->state);
237
    return 0;
238
 }
239
 
240
@@ -708,7 +839,7 @@
241
            SPA_PORT_CHANGE_MASK_PARAMS |
242
            SPA_PORT_CHANGE_MASK_PROPS;
243
    port->info = SPA_PORT_INFO_INIT();
244
-   port->info.flags = SPA_PORT_FLAG_NO_REF | SPA_PORT_FLAG_CAN_ALLOC_BUFFERS;
245
+   port->info.flags = SPA_PORT_FLAG_NO_REF;
246
    port->params0 = SPA_PARAM_INFO(SPA_PARAM_EnumFormat, SPA_PARAM_INFO_READ);
247
    port->params1 = SPA_PARAM_INFO(SPA_PARAM_Meta, SPA_PARAM_INFO_READ);
248
    port->params2 = SPA_PARAM_INFO(SPA_PARAM_IO, SPA_PARAM_INFO_READ);
249
@@ -742,6 +873,7 @@
250
            SPA_DIRECTION_OUTPUT, NULL);
251
 
252
    this->state.n_streams = 2;
253
+   spa_vulkan_init(&this->state);
254
    spa_vulkan_prepare(&this->state);
255
 
256
    return 0;
257
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
257
 
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
+       } else {
202
+           port->info.flags &= ~SPA_PORT_FLAG_CAN_ALLOC_BUFFERS;
203
+       }
204
+       port->info.change_mask |= SPA_PORT_CHANGE_MASK_FLAGS;
205
+
206
        port->current_format = info;
207
        port->have_format = true;
208
        spa_vulkan_prepare(&this->state);
209
+
210
+       if (modifier_fixed) {
211
+           port->info.change_mask |= SPA_PORT_CHANGE_MASK_PARAMS;
212
+           port->params0.flags ^= SPA_PARAM_INFO_SERIAL;
213
+           emit_port_info(this, port, false);
214
+           return 0;
215
+       }
216
    }
217
 
218
    port->info.change_mask |= SPA_PORT_CHANGE_MASK_PARAMS;
219
@@ -736,9 +861,10 @@
220
        b->flags = 0;
221
        b->h = spa_buffer_find_meta_data(buffersi, SPA_META_Header, sizeof(*b->h));
222
 
223
+       spa_log_info(this->log, "%p: %d:%d add buffer %p", port, direction, port_id, b);
224
        spa_list_append(&port->empty, &b->link);
225
    }
226
-   spa_vulkan_use_buffers(&this->state, &this->state.streams0, flags, n_buffers, buffers);
227
+   spa_vulkan_use_buffers(&this->state, &this->state.streams0, flags, &port->current_format.info.dsp, n_buffers, buffers);
228
    port->n_buffers = n_buffers;
229
 
230
    return 0;
231
@@ -859,6 +985,8 @@
232
 
233
    this = (struct impl *) handle;
234
 
235
+   spa_vulkan_deinit(&this->state);
236
+
237
    if (this->data_loop)
238
        spa_loop_invoke(this->data_loop, do_remove_timer, 0, NULL, 0, true, this);
239
    spa_system_close(this->data_system, this->timer_source.fd);
240
@@ -933,7 +1061,7 @@
241
            SPA_PORT_CHANGE_MASK_PARAMS |
242
            SPA_PORT_CHANGE_MASK_PROPS;
243
    port->info = SPA_PORT_INFO_INIT();
244
-   port->info.flags = SPA_PORT_FLAG_NO_REF | SPA_PORT_FLAG_CAN_ALLOC_BUFFERS;
245
+   port->info.flags = SPA_PORT_FLAG_NO_REF;
246
    if (this->props.live)
247
        port->info.flags |= SPA_PORT_FLAG_LIVE;
248
    port->params0 = SPA_PARAM_INFO(SPA_PARAM_EnumFormat, SPA_PARAM_INFO_READ);
249
@@ -951,6 +1079,7 @@
250
            SPA_DIRECTION_OUTPUT, NULL);
251
    this->state.shaderName = "spa/plugins/vulkan/shaders/main.spv";
252
    this->state.n_streams = 1;
253
+   spa_vulkan_init(&this->state);
254
 
255
    return 0;
256
 }
257
pipewire-0.3.80.tar.gz/spa/plugins/vulkan/vulkan-compute-utils.c Added
677
 
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
+   }
202
+   return shaderModule;
203
+}
204
+
205
+static int createComputePipeline(struct vulkan_compute_state *s, const char *shader_file)
206
+{
207
+   static const VkPushConstantRange range = {
208
+       .stageFlags = VK_SHADER_STAGE_COMPUTE_BIT,
209
+       .offset = 0,
210
+       .size = sizeof(struct push_constants)
211
+   };
212
+
213
+   const VkPipelineLayoutCreateInfo pipelineLayoutCreateInfo = {
214
+       .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
215
+       .setLayoutCount = 1,
216
+       .pSetLayouts = &s->descriptorSetLayout,
217
+       .pushConstantRangeCount = 1,
218
+       .pPushConstantRanges = &range,
219
+   };
220
+   VK_CHECK_RESULT(vkCreatePipelineLayout(s->base.device,
221
+               &pipelineLayoutCreateInfo, NULL,
222
+               &s->pipelineLayout));
223
+
224
+        s->computeShaderModule = createShaderModule(s, shader_file);
225
+   if (s->computeShaderModule == VK_NULL_HANDLE)
226
+       return -ENOENT;
227
+
228
+   const VkPipelineShaderStageCreateInfo shaderStageCreateInfo = {
229
+       .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
230
+       .stage = VK_SHADER_STAGE_COMPUTE_BIT,
231
+       .module = s->computeShaderModule,
232
+       .pName = "main",
233
+   };
234
+   const VkComputePipelineCreateInfo pipelineCreateInfo = {
235
+       .sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO,
236
+       .stage = shaderStageCreateInfo,
237
+       .layout = s->pipelineLayout,
238
+   };
239
+   VK_CHECK_RESULT(vkCreateComputePipelines(s->base.device, VK_NULL_HANDLE,
240
+               1, &pipelineCreateInfo, NULL,
241
+               &s->pipeline));
242
+   return 0;
243
+}
244
+
245
+static int createCommandBuffer(struct vulkan_compute_state *s)
246
+{
247
+   CHECK(vulkan_commandPool_create(&s->base, &s->commandPool));
248
+   CHECK(vulkan_commandBuffer_create(&s->base, s->commandPool, &s->commandBuffer));
249
+
250
+   return 0;
251
+}
252
+
253
+static int runExportSHMBuffers(struct vulkan_compute_state *s) {
254
+   for (uint32_t i = 0; i < s->n_streams; i++) {
255
+       struct vulkan_stream *p = &s->streamsi;
256
+
257
+       if (p->direction == SPA_DIRECTION_INPUT)
258
+           continue;
259
+
260
+       if (p->spa_buffersp->current_buffer_id->datas0.type == SPA_DATA_MemPtr) {
261
+           struct spa_buffer *spa_buf = p->spa_buffersp->current_buffer_id;
262
+           struct vulkan_read_pixels_info readInfo = {
263
+               .data = spa_buf->datas0.data,
264
+               .offset = spa_buf->datas0.chunk->offset,
265
+               .stride = spa_buf->datas0.chunk->stride,
266
+               .bytes_per_pixel = 16,
267
+               .size.width = s->constants.width,
268
+               .size.height = s->constants.height,
269
+           };
270
+           CHECK(vulkan_read_pixels(&s->base, &readInfo, &p->buffersp->current_buffer_id));
271
+       }
272
+   }
273
+
274
+   return 0;
275
+}
276
+
277
+/** runCommandBuffer
278
+ *  The return value of this functions means the following:
279
+ *  ret < 0: Error
280
+ *  ret = 0: queueSubmit was succsessful, but manual synchronization is required
281
+ *  ret = 1: queueSubmit was succsessful and buffers can be released without synchronization
282
+ */
283
+static int runCommandBuffer(struct vulkan_compute_state *s)
284
+{
285
+   VULKAN_INSTANCE_FUNCTION(vkQueueSubmit2KHR);
286
+   VULKAN_INSTANCE_FUNCTION(vkGetSemaphoreFdKHR);
287
+
288
+   static const VkCommandBufferBeginInfo beginInfo = {
289
+       .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
290
+       .flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT,
291
+   };
292
+   VK_CHECK_RESULT(vkBeginCommandBuffer(s->commandBuffer, &beginInfo));
293
+
294
+   vkCmdBindPipeline(s->commandBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, s->pipeline);
295
+   vkCmdPushConstants (s->commandBuffer,
296
+           s->pipelineLayout, VK_SHADER_STAGE_COMPUTE_BIT,
297
+           0, sizeof(struct push_constants), (const void *) &s->constants);
298
+   vkCmdBindDescriptorSets(s->commandBuffer, VK_PIPELINE_BIND_POINT_COMPUTE,
299
+           s->pipelineLayout, 0, 1, &s->descriptorSet, 0, NULL);
300
+
301
+   vkCmdDispatch(s->commandBuffer,
302
+           (uint32_t)ceil(s->constants.width / (float)WORKGROUP_SIZE),
303
+           (uint32_t)ceil(s->constants.height / (float)WORKGROUP_SIZE), 1);
304
+
305
+   VkImageMemoryBarrier acquire_barriers->n_streams;
306
+   VkImageMemoryBarrier release_barriers->n_streams;
307
+   VkSemaphoreSubmitInfo semaphore_wait_infos->n_streams;
308
+   uint32_t semaphore_wait_info_len = 0;
309
+   VkSemaphoreSubmitInfo semaphore_signal_info1;
310
+   uint32_t semaphore_signal_info_len = 0;
311
+
312
+   uint32_t i;
313
+   for (i = 0; i < s->n_streams; i++) {
314
+       struct vulkan_stream *p = &s->streamsi;
315
+       struct vulkan_buffer *current_buffer = &p->buffersp->current_buffer_id;
316
+       struct spa_buffer *current_spa_buffer = p->spa_buffersp->current_buffer_id;
317
+
318
+       VkAccessFlags access_flags;
319
+       if (p->direction == SPA_DIRECTION_INPUT) {
320
+           access_flags = VK_ACCESS_SHADER_READ_BIT;
321
+       } else {
322
+           access_flags = VK_ACCESS_SHADER_WRITE_BIT;
323
+       }
324
+
325
+       acquire_barrieri= (VkImageMemoryBarrier) {
326
+           .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
327
+           .srcQueueFamilyIndex = VK_QUEUE_FAMILY_FOREIGN_EXT,
328
+           .dstQueueFamilyIndex = s->base.queueFamilyIndex,
329
+           .image = current_buffer->image,
330
+           .oldLayout = VK_IMAGE_LAYOUT_UNDEFINED,
331
+           .newLayout = VK_IMAGE_LAYOUT_GENERAL,
332
+           .srcAccessMask = 0,
333
+           .dstAccessMask = access_flags,
334
+           .subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
335
+           .subresourceRange.levelCount = 1,
336
+           .subresourceRange.layerCount = 1,
337
+       };
338
+
339
+       release_barrieri= (VkImageMemoryBarrier) {
340
+           .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
341
+           .srcQueueFamilyIndex = s->base.queueFamilyIndex,
342
+           .dstQueueFamilyIndex = VK_QUEUE_FAMILY_FOREIGN_EXT,
343
+           .image = current_buffer->image,
344
+           .oldLayout = VK_IMAGE_LAYOUT_GENERAL,
345
+           .newLayout = VK_IMAGE_LAYOUT_GENERAL,
346
+           .srcAccessMask = access_flags,
347
+           .dstAccessMask = 0,
348
+           .subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
349
+           .subresourceRange.levelCount = 1,
350
+           .subresourceRange.layerCount = 1,
351
+       };
352
+
353
+       if (current_spa_buffer->datas0.type != SPA_DATA_DmaBuf)
354
+           continue;
355
+
356
+       if (vulkan_sync_foreign_dmabuf(&s->base, current_buffer) < 0) {
357
+           spa_log_warn(s->log, "Failed to wait for foreign buffer DMA-BUF fence");
358
+       } else {
359
+           if (current_buffer->foreign_semaphore != VK_NULL_HANDLE) {
360
+               semaphore_wait_infosemaphore_wait_info_len++ = (VkSemaphoreSubmitInfo) {
361
+                   .sType = VK_STRUCTURE_TYPE_SEMAPHORE_SUBMIT_INFO,
362
+                   .semaphore = current_buffer->foreign_semaphore,
363
+                   .stageMask = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
364
+               };
365
+           }
366
+       }
367
+   }
368
+
369
+        vkCmdPipelineBarrier(s->commandBuffer,
370
+               VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
371
+               VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
372
+               0, 0, NULL, 0, NULL,
373
+               s->n_streams, acquire_barrier);
374
+
375
+        vkCmdPipelineBarrier(s->commandBuffer,
376
+               VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
377
+               VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT,
378
+               0, 0, NULL, 0, NULL,
379
+               s->n_streams, release_barrier);
380
+
381
+   VK_CHECK_RESULT(vkEndCommandBuffer(s->commandBuffer));
382
+
383
+   VK_CHECK_RESULT(vkResetFences(s->base.device, 1, &s->fence));
384
+
385
+   if (s->pipelineSemaphore == VK_NULL_HANDLE) {
386
+       VkExportSemaphoreCreateInfo export_info = {
387
+           .sType = VK_STRUCTURE_TYPE_EXPORT_SEMAPHORE_CREATE_INFO,
388
+           .handleTypes = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT,
389
+       };
390
+       VkSemaphoreCreateInfo semaphore_info = {
391
+           .sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO,
392
+           .pNext = &export_info,
393
+       };
394
+       VK_CHECK_RESULT(vkCreateSemaphore(s->base.device, &semaphore_info, NULL, &s->pipelineSemaphore));
395
+   }
396
+
397
+   semaphore_signal_infosemaphore_signal_info_len++ = (VkSemaphoreSubmitInfo) {
398
+       .sType = VK_STRUCTURE_TYPE_SEMAPHORE_SUBMIT_INFO,
399
+       .semaphore = s->pipelineSemaphore,
400
+   };
401
+
402
+   VkCommandBufferSubmitInfoKHR commandBufferInfo = {
403
+       .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_SUBMIT_INFO,
404
+       .commandBuffer = s->commandBuffer,
405
+   };
406
+
407
+   const VkSubmitInfo2KHR submitInfo = {
408
+       .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO_2_KHR,
409
+       .commandBufferInfoCount = 1,
410
+       .pCommandBufferInfos = &commandBufferInfo,
411
+       .waitSemaphoreInfoCount = semaphore_wait_info_len,
412
+       .pWaitSemaphoreInfos = semaphore_wait_info,
413
+       .signalSemaphoreInfoCount = semaphore_signal_info_len,
414
+       .pSignalSemaphoreInfos = semaphore_signal_info,
415
+   };
416
+        VK_CHECK_RESULT(vkQueueSubmit2KHR(s->base.queue, 1, &submitInfo, s->fence));
417
+   s->started = true;
418
+
419
+   VkSemaphoreGetFdInfoKHR get_fence_fd_info = {
420
+       .sType = VK_STRUCTURE_TYPE_SEMAPHORE_GET_FD_INFO_KHR,
421
+       .semaphore = s->pipelineSemaphore,
422
+       .handleType = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT,
423
+   };
424
+   int sync_file_fd = -1;
425
+   VK_CHECK_RESULT(vkGetSemaphoreFdKHR(s->base.device, &get_fence_fd_info, &sync_file_fd));
426
+
427
+   int ret = 1;
428
+   for (uint32_t i = 0; i < s->n_streams; i++) {
429
+       struct vulkan_stream *p = &s->streamsi;
430
+       struct spa_buffer *current_spa_buffer = p->spa_buffersp->current_buffer_id;
431
+
432
+       if (current_spa_buffer->datas0.type != SPA_DATA_DmaBuf)
433
+           continue;
434
+
435
+       if (!vulkan_sync_export_dmabuf(&s->base, &p->buffersp->current_buffer_id, sync_file_fd)) {
436
+           ret = 0;
437
+       }
438
+   }
439
+   close(sync_file_fd);
440
+
441
+   return ret;
442
+}
443
+
444
+static void clear_buffers(struct vulkan_compute_state *s, struct vulkan_stream *p)
445
+{
446
+   uint32_t i;
447
+
448
+   for (i = 0; i < p->n_buffers; i++) {
449
+       vulkan_buffer_clear(&s->base, &p->buffersi);
450
+       p->spa_buffersi = NULL;
451
+   }
452
+   p->n_buffers = 0;
453
+}
454
+
455
+static void clear_streams(struct vulkan_compute_state *s)
456
+{
457
+   uint32_t i;
458
+   for (i = 0; i < s->n_streams; i++) {
459
+       struct vulkan_stream *p = &s->streamsi;
460
+       clear_buffers(s, p);
461
+   }
462
+}
463
+
464
+int spa_vulkan_fixate_modifier(struct vulkan_compute_state *s, struct vulkan_stream *p, struct spa_video_info_dsp *dsp_info,
465
+       uint32_t modifierCount, uint64_t *modifiers, uint64_t *modifier)
466
+{
467
+   VkFormat format = vulkan_id_to_vkformat(dsp_info->format);
468
+   if (format == VK_FORMAT_UNDEFINED) {
469
+       return -1;
470
+   }
471
+
472
+   struct dmabuf_fixation_info fixation_info = {
473
+       .format = format,
474
+       .modifierCount = modifierCount,
475
+       .modifiers = modifiers,
476
+       .size.width = s->constants.width,
477
+       .size.height = s->constants.height,
478
+       .usage = VK_IMAGE_USAGE_STORAGE_BIT,
479
+   };
480
+   return vulkan_fixate_modifier(&s->base, &fixation_info, modifier);
481
+}
482
+
483
+int spa_vulkan_use_buffers(struct vulkan_compute_state *s, struct vulkan_stream *p, uint32_t flags,
484
+       struct spa_video_info_dsp *dsp_info, uint32_t n_buffers, struct spa_buffer **buffers)
485
+{
486
+   VkFormat format = vulkan_id_to_vkformat(dsp_info->format);
487
+   if (format == VK_FORMAT_UNDEFINED)
488
+       return -1;
489
+
490
+   vulkan_wait_idle(&s->base);
491
+   clear_buffers(s, p);
492
+
493
+   bool alloc = flags & SPA_NODE_BUFFERS_FLAG_ALLOC;
494
+   int ret;
495
+   p->n_buffers = 0;
496
+   for (uint32_t i = 0; i < n_buffers; i++) {
497
+       if (alloc) {
498
+           if (SPA_FLAG_IS_SET(buffersi->datas0.type, 1<<SPA_DATA_DmaBuf)) {
499
+               struct external_buffer_info dmabufInfo = {
500
+                   .format = format,
501
+                   .modifier = dsp_info->modifier,
502
+                   .size.width = s->constants.width,
503
+                   .size.height = s->constants.height,
504
+                   .usage = p->direction == SPA_DIRECTION_OUTPUT
505
+                       ? VK_IMAGE_USAGE_STORAGE_BIT
506
+                       : VK_IMAGE_USAGE_SAMPLED_BIT,
507
+                   .spa_buf = buffersi,
508
+               };
509
+               ret = vulkan_create_dmabuf(&s->base, &dmabufInfo, &p->buffersi);
510
+           } else {
511
+               spa_log_error(s->log, "Unsupported buffer type mask %d", buffersi->datas0.type);
512
+               return -1;
513
+           }
514
+       } else {
515
+           switch (buffersi->datas0.type) {
516
+           case SPA_DATA_DmaBuf:;
517
+               struct external_buffer_info dmabufInfo = {
518
+                   .format = format,
519
+                   .modifier = dsp_info->modifier,
520
+                   .size.width = s->constants.width,
521
+                   .size.height = s->constants.height,
522
+                   .usage = p->direction == SPA_DIRECTION_OUTPUT
523
+                       ? VK_IMAGE_USAGE_STORAGE_BIT
524
+                       : VK_IMAGE_USAGE_SAMPLED_BIT,
525
+                   .spa_buf = buffersi,
526
+               };
527
+               ret = vulkan_import_dmabuf(&s->base, &dmabufInfo, &p->buffersi);
528
+               break;
529
+           case SPA_DATA_MemPtr:;
530
+               struct external_buffer_info memptrInfo = {
531
+                   .format = format,
532
+                   .size.width = s->constants.width,
533
+                   .size.height = s->constants.height,
534
+                   .usage = p->direction == SPA_DIRECTION_OUTPUT
535
+                       ? VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT
536
+                       : VK_IMAGE_USAGE_SAMPLED_BIT,
537
+                   .spa_buf = buffersi,
538
+               };
539
+               ret = vulkan_import_memptr(&s->base, &memptrInfo, &p->buffersi);
540
+               break;
541
+           default:
542
+               spa_log_error(s->log, "Unsupported buffer type %d", buffersi->datas0.type);
543
+               return -1;
544
+           }
545
+       }
546
+       if (ret != 0) {
547
+           spa_log_error(s->log, "Failed to use buffer %d", i);
548
+           return ret;
549
+       }
550
+       p->spa_buffersi = buffersi;
551
+       p->n_buffers++;
552
+   }
553
+
554
+   return 0;
555
+}
556
+
557
+int spa_vulkan_init_stream(struct vulkan_compute_state *s, struct vulkan_stream *stream,
558
+       enum spa_direction direction, struct spa_dict *props)
559
+{
560
+   return vulkan_stream_init(stream, direction, props);
561
+}
562
+
563
+int spa_vulkan_prepare(struct vulkan_compute_state *s)
564
+{
565
+   if (!s->prepared) {
566
+       CHECK(createFence(s));
567
+       CHECK(createDescriptors(s));
568
+       CHECK(createComputePipeline(s, s->shaderName));
569
+       CHECK(createCommandBuffer(s));
570
+       s->prepared = true;
571
+   }
572
+   return 0;
573
+}
574
+
575
+int spa_vulkan_unprepare(struct vulkan_compute_state *s)
576
+{
577
+   if (s->prepared) {
578
+       vkDestroyShaderModule(s->base.device, s->computeShaderModule, NULL);
579
+       vkDestroySampler(s->base.device, s->sampler, NULL);
580
+       vkDestroyDescriptorPool(s->base.device, s->descriptorPool, NULL);
581
+       vkDestroyDescriptorSetLayout(s->base.device, s->descriptorSetLayout, NULL);
582
+       vkDestroyPipelineLayout(s->base.device, s->pipelineLayout, NULL);
583
+       vkDestroyPipeline(s->base.device, s->pipeline, NULL);
584
+       vkDestroyCommandPool(s->base.device, s->commandPool, NULL);
585
+       vkDestroyFence(s->base.device, s->fence, NULL);
586
+       s->prepared = false;
587
+   }
588
+   return 0;
589
+}
590
+
591
+int spa_vulkan_start(struct vulkan_compute_state *s)
592
+{
593
+   uint32_t i;
594
+
595
+   for (i = 0; i < s->n_streams; i++) {
596
+       struct vulkan_stream *p = &s->streamsi;
597
+       p->current_buffer_id = SPA_ID_INVALID;
598
+       p->busy_buffer_id = SPA_ID_INVALID;
599
+       p->ready_buffer_id = SPA_ID_INVALID;
600
+   }
601
+   return 0;
602
+}
603
+
604
+int spa_vulkan_stop(struct vulkan_compute_state *s)
605
+{
606
+        VK_CHECK_RESULT(vkDeviceWaitIdle(s->base.device));
607
+   clear_streams(s);
608
+   s->started = false;
609
+   return 0;
610
+}
611
+
612
+int spa_vulkan_ready(struct vulkan_compute_state *s)
613
+{
614
+   uint32_t i;
615
+   VkResult result;
616
+
617
+   if (!s->started)
618
+       return 0;
619
+
620
+   result = vkGetFenceStatus(s->base.device, s->fence);
621
+   if (result == VK_NOT_READY)
622
+       return -EBUSY;
623
+   VK_CHECK_RESULT(result);
624
+
625
+   s->started = false;
626
+
627
+   for (i = 0; i < s->n_streams; i++) {
628
+       struct vulkan_stream *p = &s->streamsi;
629
+       p->ready_buffer_id = p->busy_buffer_id;
630
+       p->busy_buffer_id = SPA_ID_INVALID;
631
+   }
632
+   return 0;
633
+}
634
+
635
+int spa_vulkan_process(struct vulkan_compute_state *s)
636
+{
637
+   CHECK(updateDescriptors(s));
638
+   CHECK(runCommandBuffer(s));
639
+        VK_CHECK_RESULT(vkDeviceWaitIdle(s->base.device));
640
+   CHECK(runExportSHMBuffers(s));
641
+
642
+   return 0;
643
+}
644
+
645
+int spa_vulkan_get_buffer_caps(struct vulkan_compute_state *s, enum spa_direction direction)
646
+{
647
+   switch (direction) {
648
+   case SPA_DIRECTION_INPUT:
649
+       return VULKAN_BUFFER_TYPE_CAP_DMABUF;
650
+   case SPA_DIRECTION_OUTPUT:
651
+       return VULKAN_BUFFER_TYPE_CAP_DMABUF | VULKAN_BUFFER_TYPE_CAP_SHM;
652
+   }
653
+   return 0;
654
+}
655
+
656
+struct vulkan_modifier_info *spa_vulkan_get_modifier_info(struct vulkan_compute_state *s, struct spa_video_info_dsp *info) {
657
+   VkFormat vk_format = vulkan_id_to_vkformat(info->format);
658
+   return vulkan_modifierInfo_find(&s->base, vk_format, info->modifier);
659
+}
660
+
661
+int spa_vulkan_init(struct vulkan_compute_state *s)
662
+{
663
+   s->base.log = s->log;
664
+   uint32_t dsp_format = SPA_VIDEO_FORMAT_DSP_F32;
665
+   struct vulkan_base_info baseInfo = {
666
+       .queueFlags = VK_QUEUE_COMPUTE_BIT,
667
+       .formatInfo.formatCount = 1,
668
+       .formatInfo.formats = &dsp_format,
669
+   };
670
+   return vulkan_base_init(&s->base, &baseInfo);
671
+}
672
+
673
+void spa_vulkan_deinit(struct vulkan_compute_state *s)
674
+{
675
+   vulkan_base_deinit(&s->base);
676
+}
677
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
1226
 
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
+       VkDrmFormatModifierPropertiesListEXT modPropsList = {
202
+           .sType = VK_STRUCTURE_TYPE_DRM_FORMAT_MODIFIER_PROPERTIES_LIST_EXT,
203
+       };
204
+       VkFormatProperties2 fmtProps = {
205
+           .sType = VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2,
206
+           .pNext = &modPropsList,
207
+       };
208
+       vkGetPhysicalDeviceFormatProperties2(s->physicalDevice, format, &fmtProps);
209
 
210
-   VkDescriptorPoolSize descriptorPoolSizes2 = {
211
-       {
212
-           .type = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE,
213
-           .descriptorCount = 1,
214
-       },
215
-       {
216
-           .type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
217
-           .descriptorCount = s->n_streams - 1,
218
-       },
219
-   };
220
-   const VkDescriptorPoolCreateInfo descriptorPoolCreateInfo = {
221
-       .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO,
222
-       .maxSets = s->n_streams,
223
-       .poolSizeCount = s->n_streams > 1 ? 2 : 1,
224
-       .pPoolSizes = descriptorPoolSizes,
225
-   };
226
+       if (!modPropsList.drmFormatModifierCount)
227
+           continue;
228
 
229
-        VK_CHECK_RESULT(vkCreateDescriptorPool(s->device,
230
-               &descriptorPoolCreateInfo, NULL,
231
-               &s->descriptorPool));
232
+       modPropsList.pDrmFormatModifierProperties = calloc(modPropsList.drmFormatModifierCount,
233
+               sizeof(modPropsList.pDrmFormatModifierProperties0));
234
+       if (!modPropsList.pDrmFormatModifierProperties)
235
+           continue;
236
+       vkGetPhysicalDeviceFormatProperties2(s->physicalDevice, format, &fmtProps);
237
 
238
-   VkDescriptorSetLayoutBinding descriptorSetLayoutBindings->n_streams;
239
-   descriptorSetLayoutBinding0 = (VkDescriptorSetLayoutBinding) {
240
-       .binding = 0,
241
-       .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE,
242
-       .descriptorCount = 1,
243
-       .stageFlags = VK_SHADER_STAGE_COMPUTE_BIT
244
-   };
245
-   for (i = 1; i < s->n_streams; i++) {
246
-       descriptorSetLayoutBindingi = (VkDescriptorSetLayoutBinding) {
247
-           .binding = i,
248
-           .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
249
-           .descriptorCount = 1,
250
-           .stageFlags = VK_SHADER_STAGE_COMPUTE_BIT
251
-       };
252
-   };
253
-   const VkDescriptorSetLayoutCreateInfo descriptorSetLayoutCreateInfo = {
254
-       .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
255
-       .bindingCount = s->n_streams,
256
-       .pBindings = descriptorSetLayoutBinding
257
-   };
258
-   VK_CHECK_RESULT(vkCreateDescriptorSetLayout(s->device,
259
-               &descriptorSetLayoutCreateInfo, NULL,
260
-               &s->descriptorSetLayout));
261
-
262
-   const VkDescriptorSetAllocateInfo descriptorSetAllocateInfo = {
263
-       .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO,
264
-       .descriptorPool = s->descriptorPool,
265
-       .descriptorSetCount = 1,
266
-       .pSetLayouts = &s->descriptorSetLayout
267
-   };
268
+       f_info->infos = calloc(modPropsList.drmFormatModifierCount, sizeof(f_info->infos0));
269
+       if (!f_info->infos) {
270
+           free(modPropsList.pDrmFormatModifierProperties);
271
+           continue;
272
+       }
273
 
274
-   VK_CHECK_RESULT(vkAllocateDescriptorSets(s->device,
275
-               &descriptorSetAllocateInfo,
276
-               &s->descriptorSet));
277
-
278
-   const VkSamplerCreateInfo samplerInfo = {
279
-       .sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
280
-       .magFilter = VK_FILTER_LINEAR,
281
-       .minFilter = VK_FILTER_LINEAR,
282
-       .mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR,
283
-       .addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,
284
-       .addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,
285
-       .addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,
286
-       .borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_BLACK,
287
-       .unnormalizedCoordinates = VK_FALSE,
288
-       .compareEnable = VK_FALSE,
289
-       .compareOp = VK_COMPARE_OP_ALWAYS,
290
-       .mipLodBias = 0.0f,
291
-       .minLod = 0,
292
-       .maxLod = 5,
293
-   };
294
-   VK_CHECK_RESULT(vkCreateSampler(s->device, &samplerInfo, NULL, &s->sampler));
295
+       for (uint32_t j = 0; j < modPropsList.drmFormatModifierCount; j++) {
296
+           VkDrmFormatModifierPropertiesEXT props = modPropsList.pDrmFormatModifierPropertiesj;
297
 
298
-   return 0;
299
-}
300
+           if (!(props.drmFormatModifierTilingFeatures & VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT))
301
+               continue;
302
 
303
-static int updateDescriptors(struct vulkan_state *s)
304
-{
305
-   uint32_t i;
306
-   VkDescriptorImageInfo descriptorImageInfos->n_streams;
307
-   VkWriteDescriptorSet writeDescriptorSets->n_streams;
308
+           if (props.drmFormatModifierPlaneCount > DMABUF_MAX_PLANES)
309
+               continue;
310
 
311
-   for (i = 0; i < s->n_streams; i++) {
312
-       struct vulkan_stream *p = &s->streamsi;
313
+           VkPhysicalDeviceImageDrmFormatModifierInfoEXT modInfo = {
314
+               .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_DRM_FORMAT_MODIFIER_INFO_EXT,
315
+               .drmFormatModifier = props.drmFormatModifier,
316
+               .sharingMode = VK_SHARING_MODE_EXCLUSIVE,
317
+           };
318
+           VkPhysicalDeviceExternalImageFormatInfo extImgFmtInfo = {
319
+               .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_IMAGE_FORMAT_INFO,
320
+               .pNext = &modInfo,
321
+               .handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT,
322
+           };
323
+           VkPhysicalDeviceImageFormatInfo2 imgFmtInfo = {
324
+               .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_FORMAT_INFO_2,
325
+               .pNext = &extImgFmtInfo,
326
+               .type = VK_IMAGE_TYPE_2D,
327
+               .format = format,
328
+               .usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT,
329
+               .tiling = VK_IMAGE_TILING_DRM_FORMAT_MODIFIER_EXT,
330
+           };
331
 
332
-       if (p->current_buffer_id == p->pending_buffer_id ||
333
-           p->pending_buffer_id == SPA_ID_INVALID)
334
-           continue;
335
+           VkExternalImageFormatProperties extImgFmtProps = {
336
+               .sType = VK_STRUCTURE_TYPE_EXTERNAL_IMAGE_FORMAT_PROPERTIES,
337
+           };
338
+           VkImageFormatProperties2 imgFmtProps = {
339
+               .sType = VK_STRUCTURE_TYPE_IMAGE_FORMAT_PROPERTIES_2,
340
+               .pNext = &extImgFmtProps,
341
+           };
342
 
343
-       p->current_buffer_id = p->pending_buffer_id;
344
-       p->busy_buffer_id = p->current_buffer_id;
345
-       p->pending_buffer_id = SPA_ID_INVALID;
346
+           VK_CHECK_RESULT_LOOP(vkGetPhysicalDeviceImageFormatProperties2(s->physicalDevice, &imgFmtInfo, &imgFmtProps))
347
 
348
-       descriptorImageInfoi = (VkDescriptorImageInfo) {
349
-           .sampler = s->sampler,
350
-           .imageView = p->buffersp->current_buffer_id.view,
351
-           .imageLayout = VK_IMAGE_LAYOUT_GENERAL,
352
-       };
353
-       writeDescriptorSeti = (VkWriteDescriptorSet) {
354
-           .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
355
-           .dstSet = s->descriptorSet,
356
-           .dstBinding = i,
357
-           .descriptorCount = 1,
358
-           .descriptorType = i == 0 ?
359
-               VK_DESCRIPTOR_TYPE_STORAGE_IMAGE :
360
-               VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
361
-           .pImageInfo = &descriptorImageInfoi,
362
-       };
363
-   }
364
-   vkUpdateDescriptorSets(s->device, s->n_streams,
365
-               writeDescriptorSet, 0, NULL);
366
+           VkExternalMemoryFeatureFlags extMemFeatures =
367
+               extImgFmtProps.externalMemoryProperties.externalMemoryFeatures;
368
+           if (!(extMemFeatures & VK_EXTERNAL_MEMORY_FEATURE_EXPORTABLE_BIT)) {
369
+               continue;
370
+           }
371
+
372
+           VkExtent3D max_extent = imgFmtProps.imageFormatProperties.maxExtent;
373
+           f_info->infosf_info->modifierCount++ = (struct vulkan_modifier_info){
374
+               .props = props,
375
+               .max_extent = { .width = max_extent.width, .height = max_extent.height },
376
+           };
377
 
378
+       }
379
+       free(modPropsList.pDrmFormatModifierProperties);
380
+   }
381
    return 0;
382
 }
383
 
384
-static VkShaderModule createShaderModule(struct vulkan_state *s, const char* shaderFile)
385
+static void destroyFormatInfo(struct vulkan_base *s)
386
 {
387
-   VkShaderModule shaderModule = VK_NULL_HANDLE;
388
-   VkResult result;
389
-   void *data;
390
-   int fd;
391
-   struct stat stat;
392
-
393
-   if ((fd = open(shaderFile, 0, O_RDONLY)) == -1) {
394
-       spa_log_error(s->log, "can't open %s: %m", shaderFile);
395
-       return VK_NULL_HANDLE;
396
+   for (uint32_t i = 0; i < s->formatInfoCount; i++) {
397
+       free(s->formatInfosi.infos);
398
    }
399
-   if (fstat(fd, &stat) < 0) {
400
-       spa_log_error(s->log, "can't stat %s: %m", shaderFile);
401
-       close(fd);
402
-       return VK_NULL_HANDLE;
403
+   free(s->formatInfos);
404
+   s->formatInfos = NULL;
405
+   s->formatInfoCount = 0;
406
+}
407
+
408
+int vulkan_read_pixels(struct vulkan_base *s, struct vulkan_read_pixels_info *info, struct vulkan_buffer *vk_buf)
409
+{
410
+   VkImageSubresource img_sub_res = {
411
+       .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
412
+       .arrayLayer = 0,
413
+       .mipLevel = 0,
414
+   };
415
+   VkSubresourceLayout img_sub_layout;
416
+   vkGetImageSubresourceLayout(s->device, vk_buf->image, &img_sub_res, &img_sub_layout);
417
+
418
+   void *v;
419
+   VK_CHECK_RESULT(vkMapMemory(s->device, vk_buf->memory, 0, VK_WHOLE_SIZE, 0, &v));
420
+
421
+   const char *d = (const char *)v + img_sub_layout.offset;
422
+   unsigned char *p = (unsigned char *)info->data + info->offset;
423
+   uint32_t bytes_per_pixel = 16;
424
+   uint32_t pack_stride = img_sub_layout.rowPitch;
425
+   if (pack_stride == info->stride) {
426
+       memcpy(p, d, info->stride * info->size.height);
427
+   } else {
428
+       for (uint32_t i = 0; i < info->size.height; i++) {
429
+           memcpy(p + i * info->stride, d + i * pack_stride, info->size.width * bytes_per_pixel);
430
+       }
431
    }
432
+   vkUnmapMemory(s->device, vk_buf->memory);
433
+   return 0;
434
+}
435
 
436
-   data = mmap(NULL, stat.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
437
+int vulkan_sync_foreign_dmabuf(struct vulkan_base *s, struct vulkan_buffer *vk_buf)
438
+{
439
+   VULKAN_INSTANCE_FUNCTION(vkImportSemaphoreFdKHR);
440
 
441
-   const VkShaderModuleCreateInfo shaderModuleCreateInfo = {
442
-       .sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO,
443
-       .codeSize = stat.st_size,
444
-       .pCode = data,
445
-   };
446
-   result = vkCreateShaderModule(s->device,
447
-           &shaderModuleCreateInfo, 0, &shaderModule);
448
+   if (!s->implicit_sync_interop) {
449
+       struct pollfd pollfd = {
450
+           .fd = vk_buf->fd,
451
+           .events = POLLIN,
452
+       };
453
+       int timeout_ms = 1000;
454
+       int ret = poll(&pollfd, 1, timeout_ms);
455
+       if (ret < 0) {
456
+           spa_log_error(s->log, "Failed to wait for DMA-BUF fence");
457
+           return -1;
458
+       } else if (ret == 0) {
459
+           spa_log_error(s->log, "Timed out waiting for DMA-BUF fence");
460
+           return -1;
461
+       }
462
+       return 0;
463
+   }
464
 
465
-   munmap(data, stat.st_size);
466
-   close(fd);
467
+   int sync_file_fd = dmabuf_export_sync_file(s->log, vk_buf->fd, DMA_BUF_SYNC_READ);
468
+   if (sync_file_fd < 0) {
469
+       spa_log_error(s->log, "Failed to extract for DMA-BUF fence");
470
+       return -1;
471
+   }
472
 
473
-   if (result != VK_SUCCESS) {
474
-       spa_log_error(s->log, "can't create shader %s: %m", shaderFile);
475
-       return VK_NULL_HANDLE;
476
+   if (vk_buf->foreign_semaphore == VK_NULL_HANDLE) {
477
+       VkSemaphoreCreateInfo semaphore_info = {
478
+           .sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO,
479
+       };
480
+       VK_CHECK_RESULT_WITH_CLEANUP(vkCreateSemaphore(s->device, &semaphore_info, NULL, &vk_buf->foreign_semaphore), close(sync_file_fd));
481
    }
482
-   return shaderModule;
483
-}
484
 
485
-static int createComputePipeline(struct vulkan_state *s, const char *shader_file)
486
-{
487
-   static const VkPushConstantRange range = {
488
-       .stageFlags = VK_SHADER_STAGE_COMPUTE_BIT,
489
-       .offset = 0,
490
-       .size = sizeof(struct push_constants)
491
+   VkImportSemaphoreFdInfoKHR import_info = {
492
+       .sType = VK_STRUCTURE_TYPE_IMPORT_SEMAPHORE_FD_INFO_KHR,
493
+       .handleType = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT,
494
+       .flags = VK_SEMAPHORE_IMPORT_TEMPORARY_BIT_KHR,
495
+       .semaphore = vk_buf->foreign_semaphore,
496
+       .fd = sync_file_fd,
497
    };
498
+   VK_CHECK_RESULT_WITH_CLEANUP(vkImportSemaphoreFdKHR(s->device, &import_info), close(sync_file_fd));
499
 
500
-   const VkPipelineLayoutCreateInfo pipelineLayoutCreateInfo = {
501
-       .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
502
-       .setLayoutCount = 1,
503
-       .pSetLayouts = &s->descriptorSetLayout,
504
-       .pushConstantRangeCount = 1,
505
-       .pPushConstantRanges = &range,
506
-   };
507
-   VK_CHECK_RESULT(vkCreatePipelineLayout(s->device,
508
-               &pipelineLayoutCreateInfo, NULL,
509
-               &s->pipelineLayout));
510
-
511
-        s->computeShaderModule = createShaderModule(s, shader_file);
512
-   if (s->computeShaderModule == VK_NULL_HANDLE)
513
-       return -ENOENT;
514
-
515
-   const VkPipelineShaderStageCreateInfo shaderStageCreateInfo = {
516
-       .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
517
-       .stage = VK_SHADER_STAGE_COMPUTE_BIT,
518
-       .module = s->computeShaderModule,
519
-       .pName = "main",
520
-   };
521
-   const VkComputePipelineCreateInfo pipelineCreateInfo = {
522
-       .sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO,
523
-       .stage = shaderStageCreateInfo,
524
-       .layout = s->pipelineLayout,
525
-   };
526
-   VK_CHECK_RESULT(vkCreateComputePipelines(s->device, VK_NULL_HANDLE,
527
-               1, &pipelineCreateInfo, NULL,
528
-               &s->pipeline));
529
    return 0;
530
 }
531
 
532
-static int createCommandBuffer(struct vulkan_state *s)
533
+bool vulkan_sync_export_dmabuf(struct vulkan_base *s, struct vulkan_buffer *vk_buf, int sync_file_fd)
534
+{
535
+   if (!s->implicit_sync_interop)
536
+       return false;
537
+
538
+   return dmabuf_import_sync_file(s->log, vk_buf->fd, DMA_BUF_SYNC_WRITE, sync_file_fd);
539
+}
540
+
541
+int vulkan_commandPool_create(struct vulkan_base *s, VkCommandPool *commandPool)
542
 {
543
    const VkCommandPoolCreateInfo commandPoolCreateInfo = {
544
        .sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
545
@@ -452,217 +429,396 @@
546
    };
547
         VK_CHECK_RESULT(vkCreateCommandPool(s->device,
548
                &commandPoolCreateInfo, NULL,
549
-               &s->commandPool));
550
+               commandPool));
551
 
552
+   return 0;
553
+}
554
+
555
+int vulkan_commandBuffer_create(struct vulkan_base *s, VkCommandPool commandPool, VkCommandBuffer *commandBuffer)
556
+{
557
    const VkCommandBufferAllocateInfo commandBufferAllocateInfo = {
558
        .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
559
-       .commandPool = s->commandPool,
560
+       .commandPool = commandPool,
561
        .level = VK_COMMAND_BUFFER_LEVEL_PRIMARY,
562
        .commandBufferCount = 1,
563
    };
564
         VK_CHECK_RESULT(vkAllocateCommandBuffers(s->device,
565
                &commandBufferAllocateInfo,
566
-               &s->commandBuffer));
567
+               commandBuffer));
568
 
569
    return 0;
570
 }
571
 
572
-static int runCommandBuffer(struct vulkan_state *s)
573
+uint32_t vulkan_memoryType_find(struct vulkan_base *s,
574
+       uint32_t memoryTypeBits, VkMemoryPropertyFlags properties)
575
 {
576
-   static const VkCommandBufferBeginInfo beginInfo = {
577
-       .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
578
-       .flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT,
579
-   };
580
-   VK_CHECK_RESULT(vkBeginCommandBuffer(s->commandBuffer, &beginInfo));
581
-
582
-   VkImageMemoryBarrier barriers->n_streams;
583
    uint32_t i;
584
+   VkPhysicalDeviceMemoryProperties memoryProperties;
585
 
586
-   for (i = 0; i < s->n_streams; i++) {
587
-       struct vulkan_stream *p = &s->streamsi;
588
-
589
-       barrieri= (VkImageMemoryBarrier) {
590
-           .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
591
-           .subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
592
-           .subresourceRange.levelCount = 1,
593
-           .subresourceRange.layerCount = 1,
594
-           .oldLayout = VK_IMAGE_LAYOUT_UNDEFINED,
595
-           .newLayout = VK_IMAGE_LAYOUT_GENERAL,
596
-           .srcAccessMask = 0,
597
-           .dstAccessMask = 0,
598
-           .image = p->buffersp->current_buffer_id.image,
599
-       };
600
+   vkGetPhysicalDeviceMemoryProperties(s->physicalDevice, &memoryProperties);
601
+
602
+   for (i = 0; i < memoryProperties.memoryTypeCount; i++) {
603
+       if ((memoryTypeBits & (1 << i)) &&
604
+           ((memoryProperties.memoryTypesi.propertyFlags & properties) == properties))
605
+           return i;
606
    }
607
+   return -1;
608
+}
609
 
610
-        vkCmdPipelineBarrier(s->commandBuffer,
611
-               VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
612
-               VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
613
-               0, 0, NULL, 0, NULL,
614
-               s->n_streams, barrier);
615
+struct vulkan_format_info *vulkan_formatInfo_find(struct vulkan_base *s, VkFormat format)
616
+{
617
+   for (uint32_t i = 0; i < s->formatInfoCount; i++) {
618
+       if (s->formatInfosi.vk_format == format)
619
+           return &s->formatInfosi;
620
+   }
621
+   return NULL;
622
+}
623
 
624
-   vkCmdBindPipeline(s->commandBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, s->pipeline);
625
-   vkCmdPushConstants (s->commandBuffer,
626
-           s->pipelineLayout, VK_SHADER_STAGE_COMPUTE_BIT,
627
-           0, sizeof(struct push_constants), (const void *) &s->constants);
628
-   vkCmdBindDescriptorSets(s->commandBuffer, VK_PIPELINE_BIND_POINT_COMPUTE,
629
-           s->pipelineLayout, 0, 1, &s->descriptorSet, 0, NULL);
630
+struct vulkan_modifier_info *vulkan_modifierInfo_find(struct vulkan_base *s, VkFormat format, uint64_t mod)
631
+{
632
+   struct vulkan_format_info *f_info = vulkan_formatInfo_find(s, format);
633
+   if (!f_info)
634
+       return NULL;
635
+   for (uint32_t i = 0; i < f_info->modifierCount; i++) {
636
+       if (f_info->infosi.props.drmFormatModifier == mod)
637
+           return &f_info->infosi;
638
+   }
639
+   return NULL;
640
+}
641
+
642
+void vulkan_buffer_clear(struct vulkan_base *s, struct vulkan_buffer *buffer)
643
+{
644
+   if (buffer->fd != -1)
645
+       close(buffer->fd);
646
+   vkFreeMemory(s->device, buffer->memory, NULL);
647
+   vkDestroyImage(s->device, buffer->image, NULL);
648
+   vkDestroyImageView(s->device, buffer->view, NULL);
649
+}
650
+
651
+static VkImageAspectFlagBits mem_plane_aspect(uint32_t i)
652
+{
653
+   switch (i) {
654
+   case 0: return VK_IMAGE_ASPECT_MEMORY_PLANE_0_BIT_EXT;
655
+   case 1: return VK_IMAGE_ASPECT_MEMORY_PLANE_1_BIT_EXT;
656
+   case 2: return VK_IMAGE_ASPECT_MEMORY_PLANE_2_BIT_EXT;
657
+   case 3: return VK_IMAGE_ASPECT_MEMORY_PLANE_3_BIT_EXT;
658
+   default: abort(); // unreachable
659
+   }
660
+}
661
 
662
-   vkCmdDispatch(s->commandBuffer,
663
-           (uint32_t)ceil(s->constants.width / (float)WORKGROUP_SIZE),
664
-           (uint32_t)ceil(s->constants.height / (float)WORKGROUP_SIZE), 1);
665
+static int allocate_dmabuf(struct vulkan_base *s, VkFormat format, uint32_t modifierCount, uint64_t *modifiers, VkImageUsageFlags usage, struct spa_rectangle *size, struct vulkan_buffer *vk_buf)
666
+{
667
+   VkImageDrmFormatModifierListCreateInfoEXT imageDrmFormatModifierListCreateInfo = {
668
+       .sType = VK_STRUCTURE_TYPE_IMAGE_DRM_FORMAT_MODIFIER_LIST_CREATE_INFO_EXT,
669
+       .drmFormatModifierCount = modifierCount,
670
+       .pDrmFormatModifiers = modifiers,
671
+   };
672
 
673
-   VK_CHECK_RESULT(vkEndCommandBuffer(s->commandBuffer));
674
+   VkExternalMemoryImageCreateInfo extMemoryImageCreateInfo = {
675
+       .sType = VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO,
676
+       .handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT,
677
+   };
678
+   extMemoryImageCreateInfo.pNext = &imageDrmFormatModifierListCreateInfo;
679
+
680
+   VkImageCreateInfo imageCreateInfo = {
681
+       .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
682
+       .imageType = VK_IMAGE_TYPE_2D,
683
+       .format = format,
684
+       .extent.width = size->width,
685
+       .extent.height = size->height,
686
+       .extent.depth = 1,
687
+       .mipLevels = 1,
688
+       .arrayLayers = 1,
689
+       .samples = VK_SAMPLE_COUNT_1_BIT,
690
+       .tiling = VK_IMAGE_TILING_DRM_FORMAT_MODIFIER_EXT,
691
+       .usage = usage,
692
+       .sharingMode = VK_SHARING_MODE_EXCLUSIVE,
693
+       .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
694
+   };
695
+   imageCreateInfo.pNext = &extMemoryImageCreateInfo;
696
 
697
-   VK_CHECK_RESULT(vkResetFences(s->device, 1, &s->fence));
698
+   VK_CHECK_RESULT(vkCreateImage(s->device,
699
+               &imageCreateInfo, NULL, &vk_buf->image));
700
 
701
-   const VkSubmitInfo submitInfo = {
702
-       .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
703
-       .commandBufferCount = 1,
704
-       .pCommandBuffers = &s->commandBuffer,
705
+   VkMemoryRequirements memoryRequirements = {0};
706
+   vkGetImageMemoryRequirements(s->device,
707
+           vk_buf->image, &memoryRequirements);
708
+
709
+   VkExportMemoryAllocateInfo exportAllocateInfo = {
710
+       .sType = VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO,
711
+       .handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT,
712
    };
713
-        VK_CHECK_RESULT(vkQueueSubmit(s->queue, 1, &submitInfo, s->fence));
714
-   s->started = true;
715
+   VkMemoryAllocateInfo allocateInfo = {
716
+       .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
717
+       .allocationSize = memoryRequirements.size,
718
+       .memoryTypeIndex = vulkan_memoryType_find(s,
719
+                     memoryRequirements.memoryTypeBits,
720
+                     VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT),
721
+   };
722
+   allocateInfo.pNext = &exportAllocateInfo;
723
+
724
+   VK_CHECK_RESULT(vkAllocateMemory(s->device,
725
+           &allocateInfo, NULL, &vk_buf->memory));
726
+
727
+   VK_CHECK_RESULT(vkBindImageMemory(s->device,
728
+               vk_buf->image, vk_buf->memory, 0));
729
 
730
    return 0;
731
 }
732
 
733
-static void clear_buffers(struct vulkan_state *s, struct vulkan_stream *p)
734
+int vulkan_fixate_modifier(struct vulkan_base *s, struct dmabuf_fixation_info *info, uint64_t *modifier)
735
 {
736
-   uint32_t i;
737
+   VULKAN_INSTANCE_FUNCTION(vkGetImageDrmFormatModifierPropertiesEXT);
738
 
739
-   for (i = 0; i < p->n_buffers; i++) {
740
-       if (p->buffersi.fd != -1)
741
-           close(p->buffersi.fd);
742
-       vkFreeMemory(s->device, p->buffersi.memory, NULL);
743
-       vkDestroyImage(s->device, p->buffersi.image, NULL);
744
-       vkDestroyImageView(s->device, p->buffersi.view, NULL);
745
-   }
746
-   p->n_buffers = 0;
747
-}
748
+   struct vulkan_buffer vk_buf;
749
+   vk_buf.fd = -1;
750
+   vk_buf.view = VK_NULL_HANDLE;
751
+   VK_CHECK_RESULT(allocate_dmabuf(s, info->format, info->modifierCount, info->modifiers, info->usage, &info->size, &vk_buf));
752
 
753
-static void clear_streams(struct vulkan_state *s)
754
-{
755
-   uint32_t i;
756
-   for (i = 0; i < s->n_streams; i++) {
757
-       struct vulkan_stream *p = &s->streamsi;
758
-       clear_buffers(s, p);
759
-   }
760
+   VkImageDrmFormatModifierPropertiesEXT mod_prop = {
761
+       .sType = VK_STRUCTURE_TYPE_IMAGE_DRM_FORMAT_MODIFIER_PROPERTIES_EXT,
762
+   };
763
+   VK_CHECK_RESULT(vkGetImageDrmFormatModifierPropertiesEXT(s->device, vk_buf.image, &mod_prop));
764
+
765
+   *modifier = mod_prop.drmFormatModifier;
766
+
767
+   vulkan_buffer_clear(s, &vk_buf);
768
+
769
+   return 0;
770
 }
771
 
772
-int spa_vulkan_use_buffers(struct vulkan_state *s, struct vulkan_stream *p, uint32_t flags,
773
-       uint32_t n_buffers, struct spa_buffer **buffers)
774
+int vulkan_create_dmabuf(struct vulkan_base *s, struct external_buffer_info *info, struct vulkan_buffer *vk_buf)
775
 {
776
-   uint32_t i;
777
    VULKAN_INSTANCE_FUNCTION(vkGetMemoryFdKHR);
778
 
779
-   clear_buffers(s, p);
780
-
781
-   for (i = 0; i < n_buffers; i++) {
782
-       VkExternalMemoryImageCreateInfo extInfo;
783
-       VkImageCreateInfo imageCreateInfo = {
784
-           .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
785
-           .imageType = VK_IMAGE_TYPE_2D,
786
-           .format = VK_FORMAT_R32G32B32A32_SFLOAT,
787
-           .extent.width = s->constants.width,
788
-           .extent.height = s->constants.height,
789
-           .extent.depth = 1,
790
-           .mipLevels = 1,
791
-           .arrayLayers = 1,
792
-           .samples = VK_SAMPLE_COUNT_1_BIT,
793
-           .tiling = VK_IMAGE_TILING_LINEAR,
794
-           .usage = p->direction == SPA_DIRECTION_OUTPUT ?
795
-               VK_IMAGE_USAGE_STORAGE_BIT:
796
-               VK_IMAGE_USAGE_SAMPLED_BIT,
797
-           .sharingMode = VK_SHARING_MODE_EXCLUSIVE,
798
-           .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
799
-       };
800
+   if (info->spa_buf->n_datas != 1)
801
+       return -1;
802
 
803
-       if (!(flags & SPA_NODE_BUFFERS_FLAG_ALLOC)) {
804
-           extInfo = (VkExternalMemoryImageCreateInfo) {
805
-               .sType = VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO,
806
-               .handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT,
807
-           };
808
-           imageCreateInfo.pNext = &extInfo;
809
-       }
810
+   VK_CHECK_RESULT(allocate_dmabuf(s, info->format, 1, &info->modifier, info->usage, &info->size, vk_buf));
811
 
812
-       VK_CHECK_RESULT(vkCreateImage(s->device,
813
-                   &imageCreateInfo, NULL, &p->buffersi.image));
814
+   const VkMemoryGetFdInfoKHR getFdInfo = {
815
+       .sType = VK_STRUCTURE_TYPE_MEMORY_GET_FD_INFO_KHR,
816
+       .memory = vk_buf->memory,
817
+       .handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT
818
+   };
819
+   int fd = -1;
820
+   VK_CHECK_RESULT(vkGetMemoryFdKHR(s->device, &getFdInfo, &fd));
821
 
822
-       VkMemoryRequirements memoryRequirements;
823
-       vkGetImageMemoryRequirements(s->device,
824
-               p->buffersi.image, &memoryRequirements);
825
+   const struct vulkan_modifier_info *modInfo = vulkan_modifierInfo_find(s, info->format, info->modifier);
826
 
827
-       VkMemoryAllocateInfo allocateInfo = {
828
-           .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
829
-           .allocationSize = memoryRequirements.size,
830
-           .memoryTypeIndex = findMemoryType(s,
831
-                         memoryRequirements.memoryTypeBits,
832
-                         VK_MEMORY_PROPERTY_HOST_COHERENT_BIT |
833
-                         VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT),
834
-       };
835
+   if (info->spa_buf->n_datas != modInfo->props.drmFormatModifierPlaneCount)
836
+       return -1;
837
 
838
-       if (flags & SPA_NODE_BUFFERS_FLAG_ALLOC) {
839
-           VK_CHECK_RESULT(vkAllocateMemory(s->device,
840
-                   &allocateInfo, NULL, &p->buffersi.memory));
841
+   VkMemoryRequirements memoryRequirements = {0};
842
+   vkGetImageMemoryRequirements(s->device,
843
+           vk_buf->image, &memoryRequirements);
844
 
845
-           const VkMemoryGetFdInfoKHR getFdInfo = {
846
-               .sType = VK_STRUCTURE_TYPE_MEMORY_GET_FD_INFO_KHR,
847
-               .memory = p->buffersi.memory,
848
-               .handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT
849
-           };
850
-           int fd;
851
-
852
-               VK_CHECK_RESULT(vkGetMemoryFdKHR(s->device, &getFdInfo, &fd));
853
-
854
-           spa_log_info(s->log, "export DMABUF %zd", memoryRequirements.size);
855
-
856
-//         buffersi->datas0.type = SPA_DATA_DmaBuf;
857
-           buffersi->datas0.type = SPA_DATA_MemFd;
858
-           buffersi->datas0.fd = fd;
859
-           buffersi->datas0.flags = SPA_DATA_FLAG_READABLE;
860
-           buffersi->datas0.mapoffset = 0;
861
-           buffersi->datas0.maxsize = memoryRequirements.size;
862
-           p->buffersi.fd = fd;
863
-       } else {
864
-           VkImportMemoryFdInfoKHR importInfo = {
865
-               .sType = VK_STRUCTURE_TYPE_IMPORT_MEMORY_FD_INFO_KHR,
866
-               .handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT,
867
-               .fd = fcntl(buffersi->datas0.fd, F_DUPFD_CLOEXEC, 0),
868
-           };
869
-           allocateInfo.pNext = &importInfo;
870
-           p->buffersi.fd = -1;
871
-           spa_log_info(s->log, "import DMABUF");
872
+   spa_log_info(s->log, "export DMABUF %zd", memoryRequirements.size);
873
 
874
-           VK_CHECK_RESULT(vkAllocateMemory(s->device,
875
-                   &allocateInfo, NULL, &p->buffersi.memory));
876
-       }
877
-       VK_CHECK_RESULT(vkBindImageMemory(s->device,
878
-                   p->buffersi.image, p->buffersi.memory, 0));
879
-
880
-       VkImageViewCreateInfo viewInfo = {
881
-           .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
882
-           .image = p->buffersi.image,
883
-           .viewType = VK_IMAGE_VIEW_TYPE_2D,
884
-           .format = VK_FORMAT_R32G32B32A32_SFLOAT,
885
-           .components.r = VK_COMPONENT_SWIZZLE_R,
886
-           .components.g = VK_COMPONENT_SWIZZLE_G,
887
-           .components.b = VK_COMPONENT_SWIZZLE_B,
888
-           .components.a = VK_COMPONENT_SWIZZLE_A,
889
-           .subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
890
-           .subresourceRange.levelCount = 1,
891
-           .subresourceRange.layerCount = 1,
892
+   for (uint32_t i = 0; i < info->spa_buf->n_datas; i++) {
893
+       VkImageSubresource subresource = {
894
+           .aspectMask = mem_plane_aspect(i),
895
        };
896
+       VkSubresourceLayout subresLayout = {0};
897
+       vkGetImageSubresourceLayout(s->device, vk_buf->image, &subresource, &subresLayout);
898
+
899
+       info->spa_buf->datasi.type = SPA_DATA_DmaBuf;
900
+       info->spa_buf->datasi.fd = fd;
901
+       info->spa_buf->datasi.flags = SPA_DATA_FLAG_READABLE;
902
+       info->spa_buf->datasi.mapoffset = 0;
903
+       info->spa_buf->datasi.chunk->offset = subresLayout.offset;
904
+       info->spa_buf->datasi.chunk->stride = subresLayout.rowPitch;
905
+       info->spa_buf->datasi.chunk->size = subresLayout.size;
906
+       info->spa_buf->datasi.maxsize = memoryRequirements.size;
907
+   }
908
+   vk_buf->fd = fd;
909
+
910
+   VkImageViewCreateInfo viewInfo = {
911
+       .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
912
+       .image = vk_buf->image,
913
+       .viewType = VK_IMAGE_VIEW_TYPE_2D,
914
+       .format = info->format,
915
+       .components.r = VK_COMPONENT_SWIZZLE_R,
916
+       .components.g = VK_COMPONENT_SWIZZLE_G,
917
+       .components.b = VK_COMPONENT_SWIZZLE_B,
918
+       .components.a = VK_COMPONENT_SWIZZLE_A,
919
+       .subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
920
+       .subresourceRange.levelCount = 1,
921
+       .subresourceRange.layerCount = 1,
922
+   };
923
+
924
+   VK_CHECK_RESULT(vkCreateImageView(s->device,
925
+               &viewInfo, NULL, &vk_buf->view));
926
+   return 0;
927
+}
928
 
929
-       VK_CHECK_RESULT(vkCreateImageView(s->device,
930
-                   &viewInfo, NULL, &p->buffersi.view));
931
+int vulkan_import_dmabuf(struct vulkan_base *s, struct external_buffer_info *info, struct vulkan_buffer *vk_buf)
932
+{
933
+
934
+   if (info->spa_buf->n_datas == 0 || info->spa_buf->n_datas > DMABUF_MAX_PLANES)
935
+       return -1;
936
+
937
+   struct vulkan_modifier_info *modProps = vulkan_modifierInfo_find(s, info->format, info->modifier);
938
+   if (!modProps)
939
+       return -1;
940
+
941
+   uint32_t planeCount = info->spa_buf->n_datas;
942
+
943
+   if (planeCount != modProps->props.drmFormatModifierPlaneCount)
944
+       return -1;
945
+
946
+   if (info->size.width > modProps->max_extent.width || info->size.height > modProps->max_extent.height)
947
+       return -1;
948
+
949
+   VkSubresourceLayout planeLayoutsDMABUF_MAX_PLANES = {0};
950
+   for (uint32_t i = 0; i < planeCount; i++) {
951
+       planeLayoutsi.offset = info->spa_buf->datasi.chunk->offset;
952
+       planeLayoutsi.rowPitch = info->spa_buf->datasi.chunk->stride;
953
+       planeLayoutsi.size = 0;
954
    }
955
-   p->n_buffers = n_buffers;
956
 
957
+   VkImageDrmFormatModifierExplicitCreateInfoEXT modInfo = {
958
+       .sType = VK_STRUCTURE_TYPE_IMAGE_DRM_FORMAT_MODIFIER_EXPLICIT_CREATE_INFO_EXT,
959
+       .drmFormatModifierPlaneCount = planeCount,
960
+       .drmFormatModifier = info->modifier,
961
+       .pPlaneLayouts = planeLayouts,
962
+   };
963
+
964
+   VkExternalMemoryImageCreateInfo extInfo = {
965
+       .sType = VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO,
966
+       .handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT,
967
+       .pNext = &modInfo,
968
+   };
969
+
970
+   VkImageCreateInfo imageCreateInfo = {
971
+       .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
972
+       .imageType = VK_IMAGE_TYPE_2D,
973
+       .format = info->format,
974
+       .extent.width = info->size.width,
975
+       .extent.height = info->size.height,
976
+       .extent.depth = 1,
977
+       .mipLevels = 1,
978
+       .arrayLayers = 1,
979
+       .samples = VK_SAMPLE_COUNT_1_BIT,
980
+       .tiling = VK_IMAGE_TILING_DRM_FORMAT_MODIFIER_EXT,
981
+       .usage = info->usage,
982
+       .sharingMode = VK_SHARING_MODE_EXCLUSIVE,
983
+       .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
984
+       .pNext = &extInfo,
985
+   };
986
+
987
+   VK_CHECK_RESULT(vkCreateImage(s->device,
988
+               &imageCreateInfo, NULL, &vk_buf->image));
989
+
990
+   VkMemoryRequirements memoryRequirements;
991
+   vkGetImageMemoryRequirements(s->device,
992
+           vk_buf->image, &memoryRequirements);
993
+
994
+   vk_buf->fd = fcntl(info->spa_buf->datas0.fd, F_DUPFD_CLOEXEC, 0);
995
+   VkImportMemoryFdInfoKHR importInfo = {
996
+       .sType = VK_STRUCTURE_TYPE_IMPORT_MEMORY_FD_INFO_KHR,
997
+       .handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT,
998
+       .fd = fcntl(info->spa_buf->datas0.fd, F_DUPFD_CLOEXEC, 0),
999
+   };
1000
+
1001
+   VkMemoryAllocateInfo allocateInfo = {
1002
+       .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
1003
+       .allocationSize = memoryRequirements.size,
1004
+       .memoryTypeIndex = vulkan_memoryType_find(s,
1005
+                     memoryRequirements.memoryTypeBits,
1006
+                     VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT),
1007
+   };
1008
+   allocateInfo.pNext = &importInfo;
1009
+
1010
+   spa_log_info(s->log, "import DMABUF");
1011
+
1012
+   VK_CHECK_RESULT(vkAllocateMemory(s->device,
1013
+           &allocateInfo, NULL, &vk_buf->memory));
1014
+   VK_CHECK_RESULT(vkBindImageMemory(s->device,
1015
+               vk_buf->image, vk_buf->memory, 0));
1016
+
1017
+   VkImageViewCreateInfo viewInfo = {
1018
+       .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
1019
+       .image = vk_buf->image,
1020
+       .viewType = VK_IMAGE_VIEW_TYPE_2D,
1021
+       .format = info->format,
1022
+       .components.r = VK_COMPONENT_SWIZZLE_R,
1023
+       .components.g = VK_COMPONENT_SWIZZLE_G,
1024
+       .components.b = VK_COMPONENT_SWIZZLE_B,
1025
+       .components.a = VK_COMPONENT_SWIZZLE_A,
1026
+       .subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
1027
+       .subresourceRange.levelCount = 1,
1028
+       .subresourceRange.layerCount = 1,
1029
+   };
1030
+
1031
+   VK_CHECK_RESULT(vkCreateImageView(s->device,
1032
+               &viewInfo, NULL, &vk_buf->view));
1033
+   return 0;
1034
+}
1035
+
1036
+int vulkan_import_memptr(struct vulkan_base *s, struct external_buffer_info *info, struct vulkan_buffer *vk_buf)
1037
+{
1038
+   VkImageCreateInfo imageCreateInfo = {
1039
+       .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
1040
+       .imageType = VK_IMAGE_TYPE_2D,
1041
+       .format = info->format,
1042
+       .extent.width = info->size.width,
1043
+       .extent.height = info->size.height,
1044
+       .extent.depth = 1,
1045
+       .mipLevels = 1,
1046
+       .arrayLayers = 1,
1047
+       .samples = VK_SAMPLE_COUNT_1_BIT,
1048
+       .tiling = VK_IMAGE_TILING_LINEAR,
1049
+       .usage = info->usage,
1050
+       .sharingMode = VK_SHARING_MODE_EXCLUSIVE,
1051
+       .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
1052
+   };
1053
+
1054
+   VK_CHECK_RESULT(vkCreateImage(s->device,
1055
+               &imageCreateInfo, NULL, &vk_buf->image));
1056
+
1057
+   VkMemoryRequirements memoryRequirements;
1058
+   vkGetImageMemoryRequirements(s->device,
1059
+           vk_buf->image, &memoryRequirements);
1060
+
1061
+   VkMemoryAllocateInfo allocateInfo = {
1062
+       .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
1063
+       .allocationSize = memoryRequirements.size,
1064
+       .memoryTypeIndex = vulkan_memoryType_find(s,
1065
+                     memoryRequirements.memoryTypeBits,
1066
+                     VK_MEMORY_PROPERTY_HOST_COHERENT_BIT |
1067
+                     VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT),
1068
+   };
1069
+
1070
+   vk_buf->fd = -1;
1071
+   spa_log_info(s->log, "import MemPtr");
1072
+
1073
+   VK_CHECK_RESULT(vkAllocateMemory(s->device,
1074
+           &allocateInfo, NULL, &vk_buf->memory));
1075
+   VK_CHECK_RESULT(vkBindImageMemory(s->device,
1076
+               vk_buf->image, vk_buf->memory, 0));
1077
+
1078
+   VkImageViewCreateInfo viewInfo = {
1079
+       .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
1080
+       .image = vk_buf->image,
1081
+       .viewType = VK_IMAGE_VIEW_TYPE_2D,
1082
+       .format = info->format,
1083
+       .components.r = VK_COMPONENT_SWIZZLE_R,
1084
+       .components.g = VK_COMPONENT_SWIZZLE_G,
1085
+       .components.b = VK_COMPONENT_SWIZZLE_B,
1086
+       .components.a = VK_COMPONENT_SWIZZLE_A,
1087
+       .subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
1088
+       .subresourceRange.levelCount = 1,
1089
+       .subresourceRange.layerCount = 1,
1090
+   };
1091
+
1092
+   VK_CHECK_RESULT(vkCreateImageView(s->device,
1093
+               &viewInfo, NULL, &vk_buf->view));
1094
    return 0;
1095
 }
1096
 
1097
-int spa_vulkan_init_stream(struct vulkan_state *s, struct vulkan_stream *stream,
1098
-       enum spa_direction direction, struct spa_dict *props)
1099
+int vulkan_stream_init(struct vulkan_stream *stream, enum spa_direction direction,
1100
+       struct spa_dict *props)
1101
 {
1102
    spa_zero(*stream);
1103
    stream->direction = direction;
1104
@@ -672,87 +828,62 @@
1105
    return 0;
1106
 }
1107
 
1108
-int spa_vulkan_prepare(struct vulkan_state *s)
1109
+uint32_t vulkan_vkformat_to_id(VkFormat format)
1110
 {
1111
-   if (!s->prepared) {
1112
-       CHECK(createInstance(s));
1113
-       CHECK(findPhysicalDevice(s));
1114
-       CHECK(createDevice(s));
1115
-       CHECK(createDescriptors(s));
1116
-       CHECK(createComputePipeline(s, s->shaderName));
1117
-       CHECK(createCommandBuffer(s));
1118
-       s->prepared = true;
1119
+   SPA_FOR_EACH_ELEMENT_VAR(vk_video_format_convs, f) {
1120
+       if (f->format == format)
1121
+           return f->id;
1122
    }
1123
-   return 0;
1124
+   return SPA_VIDEO_FORMAT_UNKNOWN;
1125
 }
1126
 
1127
-int spa_vulkan_unprepare(struct vulkan_state *s)
1128
+VkFormat vulkan_id_to_vkformat(uint32_t id)
1129
 {
1130
-   if (s->prepared) {
1131
-       vkDestroyShaderModule(s->device, s->computeShaderModule, NULL);
1132
-       vkDestroySampler(s->device, s->sampler, NULL);
1133
-       vkDestroyDescriptorPool(s->device, s->descriptorPool, NULL);
1134
-       vkDestroyDescriptorSetLayout(s->device, s->descriptorSetLayout, NULL);
1135
-       vkDestroyPipelineLayout(s->device, s->pipelineLayout, NULL);
1136
-       vkDestroyPipeline(s->device, s->pipeline, NULL);
1137
-       vkDestroyCommandPool(s->device, s->commandPool, NULL);
1138
-       vkDestroyFence(s->device, s->fence, NULL);
1139
-       vkDestroyDevice(s->device, NULL);
1140
-       vkDestroyInstance(s->instance, NULL);
1141
-       s->prepared = false;
1142
+   SPA_FOR_EACH_ELEMENT_VAR(vk_video_format_convs, f) {
1143
+       if (f->id == id)
1144
+           return f->format;
1145
    }
1146
-   return 0;
1147
+   return VK_FORMAT_UNDEFINED;
1148
 }
1149
 
1150
-int spa_vulkan_start(struct vulkan_state *s)
1151
+int vulkan_vkresult_to_errno(VkResult result)
1152
 {
1153
-   uint32_t i;
1154
+   return vkresult_to_errno(result);
1155
+}
1156
+
1157
+int vulkan_wait_fence(struct vulkan_base *s, VkFence fence)
1158
+{
1159
+        VK_CHECK_RESULT(vkWaitForFences(s->device, 1, &fence, VK_TRUE, UINT64_MAX));
1160
 
1161
-   for (i = 0; i < s->n_streams; i++) {
1162
-       struct vulkan_stream *p = &s->streamsi;
1163
-       p->current_buffer_id = SPA_ID_INVALID;
1164
-       p->busy_buffer_id = SPA_ID_INVALID;
1165
-       p->ready_buffer_id = SPA_ID_INVALID;
1166
-   }
1167
    return 0;
1168
 }
1169
 
1170
-int spa_vulkan_stop(struct vulkan_state *s)
1171
+int vulkan_wait_idle(struct vulkan_base *s)
1172
 {
1173
         VK_CHECK_RESULT(vkDeviceWaitIdle(s->device));
1174
-   clear_streams(s);
1175
-   s->started = false;
1176
+
1177
    return 0;
1178
 }
1179
 
1180
-int spa_vulkan_ready(struct vulkan_state *s)
1181
+int vulkan_base_init(struct vulkan_base *s, struct vulkan_base_info *info)
1182
 {
1183
-   uint32_t i;
1184
-   VkResult result;
1185
-
1186
-   if (!s->started)
1187
-       return 0;
1188
-
1189
-   result = vkGetFenceStatus(s->device, s->fence);
1190
-   if (result == VK_NOT_READY)
1191
-       return -EBUSY;
1192
-   VK_CHECK_RESULT(result);
1193
-
1194
-   s->started = false;
1195
-
1196
-   for (i = 0; i < s->n_streams; i++) {
1197
-       struct vulkan_stream *p = &s->streamsi;
1198
-       p->ready_buffer_id = p->busy_buffer_id;
1199
-       p->busy_buffer_id = SPA_ID_INVALID;
1200
+   if (!s->initialized) {
1201
+       CHECK(createInstance(s));
1202
+       CHECK(findPhysicalDevice(s));
1203
+       CHECK(createDevice(s, info));
1204
+       CHECK(queryFormatInfo(s, info));
1205
+       s->implicit_sync_interop = dmabuf_check_sync_file_import_export(s->log);
1206
+       s->initialized = true;
1207
    }
1208
    return 0;
1209
 }
1210
 
1211
-int spa_vulkan_process(struct vulkan_state *s)
1212
+void vulkan_base_deinit(struct vulkan_base *s)
1213
 {
1214
-   CHECK(updateDescriptors(s));
1215
-   CHECK(runCommandBuffer(s));
1216
-        VK_CHECK_RESULT(vkDeviceWaitIdle(s->device));
1217
-
1218
-   return 0;
1219
+   if (s->initialized) {
1220
+       destroyFormatInfo(s);
1221
+       vkDestroyDevice(s->device, NULL);
1222
+       vkDestroyInstance(s->instance, NULL);
1223
+       s->initialized = false;
1224
+   }
1225
 }
1226
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
464
 
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
 
202
-   nread = read(impl->fd, SPA_PTROFF(d->data, d->chunk->size, void), req);
203
-   if (nread < 0) {
204
-       const bool important = !(errno == EINTR
205
-                    || errno == EAGAIN
206
-                    || errno == EWOULDBLOCK);
207
+   pw_log_debug("avail %d %u %u", avail, index, size);
208
 
209
-       if (important)
210
-           pw_log_warn("failed to read from pipe (%s): %s",
211
-                   impl->filename, strerror(errno));
212
+   if (avail < (int32_t)size) {
213
+       memset(bd->data, 0, size);
214
+       if (avail > 0)
215
+           pw_log_warn("underrun %d < %u", avail, size);
216
+       impl->have_sync = false;
217
    }
218
-   else {
219
-       d->chunk->size += nread;
220
+   if (avail > (int32_t)RINGBUFFER_SIZE) {
221
+       index += avail - impl->target_buffer;
222
+       avail = impl->target_buffer;
223
+       pw_log_warn("overrun %d > %u", avail, RINGBUFFER_SIZE);
224
    }
225
-
226
-   impl->leftover_count = d->chunk->size % impl->frame_size;
227
-   d->chunk->size -= impl->leftover_count;
228
-   memcpy(impl->leftover, SPA_PTROFF(d->data, d->chunk->size, void), impl->leftover_count);
229
+   if (avail > 0) {
230
+       avail = SPA_ROUND_DOWN(avail, impl->frame_size);
231
+       update_rate(impl, avail);
232
+
233
+       avail = SPA_MIN(size, (uint32_t)avail);
234
+       spa_ringbuffer_read_data(&impl->ring,
235
+               impl->buffer, RINGBUFFER_SIZE,
236
+               index & RINGBUFFER_MASK,
237
+               bd->data, avail);
238
+
239
+       index += avail;
240
+       spa_ringbuffer_read_update(&impl->ring, index);
241
+   }
242
+   bd->chunk->offset = 0;
243
+   bd->chunk->size = size;
244
+   bd->chunk->stride = impl->frame_size;
245
 
246
    pw_stream_queue_buffer(impl->stream, buf);
247
 }
248
 
249
+static void stream_io_changed(void *data, uint32_t id, void *area, uint32_t size)
250
+{
251
+   struct impl *impl = data;
252
+   switch (id) {
253
+   case SPA_IO_RateMatch:
254
+       impl->rate_match = area;
255
+       break;
256
+   case SPA_IO_Position:
257
+       impl->position = area;
258
+       break;
259
+   }
260
+}
261
+
262
 static const struct pw_stream_events playback_stream_events = {
263
    PW_VERSION_STREAM_EVENTS,
264
    .destroy = stream_destroy,
265
@@ -290,8 +421,9 @@
266
 static const struct pw_stream_events capture_stream_events = {
267
    PW_VERSION_STREAM_EVENTS,
268
    .destroy = stream_destroy,
269
+   .io_changed = stream_io_changed,
270
    .state_changed = stream_state_changed,
271
-   .process = capture_stream_process
272
+   .process = capture_stream_process,
273
 };
274
 
275
 static int create_stream(struct impl *impl)
276
@@ -335,6 +467,92 @@
277
    return 0;
278
 }
279
 
280
+static inline void
281
+set_iovec(struct spa_ringbuffer *rbuf, void *buffer, uint32_t size,
282
+       uint32_t offset, struct iovec *iov, uint32_t len)
283
+{
284
+   iov0.iov_len = SPA_MIN(len, size - offset);
285
+   iov0.iov_base = SPA_PTROFF(buffer, offset, void);
286
+   iov1.iov_len = len - iov0.iov_len;
287
+   iov1.iov_base = buffer;
288
+}
289
+
290
+static int handle_pipe_read(struct impl *impl)
291
+{
292
+   ssize_t nread;
293
+   int32_t filled;
294
+   uint32_t index;
295
+   struct iovec iov2;
296
+
297
+   filled = spa_ringbuffer_get_write_index(&impl->ring, &index);
298
+   if (!impl->have_sync) {
299
+       memset(impl->buffer, 0, RINGBUFFER_SIZE);
300
+   }
301
+
302
+   if (filled < 0) {
303
+       pw_log_warn("%p: underrun write:%u filled:%d",
304
+               impl, index, filled);
305
+   }
306
+
307
+   set_iovec(&impl->ring,
308
+           impl->buffer, RINGBUFFER_SIZE,
309
+           index & RINGBUFFER_MASK,
310
+           iov, RINGBUFFER_SIZE);
311
+
312
+   nread = read(impl->fd, iov0.iov_base, iov0.iov_len);
313
+   if (nread > 0) {
314
+       index += nread;
315
+       filled += nread;
316
+       if (nread == (ssize_t)iov0.iov_len) {
317
+           nread = read(impl->fd, iov1.iov_base, iov1.iov_len);
318
+           if (nread > 0) {
319
+               index += nread;
320
+               filled += nread;
321
+           }
322
+       }
323
+   }
324
+   if (!impl->have_sync) {
325
+       impl->ring.readindex = index - impl->target_buffer;
326
+
327
+       spa_dll_init(&impl->dll);
328
+       spa_dll_set_bw(&impl->dll, SPA_DLL_BW_MIN, 256.f, impl->info.rate);
329
+       impl->corr = 1.0f;
330
+
331
+       pw_log_info("resync");
332
+       impl->have_sync = true;
333
+   }
334
+   spa_ringbuffer_write_update(&impl->ring, index);
335
+
336
+   if (nread < 0) {
337
+       const bool important = !(errno == EINTR
338
+                    || errno == EAGAIN
339
+                    || errno == EWOULDBLOCK);
340
+
341
+       if (important)
342
+           pw_log_warn("failed to read from pipe (%s): %m",
343
+                   impl->filename);
344
+       else
345
+           pw_log_debug("pipe (%s) underrun: %m", impl->filename);
346
+   }
347
+   pw_log_debug("filled %d %u %d", filled, index, impl->target_buffer);
348
+
349
+   return 0;
350
+}
351
+
352
+
353
+static void on_pipe_io(void *data, int fd, uint32_t mask)
354
+{
355
+   struct impl *impl = data;
356
+
357
+   if (mask & (SPA_IO_ERR | SPA_IO_HUP)) {
358
+       pw_log_warn("error:%08x", mask);
359
+       pw_loop_update_io(impl->data_loop, impl->socket, 0);
360
+       return;
361
+   }
362
+   if (mask & SPA_IO_IN)
363
+       handle_pipe_read(impl);
364
+}
365
+
366
 static int create_fifo(struct impl *impl)
367
 {
368
    struct stat st;
369
@@ -363,7 +581,6 @@
370
 
371
        do_unlink_fifo = true;
372
    }
373
-
374
    if ((fd = open(filename, O_RDWR | O_CLOEXEC | O_NONBLOCK, 0)) < 0) {
375
        res = -errno;
376
        pw_log_error("open('%s'): %s", filename, spa_strerror(res));
377
@@ -381,6 +598,20 @@
378
        pw_log_error("'%s' is not a FIFO.", filename);
379
        goto error;
380
    }
381
+   impl->socket = pw_loop_add_io(impl->data_loop, fd,
382
+           0, false, on_pipe_io, impl);
383
+   if (impl->socket == NULL) {
384
+       res = -errno;
385
+       pw_log_error("can't create socket");
386
+       goto error;
387
+   }
388
+   impl->timer = pw_loop_add_timer(impl->data_loop, on_timeout, impl);
389
+   if (impl->timer == NULL) {
390
+       res = -errno;
391
+       pw_log_error("can't create timer");
392
+       goto error;
393
+   }
394
+
395
    pw_log_info("%s fifo '%s' with format:%s channels:%d rate:%d",
396
            impl->direction == PW_DIRECTION_OUTPUT ? "reading from" : "writing to",
397
            filename,
398
@@ -390,6 +621,7 @@
399
    impl->filename = strdup(filename);
400
    impl->unlink_fifo = do_unlink_fifo;
401
    impl->fd = fd;
402
+
403
    return 0;
404
 
405
 error:
406
@@ -440,13 +672,17 @@
407
            unlink(impl->filename);
408
        free(impl->filename);
409
    }
410
+   if (impl->socket)
411
+       pw_loop_destroy_source(impl->data_loop, impl->socket);
412
+   if (impl->timer)
413
+       pw_loop_destroy_source(impl->data_loop, impl->timer);
414
    if (impl->fd >= 0)
415
        close(impl->fd);
416
 
417
    pw_properties_free(impl->stream_props);
418
    pw_properties_free(impl->props);
419
 
420
-   free(impl->leftover);
421
+   free(impl->buffer);
422
    free(impl);
423
 }
424
 
425
@@ -569,6 +805,7 @@
426
    struct pw_properties *props = NULL;
427
    struct impl *impl;
428
    const char *str, *media_class = NULL;
429
+   struct pw_data_loop *data_loop;
430
    int res;
431
 
432
    PW_LOG_TOPIC_INIT(mod_topic);
433
@@ -601,6 +838,8 @@
434
 
435
    impl->module = module;
436
    impl->context = context;
437
+   data_loop = pw_context_get_data_loop(context);
438
+   impl->data_loop = pw_data_loop_get_loop(data_loop);
439
 
440
    if ((str = pw_properties_get(props, "tunnel.mode")) == NULL)
441
        str = "playback";
442
@@ -662,12 +901,18 @@
443
 
444
    copy_props(impl, props, PW_KEY_NODE_RATE);
445
 
446
-   impl->leftover = calloc(1, impl->frame_size);
447
-   if (impl->leftover == NULL) {
448
+   impl->buffer = calloc(1, RINGBUFFER_SIZE);
449
+   if (impl->buffer == NULL) {
450
        res = -errno;
451
-       pw_log_error("can't alloc leftover buffer: %m");
452
+       pw_log_error("can't alloc ringbuffer: %m");
453
        goto error;
454
    }
455
+   spa_ringbuffer_init(&impl->ring);
456
+   impl->target_buffer = 8192 * impl->frame_size;
457
+   spa_dll_init(&impl->dll);
458
+   spa_dll_set_bw(&impl->dll, SPA_DLL_BW_MIN, 256.f, impl->info.rate);
459
+   impl->max_error = 256.0f * impl->frame_size;
460
+   impl->corr = 1.0f;
461
 
462
    impl->core = pw_context_get_object(impl->context, PW_TYPE_INTERFACE_Core);
463
    if (impl->core == NULL) {
464
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
264
 
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
+   if (port->destroying)
202
+       return 0;
203
+
204
+   direction = SPA_DIRECTION_REVERSE(port->direction);
205
+
206
+   spa_pod_dynamic_builder_init(&b, buffer, sizeof(buffer), 4096);
207
+   spa_tag_build_start(&b.b, &f, SPA_PARAM_Tag, direction);
208
+
209
+   if (port->direction == PW_DIRECTION_OUTPUT) {
210
+       spa_list_for_each(l, &port->links, output_link) {
211
+           other = l->input;
212
+           tag = other->tagother->direction;
213
+           if (tag) {
214
+               void *state = NULL;
215
+               while (spa_tag_parse(tag, &info, &state) == 1) {
216
+                   spa_tag_build_add_info(&b.b, info.info);
217
+                   count++;
218
+               }
219
+           }
220
+       }
221
+   } else {
222
+       spa_list_for_each(l, &port->links, input_link) {
223
+           other = l->output;
224
+           tag = other->tagother->direction;
225
+           if (tag) {
226
+               void *state = NULL;
227
+               while (spa_tag_parse(tag, &info, &state) == 1) {
228
+                   spa_tag_build_add_info(&b.b, info.info);
229
+                   count++;
230
+               }
231
+           }
232
+       }
233
+   }
234
+   param = count == 0 ? NULL : spa_tag_build_end(&b.b, &f);
235
+
236
+   old = port->tagdirection;
237
+
238
+   changed = spa_tag_compare(old, param);
239
+
240
+   pw_log_info("port %d: %p %s %s tag %p",
241
+           port->info.id, port, changed ? "set" : "keep",
242
+           pw_direction_as_string(direction), param);
243
+
244
+   if (changed) {
245
+       free(old);
246
+       port->tagdirection = param ? spa_pod_copy(param) : NULL;
247
+       if (param)
248
+           pw_log_pod(SPA_LOG_LEVEL_INFO, param);
249
+   }
250
+   spa_pod_dynamic_builder_clean(&b);
251
+
252
+   if (!changed)
253
+       return 0;
254
+
255
+   if (!port->have_tag_param)
256
+       return 0;
257
+
258
+   return pw_impl_port_set_param(port, SPA_PARAM_Tag, 0, port->tagdirection);
259
+}
260
+
261
 SPA_EXPORT
262
 int pw_impl_port_is_linked(struct pw_impl_port *port)
263
 {
264
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
328
 
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
    }
202
@@ -435,7 +454,7 @@
203
            PW_PERMISSION_ARGS(data->permissions));
204
    printf("\ttype: %s (version %d)\n", data->type, data->version);
205
 
206
-   if (print_all) {
207
+   if (!hide_props) {
208
        print_properties(info->props, MARK_CHANGE(PW_CLIENT_CHANGE_MASK_PROPS));
209
    }
210
 }
211
@@ -448,14 +467,15 @@
212
 static void link_event_info(void *_data, const struct pw_link_info *info)
213
 {
214
         struct proxy_data *data = _data;
215
-   bool print_all, print_mark;
216
+   bool hide_props, print_mark;
217
 
218
-   print_all = true;
219
-        if (data->info == NULL) {
220
+   hide_props = data->data->hide_props;
221
+
222
+   if (data->info == NULL) {
223
        printf("added:\n");
224
        print_mark = false;
225
    }
226
-        else {
227
+   else {
228
        printf("changed:\n");
229
        print_mark = true;
230
    }
231
@@ -471,7 +491,7 @@
232
    printf("\toutput-port-id: %u\n", info->output_port_id);
233
    printf("\tinput-node-id: %u\n", info->input_node_id);
234
    printf("\tinput-port-id: %u\n", info->input_port_id);
235
-   if (print_all) {
236
+   if (!hide_props) {
237
        with_prefix(MARK_CHANGE(PW_LINK_CHANGE_MASK_STATE)) {
238
            printf("\tstate: \"%s\"",
239
                pw_link_state_as_string(info->state));
240
@@ -499,15 +519,17 @@
241
 static void print_device(struct proxy_data *data)
242
 {
243
    struct pw_device_info *info = data->info;
244
-   bool print_all, print_mark;
245
+   bool hide_params, hide_props, print_mark;
246
+
247
+   hide_params = data->data->hide_params;
248
+   hide_props = data->data->hide_props;
249
 
250
-   print_all = true;
251
-        if (data->first) {
252
+   if (data->first) {
253
        printf("added:\n");
254
        print_mark = false;
255
        data->first = false;
256
    }
257
-        else {
258
+   else {
259
        printf("changed:\n");
260
        print_mark = true;
261
    }
262
@@ -517,8 +539,11 @@
263
            PW_PERMISSION_ARGS(data->permissions));
264
    printf("\ttype: %s (version %d)\n", data->type, data->version);
265
 
266
-   if (print_all) {
267
-       print_params(data, MARK_CHANGE(PW_DEVICE_CHANGE_MASK_PARAMS));
268
+   if (!hide_params) {
269
+       print_parameters(data, MARK_CHANGE(PW_DEVICE_CHANGE_MASK_PARAMS));
270
+   }
271
+
272
+   if (!hide_props) {
273
        print_properties(info->props, MARK_CHANGE(PW_DEVICE_CHANGE_MASK_PROPS));
274
    }
275
 }
276
@@ -732,7 +757,9 @@
277
        "      --version                         Show version\n"
278
        "  -r, --remote                          Remote daemon name\n"
279
        "  -N, --no-colors                       disable color output\n"
280
-       "  -C, --color=WHEN                    whether to enable color support. WHEN is `never`, `always`, or `auto`\n",
281
+       "  -C, --color=WHEN                    whether to enable color support. WHEN is `never`, `always`, or `auto`\n"
282
+       "  -o, --hide-props                      hide node properties\n"
283
+       "  -a, --hide-params                     hide node properties\n",
284
        name);
285
 }
286
 
287
@@ -742,11 +769,13 @@
288
    struct pw_loop *l;
289
    const char *opt_remote = NULL;
290
    static const struct option long_options = {
291
-       { "help",   no_argument,        NULL, 'h' },
292
-       { "version",    no_argument,        NULL, 'V' },
293
-       { "remote", required_argument,  NULL, 'r' },
294
-       { "no-colors",  no_argument,        NULL, 'N' },
295
-       { "color",  optional_argument,  NULL, 'C' },
296
+       { "help",       no_argument,        NULL, 'h' },
297
+       { "version",        no_argument,        NULL, 'V' },
298
+       { "remote",     required_argument,  NULL, 'r' },
299
+       { "no-colors",      no_argument,        NULL, 'N' },
300
+       { "color",      optional_argument,  NULL, 'C' },
301
+       { "hide-props",     no_argument,        NULL, 'o' },
302
+       { "hide-params",    no_argument,        NULL, 'a' },
303
        { NULL, 0, NULL, 0}
304
    };
305
    int c;
306
@@ -760,7 +789,7 @@
307
    if (isatty(STDOUT_FILENO) && getenv("NO_COLOR") == NULL)
308
        colors = true;
309
 
310
-   while ((c = getopt_long(argc, argv, "hVr:NC", long_options, NULL)) != -1) {
311
+   while ((c = getopt_long(argc, argv, "hVr:NCoa", long_options, NULL)) != -1) {
312
        switch (c) {
313
        case 'h':
314
            show_help(argv0, false);
315
@@ -793,6 +822,12 @@
316
                return -1;
317
            }
318
            break;
319
+       case 'o':
320
+           data.hide_props = true;
321
+           break;
322
+       case 'a':
323
+           data.hide_params = true;
324
+           break;
325
        default:
326
            show_help(argv0, true);
327
            return -1;
328
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