Changes of Revision 28

pipewire-aptx.changes Changed
x
 
1
@@ -1,4 +1,9 @@
2
 -------------------------------------------------------------------
3
+Sat May 20 12:08:17 UTC 2023 - Bjørn Lie <zaitor@opensuse.org>
4
+
5
+- Update to version 0.3.71
6
+
7
+-------------------------------------------------------------------
8
 Sun May 14 10:53:41 UTC 2023 - Bjørn Lie <zaitor@opensuse.org>
9
 
10
 - Update to version 0.3.70
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.70
6
+Version:        0.3.71
7
 Release:        0
8
 Summary:        PipeWire Bluetooth aptX codec plugin
9
 License:        MIT
10
pipewire-0.3.70.tar.gz/NEWS -> pipewire-0.3.71.tar.gz/NEWS Changed
160
 
1
@@ -1,3 +1,148 @@
2
+# PipeWire 0.3.71 (2023-05-17)
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 zero-latency jackdbus bridge was added. This works similar to what
9
+    PulseAudio has to offer and creates a sink/source when jackdbus is
10
+    started. It is however much more efficient and runs the complete PipeWire
11
+    graph as a synchronous JACK client with no added latency.
12
+  - Many performance improvements. Activation of remote nodes is more
13
+    efficient, fewer eventfds are required on the clients, less callback
14
+    overhead in performence critical paths and an optimized poll function
15
+    was added. This was mainly driven by the jackdbus module to get the lowest
16
+    possible overhead when running the graph.
17
+  - The JACK notify callback implementation was reworked to emulate better what
18
+    JACK does, improving compatibility with ardour7 and the JACK stress test.
19
+  - More work on BAP devices. Device latency is now passed on to
20
+    applications also for multi-device headsets, and channel allocation
21
+    is handled better.
22
+  - Many more improvements and bugfixes.
23
+
24
+
25
+## PipeWire
26
+  - Remove the hardcoded limit on io_areas. This is used to link nodes together
27
+    and exchange buffers, it was limited to 2048 but now dynamically scales
28
+    based on requirements.
29
+  - Rate and quantum changes are now applied correctly in more cases. (#3159)
30
+  - Updates to client-node to more efficiently process the driver.
31
+  - The profiler information was improved to be more accurate. It should
32
+    now work better for remote drivers.
33
+  - Some potential memory map errors were fixed in the protocol because in some
34
+    case with large messages, some fds were closed too soon.
35
+  - pw-filter now implements the pw_filter_set_active() method.
36
+  - A potential out-of-buffers case was fixed in capture pw-streams where buffers
37
+    were not moved to the recycle queue when the node suspended.
38
+  - Nodes are now always woken up with the eventfd. Previously there were
39
+    some optimiztions in the server to directly call into the node process
40
+    function but that optimization is not necessary. Without this optimization
41
+    it is now possible to run nodes in different threads.
42
+  - pw-stream trigger is now implemented correctly in all cases.
43
+  - Remote nodes now use one eventfd less because they get triggered with the
44
+    node eventfd directly.
45
+  - Monitor ports are now ignored in latency updates.
46
+  - A potential race when reporting an error to a client was fixed. (#3192)
47
+  - Fix a bug where always_process nodes would sometimes IDLE. (#3189)
48
+  - Optimize peer activation. Nodes are now activated more efficiently and
49
+    independent of the number of links. It also reduces the number of eventfds
50
+    and memory in remote clients.
51
+  - A bug in property serialization was fixed. Values with spaces would only
52
+    serialize the first part of the value.
53
+
54
+## Modules
55
+  - Correctly handle the echo-canceler plugin init method fallback. The
56
+    samplerate was not correctly configured. This is only a regression for people
57
+    that have external echo-canceler plugins.
58
+  - RAOP sink now only sets the volume on the remote end when the stream is
59
+    recording. (#3175)
60
+  - RAOP discover now tries to deduplicate entries from the same host.
61
+  - A new zero-latency jackdbus bridge was added. This works similar to what
62
+    pulseaudio has to offer and creates a sink/source when jackdbus is
63
+    started. It is however much more efficient and runs the complete PipeWire
64
+    graph as a synchronous JACK client.
65
+  - The access module uses a more secure way to check the application
66
+    executable.
67
+  - module-combine-stream now has configurable delay and latency for each
68
+    stream. This can be used to align sinks/sources with different latencies.
69
+  - A potential crash in module-pulse-tunnel was fixed when shutting down.
70
+    (#3199)
71
+  - Module-rt will now clamp the nice value to the min allowed value to avoid
72
+    errors from rtkit. (#3186)
73
+  - Fix a bug with the session counters in module-rtp-sap. Also use the right
74
+    format for L24. Improve the AES67 example config.
75
+  - Improve some warning and info messages in module-rt. (#3194)
76
+  - module-rtp-session should now do something when started without arguments.
77
+  - A potential crash in module-rtp-session was fixed. (#3217)
78
+  - module-filter-chain has better error reporting when a convolver fails to
79
+    load. (#3223)
80
+
81
+## SPA
82
+  - Move some things around to avoid compiler warnings. (#3171)
83
+  - Increase mixer ports. Reorganize some things and bump mixer input ports
84
+    from 128 to 512.
85
+  - Fix a potential crash when a node is scheduled before it completes
86
+    the setup.
87
+  - The JACK sink and source SPA plugins have seen some improvements.
88
+  - Allow the peaks resampler still if we disabled resampling.
89
+  - Perform more cleanup in audioadapter when in error.
90
+  - An optimized non-cancellable loop implementation was added.
91
+  - Callbacks were optimized with a _fast() varsion that doesn't check the
92
+    version and method. When this check is performed earlier, it can
93
+    be skipped in performance critical places.
94
+  - Some of the callbacks and system methods are now using the fast function
95
+    calls in critical paths.
96
+  - A potential division by zero was fixed in the ALSA plugins.
97
+  - Improve rate and quantum when starting audioconvert.
98
+  - Make it possible to override node.driver in the SPA null-audio-driver.
99
+    (#3220)
100
+
101
+## pulse-server
102
+  - The audio info parameter parsing was refactored and improved.
103
+  - Fix some races with clients exiting when playing samples.
104
+  - An option was added to change or disable the dbus name registration.
105
+    (#2987)
106
+
107
+## Bluetooth
108
+  - Implement battery reporting using AT+XEVENT.
109
+  - Disable hardware volume for 3M WorkTunes.
110
+  - Implement BAP audio locations (channel positions) by using the new
111
+    bluez properties.
112
+
113
+## JACK
114
+  - Fix some errors reported by JACK test.cpp. (#2638)
115
+  - Add jack.show-midi option to show/hide midi ports.
116
+  - Add jack.max-client-ports option. JACK also has a port limit and so
117
+    PipeWire needs it as well to make the tests happy.
118
+  - Call the shutdown callback only when the server stopped, not when there
119
+    is a random error. (#3070)
120
+  - Avoid registering the same port name twice.
121
+  - Call port registration callbacks in activate/deactivate.
122
+  - Improve jack_port_connected().
123
+  - Improve some error reporting.
124
+  - The JACK headers were updated to a newer version.
125
+  - JACK callbacks are now managed with an event queue to simulate
126
+    more what JACK does. This avoids emiting callbacks when a method is blocking
127
+    for a reply and causing deadlocks. (#3183)
128
+  - Assign unique names to JACK clients. (#2833)
129
+  - Fix a potential crash when the thread_utils was used after free.
130
+  - Aliases are now not filled in by default to improve JACK compatibility.
131
+    (#3154)
132
+
133
+# ALSA
134
+  - The ALSA plugin will now wait for negotiation to complete or an error
135
+    before _prepare() completes. This makes more applications deal correctly
136
+    with the potential errors.
137
+
138
+# Docs
139
+  - A new document about how scheduling is implemented was added.
140
+  - Update the pw-cli man page. (#2988)
141
+  - Document the SPA Pod serialization.
142
+  - Document the PipeWire native protocol.
143
+
144
+
145
+Older versions:
146
+
147
 # PipeWire 0.3.70 (2023-04-20)
148
 
149
 This is a quick bugfix release that is API and ABI compatible with previous
150
@@ -62,9 +207,6 @@
151
   - The GStreamer source now uses the BaseSrc clocking code to implement
152
     the clock and timing code.
153
 
154
-
155
-Older versions:
156
-
157
 # PipeWire 0.3.69 (2023-04-13)
158
 
159
 This is a quick bugfix release that is API and ABI compatible with previous
160
pipewire-0.3.70.tar.gz/doc/meson.build -> pipewire-0.3.71.tar.gz/doc/meson.build Changed
10
 
1
@@ -30,6 +30,8 @@
2
   'pipewire-session-manager.dox',
3
   'pipewire-objects-design.dox',
4
   'pipewire-audio.dox',
5
+  'pipewire-scheduling.dox',
6
+  'pipewire-protocol.dox',
7
   'tutorial.dox',
8
   'tutorial1.dox',
9
   'tutorial2.dox',
10
pipewire-0.3.70.tar.gz/doc/pipewire-modules.dox -> pipewire-0.3.71.tar.gz/doc/pipewire-modules.dox Changed
10
 
1
@@ -60,6 +60,8 @@
2
 - \subpage page_module_example_source
3
 - \subpage page_module_fallback_sink
4
 - \subpage page_module_filter_chain
5
+- \subpage page_module_jackdbus_detect
6
+- \subpage page_module_jack_tunnel
7
 - \subpage page_module_link_factory
8
 - \subpage page_module_loopback
9
 - \subpage page_module_metadata
10
pipewire-0.3.71.tar.gz/doc/pipewire-protocol.dox Added
201
 
1
@@ -0,0 +1,1549 @@
2
+/** \page page_native_protocol Native Protocol
3
+
4
+PipeWire has a pluggable client/server IPC protocol.
5
+
6
+The reference implementation uses unix sockets and is implemented in
7
+\ref page_module_protocol_native.
8
+
9
+We document the messages here.
10
+
11
+# Message header
12
+
13
+Each message on the unix socket contains a 16 bytes header and a
14
+variable length payload size:
15
+
16
+```
17
+    0                   1                   2                   3
18
+    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
19
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
20
+   |                     Id                                        |
21
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
22
+   |    opcode     |                   size                        |
23
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
24
+   |                         seq                                   |
25
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
26
+   |                         n_fds                                 |
27
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
28
+   |                     payload POD                               |
29
+   .                                                               .
30
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
31
+   |                     optional footer POD                       |
32
+   .                                                               .
33
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
34
+```
35
+
36
+
37
+These are four uint32 words to be read in native endianness.
38
+
39
+- Id: the message id this is the destination resource/proxy id
40
+- opcode: the opcode on the resource/proxy interface
41
+- size: the size of the payload and optional footer of the message
42
+- seq: an increasing sequence number for each message
43
+- n_fds: number of file descriptors in this message.
44
+
45
+The sender should send along with each message the fds that belong to
46
+the message. If there are more than the maximum number of fds in the
47
+message than can fit in one message, the message is split into multiple
48
+parts.
49
+
50
+The payload is a single POD see \ref page_spa_pod for details.
51
+
52
+After the payload, there is an optional footer POD object.
53
+
54
+# Making a connection
55
+
56
+First a connection is made to a unix domain socket. By default, the socket is
57
+named as "pipewire-0" and searched in the following directories:
58
+
59
+ - getenv("PIPEWIRE_RUNTIME_DIR")
60
+ - getenv("XDG_RUNTIME_DIR")
61
+ - getenv("USERPROFILE")
62
+
63
+
64
+The client opens the socket and allocates a Core proxy with id 0 and a Client
65
+proxy with id 1.
66
+
67
+The server will allocate a new Core resource with id 0.
68
+
69
+The client sends the Core::Hello message on the socket to start the
70
+communication.
71
+
72
+The server binds the client resource with id 1 to the client.
73
+
74
+The client then sends client properties to the server.
75
+
76
+```
77
+   client                                    server
78
+     |---------------------------------------->|
79
+     |            open socket                  |
80
+     |                                         |
81
+     |---------------------------------------->|
82
+     |            Core::Hello(version)         |
83
+     |                                         |
84
+     |---------------------------------------->|
85
+     |       Client::UpdateProperties          |
86
+     .                                         .
87
+```
88
+
89
+This completes the setup of the client. The newly connected client will
90
+appear in the registry at this point.
91
+
92
+# Core proxy/resource
93
+
94
+The core is always the object with Id 0.
95
+
96
+## Core Methods (Id 0)
97
+
98
+### Core::Hello (Opcode 1)
99
+
100
+The first message sent by a client is the Hello message and contains the
101
+version number of the client.
102
+
103
+```
104
+   Struct(
105
+      Int: version
106
+   )
107
+```
108
+
109
+The version is 3.
110
+
111
+### Core::Sync (Opcode 2)
112
+
113
+The Sync message will result in a Done event from the server. When the Done
114
+event is received, the client can be sure that all operations before the Sync
115
+method have been completed.
116
+
117
+```
118
+   Struct(
119
+      Int: id
120
+      Int: seq
121
+   )
122
+```
123
+
124
+- id: the id will be returned in the Done event.
125
+- seq: is usually generated automatically and will be returned in the Done event.
126
+
127
+### Core::Pong (Opcode 3)
128
+
129
+Is sent from the client to the server when the server emits the Ping event.
130
+The id and seq should be copied from the Ping event.
131
+
132
+```
133
+   Struct(
134
+      Int: id
135
+      Int: seq
136
+   )
137
+```
138
+
139
+### Core::Error (Opcode 4)
140
+
141
+An error occured in an object on the client.
142
+
143
+```
144
+   Struct(
145
+      Int: id
146
+      Int: seq
147
+      Int: res
148
+      String: message
149
+   )
150
+```
151
+
152
+- id: The id of the proxy that is in error.
153
+- seq: a seq number from the failing request (if any)
154
+- res: a negative errno style error code
155
+- message: an error message
156
+
157
+### Core::GetRegistry (Opcode 5)
158
+
159
+A client requests to bind to the registry object and list the available objects
160
+on the server.
161
+
162
+Like with all bindings, first the client allocates a new proxy id and puts this
163
+as the new_id field. Methods and Events can then be sent and received on the 
164
+new_id (in the message Id field).
165
+
166
+```
167
+   Struct(
168
+      Int: version
169
+      Int: new_id
170
+   )
171
+```
172
+- version: the version of the registry interface used on the client
173
+- new_id: the id of the new proxy with the registry interface
174
+
175
+After this method, the server will start sending Registry::Global events
176
+to the proxy with new_id.
177
+
178
+```
179
+   client                                    server
180
+     |                                         |
181
+     | new proxy(new_id)                       |
182
+     |---------------------------------------->|  new resource(new_id)
183
+     |      Core::GetRegistry(version)         |
184
+     |                                         |
185
+     |<----------------------------------------| new_id
186
+     |      Registry::Global()                 |
187
+     |<----------------------------------------|
188
+     |      Registry::Global()                 |
189
+     .                                         .
190
+```
191
+
192
+There is no explicit last Global event to signal that the last object
193
+has been received. The usual way of knowing this is to send a Core::Sync
194
+method right after the Core::GetRegistry method and to wait for the
195
+Core::Done event.
196
+
197
+```
198
+   client                                    server
199
+     |                                         |
200
+     | new proxy(new_id)                       |
201
pipewire-0.3.71.tar.gz/doc/pipewire-scheduling.dox Added
193
 
1
@@ -0,0 +1,191 @@
2
+/** \page page_scheduling Graph Scheduling
3
+
4
+This document tries to explain how the PipeWire graph is scheduled.
5
+
6
+Graph are constructed from linked nodes together with their ports. This
7
+results in a dependency graph between nodes. Special care is taken for
8
+loopback links so that the graph remains a directed graph.
9
+
10
+
11
+# Nodes
12
+
13
+Nodes are objects with 0 or more input and output ports.
14
+
15
+Each node also has:
16
+
17
+- an eventfd to signal the node that it can start processing
18
+- an activation record that lives in shared memory with memfd.
19
+
20
+```
21
+   evenfd  
22
+  +-^---------+
23
+  |           |
24
+ in          out
25
+  |           |
26
+  +-v---------+
27
+    activation {
28
+      status:OK,        // bitmask of NEED_DATA, HAVE_DATA or OK
29
+      pending:0,   // number of unsatisfied dependencies to be able to run
30
+      required:0        // number of dependencies with other nodes
31
+    }
32
+```
33
+
34
+The activation record has the following information:
35
+
36
+ - processing state and pending dependencies. As long as there are pending dependencies
37
+   the node can not be processed. This is the only relevant information for actually
38
+   scheduling the graph and is shown in the above illustration.
39
+ - Current status of the node and profiling info (TRIGGERED, AWAKE, FINISHED, timestamps
40
+   when the node changed state).
41
+ - Timing information, mostly for drivers when the processing started, the time, duration
42
+   and rate (quantum) etc..
43
+ - Information about repositions (seek) and timebase owners.
44
+
45
+
46
+# links.
47
+
48
+When two nodes are linked together, the output node becomes a dependency for the input
49
+node. This means the input node can only start processing when the output node is finished.
50
+
51
+This dependency is reflected in the required counter in the activation record. In below
52
+illustration, B's required field is incremented with 1. The pending field is set to the
53
+required field when the graph is started. Node A will keep a list of all targets (B) that it
54
+is a dependency of.
55
+
56
+This dependency update is only performed when the link is ready (negotiated) and the nodes
57
+are ready to schedule (runnable).
58
+
59
+
60
+```
61
+   evenfd                                 eventfd
62
+  +-^---------+                          +-^---------+
63
+  |           |        link              |           |
64
+ in     A    out ---------------------> in     B    out
65
+  |           |                          |           |
66
+  +-v---------+                          +-v---------+
67
+    activation {       target              activation {
68
+      status:OK,  -------------------->      status:OK,
69
+      pending:0,                             pending:1,
70
+      required:0                             required:1
71
+    }                                      }
72
+```
73
+
74
+Multiple links between A and B will only result in 1 target link between A and B.
75
+
76
+
77
+# Drivers
78
+
79
+The graph can only run if there is a driver node that is in some way linked to an
80
+active node.
81
+
82
+The driver is special because it will have to initiate the processing in the graph. It
83
+will use a timer or some sort of interrupt from hardware to start the cycle.
84
+
85
+Any node can also be a candidate for a driver (when the node.driver property is true).
86
+PipeWire will select the node with the highest priority.driver property as the driver.
87
+
88
+Nodes will be assigned to the driver node they will be scheduled with. Each node holds
89
+a reference to the driver and increments the required field of the driver.
90
+
91
+When a node is ready to be scheduled, the driver adds the node to its list of targets
92
+and increments the required field.
93
+
94
+
95
+```
96
+   evenfd                                 eventfd
97
+  +-^---------+                          +-^---------+
98
+  |           |        link              |           |
99
+ in     A    out ---------------------> in     B    out
100
+  |           |                          |           |
101
+  +-v---------+                          +-v---------+
102
+    activation {       target              activation {
103
+      status:OK,  -------------------->      status:OK,
104
+      pending:0,                             pending:0,
105
+      required:1                             required:2
106
+    }                                      }
107
+      |    ^                         ^
108
+      |    |                        /   /
109
+      |    |                       /   /
110
+      |    |                      /   /
111
+      |    |                     /   /
112
+      |    |                    /   / 
113
+      v    |     /-------------/   /
114
+    activation {                  /
115
+      status:OK, V---------------/
116
+      pending:0, 
117
+      required:2
118
+    }
119
+  +-^---------+
120
+  |           |
121
+  |   driver  |
122
+  |           |
123
+  +-v---------+
124
+   eventfd
125
+```
126
+
127
+As seen in the illustration above, the driver holds a link to each node it needs to
128
+schedule and each node holds a link to the driver. Some nodes hold a link to other
129
+nodes.
130
+
131
+It is possible that the driver is the same as a node in the graph (for example node A)
132
+but conceptually, the links above are still valid.
133
+
134
+The driver will then start processing the graph by emitting the ready signal. PipeWire
135
+will then:
136
+
137
+ - Perform some statistics about the previous cycle. Did it complete? compute processing
138
+   times, cpu usage etc.
139
+ - Perform reposition requests if any, timebase changes, etc..
140
+ - The pending counter of each follower node is set to the required field.
141
+ - it then loops over all targets of the driver and atomically decrements the required
142
+   field of the activation record. When the required field is 0, the eventfd is signaled
143
+   and the node can be scheduled.
144
+
145
+In our example above, Node A and be will have their pending state decremented. Node A
146
+will be 0 and will be triggered first (node B has 2 pending dependencies to start with and
147
+will not be triggered yet). The driver itself also has 2 dependcies left and will not
148
+be triggered (complete) yet.
149
+
150
+## Scheduling node A
151
+
152
+When the eventfd is signaled on a node, we say the node is triggered and it will be able
153
+to process data. It consumes the input on the input ports and produces more data on the
154
+output ports.
155
+
156
+After processing, node A goes through the list of targets and decrements each pending
157
+field (node A has a reference to B and the driver).
158
+
159
+In our above example, the driver is decremented (from 2 to 1) but is not yet triggered.
160
+node B is decremented (from 1 to 0) and is triggered by writing to the eventfd.
161
+
162
+## Scheduling node B
163
+
164
+Node B is scheduled and processes the input from node A. It then goes through the list of
165
+targets and decrements the pending fields.  It decrements the pending field of the
166
+driver (from 1 to 0) and triggers the driver.
167
+
168
+## Scheduling the driver
169
+
170
+The graph always completes after the driver is triggered and scheduled. All required
171
+fields from all the nodes in the target list of the driver are now 0.
172
+
173
+# Remote nodes.
174
+
175
+For remote nodes, the eventfd and the activation is transfered from the server
176
+to the client.
177
+
178
+This means that writing to the remote client eventfd will wake the client directly
179
+without going to the server first. 
180
+
181
+All remote clients also get the activation and eventfd of the peer and driver they
182
+are linked to and can directly trigger peers and drivers without going to the
183
+server first.
184
+
185
+## Remote driver nodes.
186
+
187
+Currently the graph start cycle is managed by the server.
188
+
189
+Remote driver nodes therefore have an extra eventfd to wake up the server and signal
190
+the graph start.
191
+
192
+*/
193
pipewire-0.3.70.tar.gz/doc/pipewire.dox -> pipewire-0.3.71.tar.gz/doc/pipewire.dox Changed
10
 
1
@@ -10,6 +10,8 @@
2
 - \subpage page_objects_design
3
 - \subpage page_library
4
 - \subpage page_dma_buf
5
+- \subpage page_scheduling
6
+- \subpage page_native_protocol
7
 
8
 
9
 # Components
10
pipewire-0.3.70.tar.gz/doc/spa-pod.dox -> pipewire-0.3.71.tar.gz/doc/spa-pod.dox Changed
201
 
1
@@ -517,11 +517,515 @@
2
 
3
 # POD Layout
4
 
5
-Each POD has a 32 bits size field, followed by a 32 bits type field. The size
6
-field specifies the size following the type field.
7
-
8
-Each POD is aligned to an 8 byte boundary.
9
-
10
+A POD always starts with a size/type pair of uint32_t in native endianness,
11
+followed by size in bytes of the payload data and padding. See
12
+\ref page_spa_pod for more details.
13
+
14
+The payload is always padded to 8 bytes so that a complete pod is always
15
+a multiple of 8 bytes.
16
+
17
+```
18
+    0                   1                   2                   3
19
+    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
20
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
21
+   |   size                                                        |
22
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
23
+   |   type                                                        |
24
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
25
+   |   payload ...                                                 |
26
+   .                                                |  ... padding .
27
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
28
+```
29
+
30
+The total size of the POD is thus ROUND_UP_8(8 + size).
31
+
32
+# POD Types
33
+
34
+Here follows the layout of the POD types.
35
+
36
+## None (1)
37
+
38
+Type 1 is the None type or the null pointer. It has a size of 0 and thus
39
+no payload.
40
+
41
+```
42
+    0                   1                   2                   3
43
+    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
44
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
45
+   |   0                                                           |
46
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
47
+   |   1                                                           |
48
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
49
+```
50
+
51
+## Bool (2)
52
+
53
+Type 2 is the Bool type. I contains a true or false value. The value is
54
+stored in a int32, a value of 0 is false, any other value is true.
55
+
56
+```
57
+    0                   1                   2                   3
58
+    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
59
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
60
+   |   4                                                           |
61
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
62
+   |   2                                                           |
63
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
64
+   |   value (int32)                                               |
65
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
66
+   |   padding                                                     |
67
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
68
+```
69
+
70
+## Id (3)
71
+
72
+An id is stored as a uint32. The id refers to an index in a table where more
73
+information about the value can be found. This is typically a type table
74
+containing some well known ids.
75
+
76
+```
77
+    0                   1                   2                   3
78
+    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
79
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
80
+   |   4                                                           |
81
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
82
+   |   3                                                           |
83
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
84
+   |   id (uint32)                                                 |
85
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
86
+   |   padding                                                     |
87
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
88
+```
89
+
90
+## Int (4)
91
+
92
+A 32 bit signed integer.
93
+
94
+```
95
+    0                   1                   2                   3
96
+    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
97
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
98
+   |   4                                                           |
99
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
100
+   |   4                                                           |
101
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
102
+   |   value (int32)                                               |
103
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
104
+   |   padding                                                     |
105
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
106
+```
107
+
108
+## Long (5)
109
+
110
+A 64 bit signed integer.
111
+
112
+```
113
+    0                   1                   2                   3
114
+    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
115
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
116
+   |   4                                                           |
117
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
118
+   |   5                                                           |
119
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
120
+   |   value (int64)                                               |
121
+   +                                                               +
122
+   |                                                               |
123
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
124
+```
125
+
126
+## Float (6)
127
+
128
+A 32 bit float value.
129
+
130
+```
131
+    0                   1                   2                   3
132
+    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
133
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
134
+   |   4                                                           |
135
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
136
+   |   6                                                           |
137
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
138
+   |   value (float32)                                             |
139
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
140
+   |   padding                                                     |
141
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
142
+```
143
+
144
+## Double (7)
145
+
146
+A 64 bit float value.
147
+
148
+```
149
+    0                   1                   2                   3
150
+    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
151
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
152
+   |   4                                                           |
153
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
154
+   |   7                                                           |
155
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
156
+   |   value (float64)                                             |
157
+   +                                                               +
158
+   |                                                               |
159
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
160
+```
161
+
162
+## String (8)
163
+
164
+A string. This does not have to be valid UTF8 but it is 0 terminated.
165
+The size field is set to the length of the string, including the 0
166
+byte.
167
+
168
+```
169
+    0                   1                   2                   3
170
+    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
171
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
172
+   |   size                                                        |
173
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
174
+   |   8                                                           |
175
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
176
+   |   chars ....                                                  |
177
+   .                                                               .
178
+   |                                            ... 0 | padding..  |
179
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
180
+```
181
+
182
+
183
+## Bytes (9)
184
+
185
+A byte array.  The size field is set to the number of bytes.
186
+
187
+```
188
+    0                   1                   2                   3
189
+    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
190
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
191
+   |   size                                                        |
192
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
193
+   |   9                                                           |
194
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
195
+   |   bytes ....                                                  |
196
+   .                                                               .
197
+   |                                                  | padding..  |
198
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
199
+```
200
+
201
pipewire-0.3.70.tar.gz/man/pw-cli.1.rst.in -> pipewire-0.3.71.tar.gz/man/pw-cli.1.rst.in Changed
52
 
1
@@ -26,8 +26,11 @@
2
 *pipewire-0*.
3
 
4
 Connections to other, remote instances can be made. The current instance
5
-name is displayed at the prompt. Some commands operate on the current
6
-instance and some on the local instance.
7
+name is displayed at the prompt.
8
+
9
+Note that **pw-cli** also creates a local PipeWire instance. Some commands
10
+operate on the current (remote) instance and some on the local instance, such
11
+as module loading.
12
 
13
 Use the 'help' command to list the available commands.
14
 
15
@@ -50,12 +53,15 @@
16
 | instance.
17
 
18
 load-module *name* *arguments...*
19
-  Load a module specified by its name and arguments. For most
20
-  modules it is OK to be loaded more than once.
21
+  Load a module specified by its name and arguments in the local instance.
22
+  For most modules it is OK to be loaded more than once.
23
 
24
   This command returns a module variable that can be used
25
   to unload the module.
26
 
27
+  The locally module is *not* visible in the remote instance. It is not
28
+  possible in PipeWire to load modules in a remote instance.
29
+
30
 unload-module *module-var*
31
   Unload a module, specified either by its variable.
32
 
33
@@ -82,6 +88,9 @@
34
   If no remote name is specified, a connection is made to
35
   the default remote instance, usually *pipewire-0*.
36
 
37
+  The special remote name called *internal* can be used to connect to
38
+  the local **pw-cli** PipeWire instance.
39
+
40
   This command returns a remote var that can be used to disconnect or
41
   switch remotes.
42
 
43
@@ -96,7 +105,7 @@
44
 switch-remote *remote-var*
45
   Make the specified *remote* the current instance.
46
 
47
-  If no remote name is specified, the local instance is made current.
48
+  If no remote name is specified, the first instance is made current.
49
 
50
 NODE MANAGEMENT
51
 ===============
52
pipewire-0.3.70.tar.gz/meson.build -> pipewire-0.3.71.tar.gz/meson.build Changed
16
 
1
@@ -1,5 +1,5 @@
2
 project('pipewire', 'c' ,
3
-  version : '0.3.70',
4
+  version : '0.3.71',
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
@@ -191,6 +191,7 @@
9
 versiondata.set_quoted('PIPEWIRE_API_VERSION', apiversion)
10
 
11
 cdata = configuration_data()
12
+cdata.set_quoted('PREFIX', prefix)
13
 cdata.set_quoted('PIPEWIRE_CONFDATADIR', pipewire_confdatadir)
14
 cdata.set_quoted('LOCALEDIR', pipewire_localedir)
15
 cdata.set_quoted('LIBDIR', pipewire_libdir)
16
pipewire-0.3.70.tar.gz/pipewire-alsa/alsa-plugins/pcm_pipewire.c -> pipewire-0.3.71.tar.gz/pipewire-alsa/alsa-plugins/pcm_pipewire.c Changed
66
 
1
@@ -64,6 +64,7 @@
2
    unsigned int xrun_detected:1;
3
    unsigned int hw_params_changed:1;
4
    unsigned int active:1;
5
+   unsigned int negotiated:1;
6
 
7
    snd_pcm_uframes_t hw_ptr;
8
    snd_pcm_uframes_t boundary;
9
@@ -385,6 +386,20 @@
10
            SPA_PARAM_BUFFERS_stride,  SPA_POD_Int(pw->stride));
11
 
12
    pw_stream_update_params(pw->stream, params, n_params);
13
+
14
+   pw->negotiated = true;
15
+   pw_thread_loop_signal(pw->main_loop, false);
16
+}
17
+
18
+static void on_stream_state_changed(void *data, enum pw_stream_state old, enum pw_stream_state state, const char *error)
19
+{
20
+   snd_pcm_pipewire_t *pw = data;
21
+
22
+   if (state == PW_STREAM_STATE_ERROR) {
23
+       pw_log_warn("%s", error);
24
+       pw->error = -EIO;
25
+       update_active(&pw->io);
26
+   }
27
 }
28
 
29
 static void on_stream_drained(void *data)
30
@@ -463,6 +478,7 @@
31
 static const struct pw_stream_events stream_events = {
32
    PW_VERSION_STREAM_EVENTS,
33
    .param_changed = on_stream_param_changed,
34
+   .state_changed = on_stream_state_changed,
35
    .process = on_stream_process,
36
    .drained = on_stream_drained,
37
 };
38
@@ -546,6 +562,7 @@
39
 
40
    pw->error = 0;
41
 
42
+   pw->negotiated = false;
43
    pw_stream_connect(pw->stream,
44
                io->stream == SND_PCM_STREAM_PLAYBACK ?
45
                PW_DIRECTION_OUTPUT :
46
@@ -563,13 +580,18 @@
47
    pw->drained = false;
48
    pw->draining = false;
49
 
50
+   while (!pw->negotiated && pw->error >= 0)
51
+       pw_thread_loop_wait(pw->main_loop);
52
+   if (pw->error < 0)
53
+       goto error;
54
+
55
    pw_thread_loop_unlock(pw->main_loop);
56
 
57
    return 0;
58
 
59
 error:
60
    pw_thread_loop_unlock(pw->main_loop);
61
-   return -ENOMEM;
62
+   return pw->error < 0 ? pw->error : -ENOMEM;
63
 }
64
 
65
 static int snd_pcm_pipewire_start(snd_pcm_ioplug_t *io)
66
pipewire-0.3.70.tar.gz/pipewire-jack/jack/control.h -> pipewire-0.3.71.tar.gz/pipewire-jack/jack/control.h Changed
10
 
1
@@ -646,7 +646,7 @@
2
    const char *format,
3
    ...);
4
 
5
-/* @} */
6
+/**@}*/
7
 
8
 #if 0
9
 { /* Adjust editor indent */
10
pipewire-0.3.70.tar.gz/pipewire-jack/jack/jack.h -> pipewire-0.3.71.tar.gz/pipewire-jack/jack/jack.h Changed
118
 
1
@@ -230,7 +230,7 @@
2
  */
3
 jack_native_thread_t jack_client_thread_id (jack_client_t *client) JACK_OPTIONAL_WEAK_EXPORT;
4
 
5
-/*@}*/
6
+/**@}*/
7
 
8
 /**
9
  * @param client pointer to JACK client structure.
10
@@ -289,7 +289,7 @@
11
 */
12
 int jack_set_process_thread(jack_client_t* client, JackThreadCallback thread_callback, void *arg) JACK_OPTIONAL_WEAK_EXPORT;
13
 
14
-/*@}*/
15
+/**@}*/
16
 
17
 /**
18
  * @defgroup ClientCallbacks Setting Client Callbacks
19
@@ -557,8 +557,6 @@
20
 int jack_set_xrun_callback (jack_client_t *client,
21
                             JackXRunCallback xrun_callback, void *arg) JACK_OPTIONAL_WEAK_EXPORT;
22
 
23
-/*@}*/
24
-
25
 /**
26
  * Tell the Jack server to call @a latency_callback whenever it
27
  * is necessary to recompute the latencies for some or all
28
@@ -609,7 +607,7 @@
29
  * @ref jack_port_get_latency_range(), which only returns meaningful
30
  * values when ports get connected and latency values change.
31
  *
32
- * See the documentation for  @ref jack_port_set_latency_range()
33
+ * See the documentation for @ref jack_port_set_latency_range()
34
  * on how the callback should operate. Remember that the @a mode
35
  * argument given to the latency callback will need to be
36
  * passed into @ref jack_port_set_latency_range()
37
@@ -619,7 +617,7 @@
38
 int jack_set_latency_callback (jack_client_t *client,
39
                   JackLatencyCallback latency_callback,
40
                   void *) JACK_WEAK_EXPORT;
41
-/*@}*/
42
+/**@}*/
43
 
44
 /**
45
  * @defgroup ServerClientControl Controlling & querying JACK server operation
46
@@ -706,7 +704,7 @@
47
  */
48
 float jack_cpu_load (jack_client_t *client) JACK_OPTIONAL_WEAK_EXPORT;
49
 
50
-/*@}*/
51
+/**@}*/
52
 
53
 /**
54
  * @defgroup PortFunctions Creating & manipulating ports
55
@@ -1049,10 +1047,11 @@
56
  */
57
 size_t jack_port_type_get_buffer_size (jack_client_t *client, const char *port_type) JACK_WEAK_EXPORT;
58
 
59
-/*@}*/
60
+/**@}*/
61
 
62
 /**
63
  * @defgroup LatencyFunctions Managing and determining latency
64
+ * @{
65
  *
66
  * The purpose of JACK's latency API is to allow clients to
67
  * easily answer two questions:
68
@@ -1097,7 +1096,6 @@
69
  * clients that add latency to the signal path should interact
70
  * with JACK to ensure that the correct latency figures are
71
  * used.
72
- * @{
73
  */
74
 
75
 /**
76
@@ -1263,7 +1261,7 @@
77
  */
78
 int jack_recompute_total_latency (jack_client_t*, jack_port_t* port) JACK_OPTIONAL_WEAK_DEPRECATED_EXPORT;
79
 
80
-/*@}*/
81
+/**@}*/
82
 
83
 /**
84
  * @defgroup PortSearching Looking up ports
85
@@ -1304,7 +1302,7 @@
86
 jack_port_t * jack_port_by_id (jack_client_t *client,
87
                                jack_port_id_t port_id) JACK_OPTIONAL_WEAK_EXPORT;
88
 
89
-/*@}*/
90
+/**@}*/
91
 
92
 /**
93
  * @defgroup TimeFunctions Handling time
94
@@ -1412,12 +1410,12 @@
95
  */
96
 jack_time_t jack_get_time(void) JACK_OPTIONAL_WEAK_EXPORT;
97
 
98
-/*@}*/
99
+/**@}*/
100
 
101
 /**
102
  * @defgroup ErrorOutput Controlling error/information output
103
+ * @{
104
  */
105
-/*@{*/
106
 
107
 /**
108
  * Display JACK error message.
109
@@ -1457,7 +1455,7 @@
110
  */
111
 void jack_set_info_function (void (*func)(const char *)) JACK_OPTIONAL_WEAK_EXPORT;
112
 
113
-/*@}*/
114
+/**@}*/
115
 
116
 /**
117
  * The free function to be used on memory returned by jack_port_get_connections,
118
pipewire-0.3.70.tar.gz/pipewire-jack/jack/midiport.h -> pipewire-0.3.71.tar.gz/pipewire-jack/jack/midiport.h Changed
10
 
1
@@ -185,7 +185,7 @@
2
 uint32_t
3
 jack_midi_get_lost_event_count(void *port_buffer) JACK_OPTIONAL_WEAK_EXPORT;
4
 
5
-/*@}*/
6
+/**@}*/
7
 
8
 #ifdef __cplusplus
9
 }
10
pipewire-0.3.70.tar.gz/pipewire-jack/jack/ringbuffer.h -> pipewire-0.3.71.tar.gz/pipewire-jack/jack/ringbuffer.h Changed
12
 
1
@@ -50,8 +50,8 @@
2
 
3
 typedef struct {
4
     char   *buf;
5
-    volatile size_t write_ptr;
6
-    volatile size_t read_ptr;
7
+    size_t write_ptr;
8
+    size_t read_ptr;
9
     size_t size;
10
     size_t size_mask;
11
     int    mlocked;
12
pipewire-0.3.70.tar.gz/pipewire-jack/jack/session.h -> pipewire-0.3.71.tar.gz/pipewire-jack/jack/session.h Changed
10
 
1
@@ -33,7 +33,7 @@
2
  *
3
  * @deprecated Use of JACK-Session is currently deprecated and unsupported.
4
  * JACK developers recommend the use of NSM instead.
5
- * See https://github.com/linuxaudio/new-session-manager
6
+ * See https://new-session-manager.jackaudio.org/
7
  * @{
8
  */
9
 
10
pipewire-0.3.70.tar.gz/pipewire-jack/jack/thread.h -> pipewire-0.3.71.tar.gz/pipewire-jack/jack/thread.h Changed
10
 
1
@@ -151,7 +151,7 @@
2
 
3
 #endif
4
 
5
-/* @} */
6
+/**@}*/
7
 
8
 #ifdef __cplusplus
9
 }
10
pipewire-0.3.70.tar.gz/pipewire-jack/jack/transport.h -> pipewire-0.3.71.tar.gz/pipewire-jack/jack/transport.h Changed
10
 
1
@@ -238,7 +238,7 @@
2
 void jack_set_transport_info (jack_client_t *client,
3
                  jack_transport_info_t *tinfo) JACK_OPTIONAL_WEAK_EXPORT;
4
 
5
-/*@}*/
6
+/**@}*/
7
 
8
 #ifdef __cplusplus
9
 }
10
pipewire-0.3.70.tar.gz/pipewire-jack/jack/types.h -> pipewire-0.3.71.tar.gz/pipewire-jack/jack/types.h Changed
49
 
1
@@ -538,11 +538,12 @@
2
  */
3
 typedef enum {
4
 
5
-    JackPositionBBT = 0x10,     /**< Bar, Beat, Tick */
6
-    JackPositionTimecode = 0x20,        /**< External timecode */
7
-    JackBBTFrameOffset =      0x40,     /**< Frame offset of BBT information */
8
-    JackAudioVideoRatio =     0x80, /**< audio frames per video frame */
9
-    JackVideoFrameOffset =   0x100  /**< frame offset of first video frame */
10
+    JackPositionBBT      = 0x10,  /**< Bar, Beat, Tick */
11
+    JackPositionTimecode = 0x20,  /**< External timecode */
12
+    JackBBTFrameOffset   = 0x40,  /**< Frame offset of BBT information */
13
+    JackAudioVideoRatio  = 0x80,  /**< audio frames per video frame */
14
+    JackVideoFrameOffset = 0x100, /**< frame offset of first video frame */
15
+    JackTickDouble       = 0x200, /**< double-resolution tick */
16
 
17
 } jack_position_bits_t;
18
 
19
@@ -550,6 +551,9 @@
20
 #define JACK_POSITION_MASK (JackPositionBBT|JackPositionTimecode)
21
 #define EXTENDED_TIME_INFO
22
 
23
+/** transport tick_double member is available for use */
24
+#define JACK_TICK_DOUBLE
25
+
26
 PRE_PACKED_STRUCTURE
27
 struct _jack_position {
28
 
29
@@ -609,10 +613,18 @@
30
                          set, but the value is zero, there is
31
                          no video frame within this cycle. */
32
 
33
+    /* JACK extra transport fields */
34
+
35
+    double              tick_double; /**< current tick-within-beat in double resolution.
36
+                         Should be assumed zero if JackTickDouble is not set.
37
+                         Since older versions of JACK do not expose this variable,
38
+                         the macro JACK_TICK_DOUBLE is provided,
39
+                         which can be used as build-time detection. */
40
+
41
     /* For binary compatibility, new fields should be allocated from
42
      * this padding area with new valid bits controlling access, so
43
      * the existing structure size and offsets are preserved. */
44
-    int32_t             padding7;
45
+    int32_t             padding5;
46
 
47
     /* When (unique_1 == unique_2) the contents are consistent. */
48
     jack_unique_t       unique_2;       /**< unique ID */
49
pipewire-0.3.70.tar.gz/pipewire-jack/jack/weakjack.h -> pipewire-0.3.71.tar.gz/pipewire-jack/jack/weakjack.h Changed
9
 
1
@@ -120,6 +120,6 @@
2
 #endif
3
 #endif
4
 
5
-/*@}*/
6
+/**@}*/
7
 
8
 #endif /* weakjack */
9
pipewire-0.3.70.tar.gz/pipewire-jack/src/pipewire-jack.c -> pipewire-0.3.71.tar.gz/pipewire-jack/src/pipewire-jack.c Changed
201
 
1
@@ -24,6 +24,7 @@
2
 #include <spa/debug/pod.h>
3
 #include <spa/utils/json.h>
4
 #include <spa/utils/string.h>
5
+#include <spa/utils/ringbuffer.h>
6
 
7
 #include <pipewire/pipewire.h>
8
 #include <pipewire/private.h>
9
@@ -50,6 +51,8 @@
10
 #define MAX_MIX                1024
11
 #define MAX_BUFFER_FRAMES      8192
12
 
13
+#define MAX_CLIENT_PORTS       768
14
+
15
 #define MAX_ALIGN          16
16
 #define MAX_BUFFERS            2
17
 #define MAX_BUFFER_DATAS       1u
18
@@ -70,6 +73,28 @@
19
 #define SELF_CONNECT_FAIL_ALL  -2
20
 #define SELF_CONNECT_IGNORE_ALL    2
21
 
22
+#define NOTIFY_BUFFER_SIZE (1u<<13)
23
+#define NOTIFY_BUFFER_MASK (NOTIFY_BUFFER_SIZE-1)
24
+
25
+struct notify {
26
+#define NOTIFY_ACTIVE_FLAG     (1<<0)
27
+
28
+#define NOTIFY_TYPE_NONE       ((0<<4)|NOTIFY_ACTIVE_FLAG)
29
+#define NOTIFY_TYPE_REGISTRATION   ((1<<4))
30
+#define NOTIFY_TYPE_PORTREGISTRATION   ((2<<4)|NOTIFY_ACTIVE_FLAG)
31
+#define NOTIFY_TYPE_CONNECT        ((3<<4)|NOTIFY_ACTIVE_FLAG)
32
+#define NOTIFY_TYPE_GRAPH      ((4<<4)|NOTIFY_ACTIVE_FLAG)
33
+#define NOTIFY_TYPE_BUFFER_FRAMES  ((5<<4)|NOTIFY_ACTIVE_FLAG)
34
+#define NOTIFY_TYPE_SAMPLE_RATE        ((6<<4)|NOTIFY_ACTIVE_FLAG)
35
+#define NOTIFY_TYPE_FREEWHEEL      ((7<<4)|NOTIFY_ACTIVE_FLAG)
36
+#define NOTIFY_TYPE_SHUTDOWN       ((8<<4)|NOTIFY_ACTIVE_FLAG)
37
+#define NOTIFY_TYPE_LATENCY        ((9<<4)|NOTIFY_ACTIVE_FLAG)
38
+   int type;
39
+   struct object *object;
40
+   int arg1;
41
+   const char *msg;
42
+};
43
+
44
 struct client;
45
 struct port;
46
 
47
@@ -141,6 +166,7 @@
48
    struct pw_proxy *proxy;
49
    struct spa_hook proxy_listener;
50
    struct spa_hook object_listener;
51
+   int registered;
52
    unsigned int removing:1;
53
    unsigned int removed:1;
54
 };
55
@@ -296,7 +322,12 @@
56
 
57
    uint32_t node_id;
58
    uint32_t serial;
59
+   struct object *object;
60
+
61
    struct spa_source *socket_source;
62
+   struct spa_source *notify_source;
63
+   void *notify_buffer;
64
+   struct spa_ringbuffer notify_ring;
65
 
66
    JackThreadCallback thread_callback;
67
    void *thread_arg;
68
@@ -345,6 +376,7 @@
69
 
70
    struct spa_list free_ports;
71
    struct pw_map ports2;
72
+   uint32_t n_ports;
73
 
74
    struct spa_list links;
75
    uint32_t driver_id;
76
@@ -374,6 +406,7 @@
77
    unsigned int warn_mlock:1;
78
    unsigned int timeowner_conditional:1;
79
    unsigned int show_monitor:1;
80
+   unsigned int show_midi:1;
81
    unsigned int merge_monitor:1;
82
    unsigned int short_name:1;
83
    unsigned int filter_name:1;
84
@@ -385,12 +418,35 @@
85
    unsigned int fix_midi_events:1;
86
    unsigned int global_buffer_size:1;
87
    unsigned int passive_links:1;
88
+   unsigned int graph_callback_pending:1;
89
+   unsigned int pending_callbacks:1;
90
+   int frozen_callbacks;
91
    char filter_char;
92
+   uint32_t max_ports;
93
+   unsigned int fill_aliases:1;
94
 
95
    jack_position_t jack_position;
96
    jack_transport_state_t jack_state;
97
 };
98
 
99
+#define return_val_if_fail(expr, val)              \
100
+({                             \
101
+   if (SPA_UNLIKELY(!(expr))) {                \
102
+       pw_log_warn("'%s' failed at %s:%u %s()",    \
103
+           #expr , __FILE__, __LINE__, __func__);  \
104
+       return (val);                   \
105
+   }                           \
106
+})
107
+
108
+#define return_if_fail(expr)                   \
109
+({                             \
110
+   if (SPA_UNLIKELY(!(expr))) {                \
111
+       pw_log_warn("'%s' failed at %s:%u %s()",    \
112
+           #expr , __FILE__, __LINE__, __func__);  \
113
+       return;                     \
114
+   }                           \
115
+})
116
+
117
 static int do_sync(struct client *client);
118
 static struct object *find_by_serial(struct client *c, uint32_t serial);
119
 
120
@@ -556,6 +612,11 @@
121
    struct object *o;
122
    uint32_t i;
123
 
124
+   if (c->n_ports >= c->max_ports) {
125
+       errno = ENOSPC;
126
+       return NULL;
127
+   }
128
+
129
    if (spa_list_is_empty(&c->free_ports)) {
130
        p = calloc(OBJECT_CHUNK, sizeof(struct port));
131
        if (p == NULL)
132
@@ -567,6 +628,9 @@
133
    spa_list_remove(&p->link);
134
 
135
    o = alloc_object(c, INTERFACE_Port);
136
+   if (o == NULL)
137
+       return NULL;
138
+
139
    o->id = SPA_ID_INVALID;
140
    o->port.node_id = c->node_id;
141
    o->port.port = p;
142
@@ -583,6 +647,7 @@
143
    p->direction = direction;
144
    p->emptyptr = SPA_PTR_ALIGN(p->empty, MAX_ALIGN, float);
145
    p->port_id = pw_map_insert_new(&c->portsdirection, p);
146
+   c->n_ports++;
147
 
148
    pthread_mutex_lock(&c->context.lock);
149
    spa_list_append(&c->context.objects, &o->link);
150
@@ -591,17 +656,21 @@
151
    return p;
152
 }
153
 
154
-static void free_port(struct client *c, struct port *p)
155
+static void free_port(struct client *c, struct port *p, bool free)
156
 {
157
    struct mix *m;
158
 
159
    spa_list_consume(m, &p->mix, port_link)
160
        free_mix(c, m);
161
 
162
+   c->n_ports--;
163
    pw_map_remove(&c->portsp->direction, p->port_id);
164
-   free_object(c, p->object);
165
    pw_properties_free(p->props);
166
    spa_list_append(&c->free_ports, &p->link);
167
+   if (free)
168
+       free_object(c, p->object);
169
+   else
170
+       p->object->removing = true;
171
 }
172
 
173
 static struct object *find_node(struct client *c, const char *name)
174
@@ -766,9 +835,9 @@
175
        *proto_ptr = 0;
176
 }
177
 
178
-#define do_callback_expr(c,expr,callback,...)          \
179
+#define do_callback_expr(c,expr,callback,do_emit,...)      \
180
 ({                             \
181
-   if (c->callback && c->active) {             \
182
+   if (c->callback && do_emit) {               \
183
        pw_thread_loop_unlock(c->context.loop);     \
184
        if (c->locked_process)              \
185
            pthread_mutex_lock(&c->rt_lock);    \
186
@@ -779,15 +848,14 @@
187
            pthread_mutex_unlock(&c->rt_lock);  \
188
        pw_thread_loop_lock(c->context.loop);       \
189
    } else {                        \
190
-       if (c->active)                  \
191
-           (expr);                 \
192
+       (expr);                     \
193
        pw_log_debug("skip " #callback          \
194
-           " cb:%p active:%d", c->callback,    \
195
-           c->active);             \
196
+           " cb:%p do_emit:%d", c->callback,   \
197
+           do_emit);               \
198
    }                           \
199
 })
200
 
201
pipewire-0.3.70.tar.gz/spa/include/spa/node/keys.h -> pipewire-0.3.71.tar.gz/spa/include/spa/node/keys.h Changed
9
 
1
@@ -31,6 +31,7 @@
2
 #define SPA_KEY_PORT_NAME      "port.name"     /**< a port name */
3
 #define SPA_KEY_PORT_ALIAS     "port.alias"        /**< a port alias */
4
 #define SPA_KEY_PORT_MONITOR       "port.monitor"      /**< this port is a monitor port */
5
+#define SPA_KEY_PORT_IGNORE_LATENCY    "port.ignore-latency"   /**< latency ignored by peers */
6
 
7
 
8
 /**
9
pipewire-0.3.70.tar.gz/spa/include/spa/node/node.h -> pipewire-0.3.71.tar.gz/spa/include/spa/node/node.h Changed
28
 
1
@@ -632,6 +632,16 @@
2
    _res;                               \
3
 })
4
 
5
+#define spa_node_method_fast(o,method,version,...)         \
6
+({                                 \
7
+   int _res;                           \
8
+   struct spa_node *_n = o;                    \
9
+   spa_interface_call_fast_res(&_n->iface,             \
10
+           struct spa_node_methods, _res,          \
11
+           method, version, ##__VA_ARGS__);        \
12
+   _res;                               \
13
+})
14
+
15
 #define spa_node_add_listener(n,...)       spa_node_method(n, add_listener, 0, __VA_ARGS__)
16
 #define spa_node_set_callbacks(n,...)      spa_node_method(n, set_callbacks, 0, __VA_ARGS__)
17
 #define spa_node_sync(n,...)           spa_node_method(n, sync, 0, __VA_ARGS__)
18
@@ -647,7 +657,9 @@
19
 #define spa_node_port_set_io(n,...)        spa_node_method(n, port_set_io, 0, __VA_ARGS__)
20
 
21
 #define spa_node_port_reuse_buffer(n,...)  spa_node_method(n, port_reuse_buffer, 0, __VA_ARGS__)
22
+#define spa_node_port_reuse_buffer_fast(n,...) spa_node_method_fast(n, port_reuse_buffer, 0, __VA_ARGS__)
23
 #define spa_node_process(n)            spa_node_method(n, process, 0)
24
+#define spa_node_process_fast(n)       spa_node_method_fast(n, process, 0)
25
 
26
 /**
27
  * \}
28
pipewire-0.3.70.tar.gz/spa/include/spa/node/utils.h -> pipewire-0.3.71.tar.gz/spa/include/spa/node/utils.h Changed
12
 
1
@@ -117,8 +117,8 @@
2
 
3
 #define spa_node_call(callbacks,method,version,...)            \
4
 ({                                 \
5
-   int _res = -ENOTSUP;                            \
6
-   spa_callbacks_call_res(callbacks, struct spa_node_callbacks,    \
7
+   int _res;                           \
8
+   spa_callbacks_call_fast_res(callbacks, struct spa_node_callbacks,   \
9
            _res, method, version, ##__VA_ARGS__);      \
10
    _res;                               \
11
 })
12
pipewire-0.3.70.tar.gz/spa/include/spa/param/latency-utils.h -> pipewire-0.3.71.tar.gz/spa/include/spa/param/latency-utils.h Changed
10
 
1
@@ -21,7 +21,7 @@
2
 #include <spa/param/latency.h>
3
 
4
 static inline int
5
-spa_latency_info_compare(const struct spa_latency_info *a, struct spa_latency_info *b)
6
+spa_latency_info_compare(const struct spa_latency_info *a, const struct spa_latency_info *b)
7
 {
8
    if (a->min_quantum == b->min_quantum &&
9
        a->max_quantum == b->max_quantum &&
10
pipewire-0.3.70.tar.gz/spa/include/spa/pod/parser.h -> pipewire-0.3.71.tar.gz/spa/include/spa/pod/parser.h Changed
10
 
1
@@ -455,7 +455,7 @@
2
        const struct spa_pod *pod = NULL;
3
        const char *format;
4
 
5
-       if (ftype == SPA_TYPE_Object) {
6
+       if (f && ftype == SPA_TYPE_Object) {
7
            uint32_t key = va_arg(args, uint32_t);
8
            const struct spa_pod_object *object;
9
 
10
pipewire-0.3.70.tar.gz/spa/include/spa/support/loop.h -> pipewire-0.3.71.tar.gz/spa/include/spa/support/loop.h Changed
45
 
1
@@ -123,7 +123,7 @@
2
    struct spa_hook_list *_l = l;                           \
3
    struct spa_hook *_h;                                \
4
    spa_list_for_each_reverse(_h, &_l->list, link)                  \
5
-       spa_callbacks_call(&_h->cb, struct spa_loop_control_hooks, before, 0);  \
6
+       spa_callbacks_call_fast(&_h->cb, struct spa_loop_control_hooks, before, 0); \
7
 })
8
 
9
 #define spa_loop_control_hook_after(l)                         \
10
@@ -131,7 +131,7 @@
11
    struct spa_hook_list *_l = l;                           \
12
    struct spa_hook *_h;                                \
13
    spa_list_for_each(_h, &_l->list, link)                      \
14
-       spa_callbacks_call(&_h->cb, struct spa_loop_control_hooks, after, 0);   \
15
+       spa_callbacks_call_fast(&_h->cb, struct spa_loop_control_hooks, after, 0);  \
16
 })
17
 
18
 /**
19
@@ -212,6 +212,16 @@
20
    _res;                               \
21
 })
22
 
23
+#define spa_loop_control_method_fast_r(o,method,version,...)       \
24
+({                                 \
25
+   int _res;                           \
26
+   struct spa_loop_control *_o = o;                \
27
+   spa_interface_call_fast_res(&_o->iface,             \
28
+           struct spa_loop_control_methods, _res,      \
29
+           method, version, ##__VA_ARGS__);        \
30
+   _res;                               \
31
+})
32
+
33
 #define spa_loop_control_get_fd(l)     spa_loop_control_method_r(l,get_fd,0)
34
 #define spa_loop_control_add_hook(l,...)   spa_loop_control_method_v(l,add_hook,0,__VA_ARGS__)
35
 #define spa_loop_control_enter(l)      spa_loop_control_method_v(l,enter,0)
36
@@ -219,6 +229,8 @@
37
 #define spa_loop_control_iterate(l,...)        spa_loop_control_method_r(l,iterate,0,__VA_ARGS__)
38
 #define spa_loop_control_check(l)      spa_loop_control_method_r(l,check,1)
39
 
40
+#define spa_loop_control_iterate_fast(l,...)   spa_loop_control_method_fast_r(l,iterate,0,__VA_ARGS__)
41
+
42
 typedef void (*spa_source_io_func_t) (void *data, int fd, uint32_t mask);
43
 typedef void (*spa_source_idle_func_t) (void *data);
44
 typedef void (*spa_source_event_func_t) (void *data, uint64_t count);
45
pipewire-0.3.70.tar.gz/spa/include/spa/support/system.h -> pipewire-0.3.71.tar.gz/spa/include/spa/support/system.h Changed
16
 
1
@@ -101,13 +101,12 @@
2
 ({                                 \
3
    volatile int _res = -ENOTSUP;                   \
4
    struct spa_system *_o = o;                  \
5
-   spa_interface_call_res(&_o->iface,              \
6
+   spa_interface_call_fast_res(&_o->iface,             \
7
            struct spa_system_methods, _res,        \
8
            method, version, ##__VA_ARGS__);        \
9
    _res;                               \
10
 })
11
 
12
-
13
 #define spa_system_read(s,...)         spa_system_method_r(s,read,0,__VA_ARGS__)
14
 #define spa_system_write(s,...)            spa_system_method_r(s,write,0,__VA_ARGS__)
15
 #define spa_system_ioctl(s,...)            spa_system_method_r(s,ioctl,0,__VA_ARGS__)
16
pipewire-0.3.70.tar.gz/spa/include/spa/utils/hook.h -> pipewire-0.3.71.tar.gz/spa/include/spa/utils/hook.h Changed
48
 
1
@@ -162,6 +162,14 @@
2
    _res;                                   \
3
 })
4
 
5
+#define spa_callbacks_call_fast(callbacks,type,method,vers,...)            \
6
+({                                     \
7
+   const type *_f = (const type *) (callbacks)->funcs;         \
8
+   _f->method((callbacks)->data, ## __VA_ARGS__);              \
9
+   true;                                   \
10
+})
11
+
12
+
13
 /**
14
  * True if the \a callbacks are of version \a vers, false otherwise
15
  */
16
@@ -194,6 +202,11 @@
17
        res = _f->method((callbacks)->data, ## __VA_ARGS__);        \
18
    res;                                    \
19
 })
20
+#define spa_callbacks_call_fast_res(callbacks,type,res,method,vers,...)        \
21
+({                                     \
22
+   const type *_f = (const type *) (callbacks)->funcs;         \
23
+   res = _f->method((callbacks)->data, ## __VA_ARGS__);            \
24
+})
25
 
26
 /**
27
  * True if the \a iface's callbacks are of version \a vers, false otherwise
28
@@ -216,6 +229,9 @@
29
 #define spa_interface_call(iface,method_type,method,vers,...)          \
30
    spa_callbacks_call(&(iface)->cb,method_type,method,vers,##__VA_ARGS__)
31
 
32
+#define spa_interface_call_fast(iface,method_type,method,vers,...)     \
33
+   spa_callbacks_call_fast(&(iface)->cb,method_type,method,vers,##__VA_ARGS__)
34
+
35
 /**
36
  * Invoke method named \a method in the callbacks on the given interface object.
37
  * The \a method_type defines the type of the method struct, not the interface
38
@@ -226,6 +242,9 @@
39
 #define spa_interface_call_res(iface,method_type,res,method,vers,...)          \
40
    spa_callbacks_call_res(&(iface)->cb,method_type,res,method,vers,##__VA_ARGS__)
41
 
42
+#define spa_interface_call_fast_res(iface,method_type,res,method,vers,...)     \
43
+   spa_callbacks_call_fast_res(&(iface)->cb,method_type,res,method,vers,##__VA_ARGS__)
44
+
45
 /**
46
  * \}
47
  */
48
pipewire-0.3.70.tar.gz/spa/plugins/alsa/alsa-pcm-sink.c -> pipewire-0.3.71.tar.gz/spa/plugins/alsa/alsa-pcm-sink.c Changed
11
 
1
@@ -47,7 +47,8 @@
2
            itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_NODE_MAX_LATENCY, latency);
3
            snprintf(period, sizeof(period), "%lu", this->period_frames);
4
            itemsn_items++ = SPA_DICT_ITEM_INIT("api.alsa.period-size", period);
5
-           snprintf(nperiods, sizeof(nperiods), "%lu", this->buffer_frames / this->period_frames);
6
+           snprintf(nperiods, sizeof(nperiods), "%lu",
7
+                   this->period_frames != 0 ? this->buffer_frames / this->period_frames : 0);
8
            itemsn_items++ = SPA_DICT_ITEM_INIT("api.alsa.period-num", nperiods);
9
            snprintf(headroom, sizeof(headroom), "%u", this->headroom);
10
            itemsn_items++ = SPA_DICT_ITEM_INIT("api.alsa.headroom", headroom);
11
pipewire-0.3.70.tar.gz/spa/plugins/alsa/alsa-pcm-source.c -> pipewire-0.3.71.tar.gz/spa/plugins/alsa/alsa-pcm-source.c Changed
11
 
1
@@ -49,7 +49,8 @@
2
            itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_NODE_MAX_LATENCY, latency);
3
            snprintf(period, sizeof(period), "%lu", this->period_frames);
4
            itemsn_items++ = SPA_DICT_ITEM_INIT("api.alsa.period-size", period);
5
-           snprintf(nperiods, sizeof(nperiods), "%lu", this->buffer_frames / this->period_frames);
6
+           snprintf(nperiods, sizeof(nperiods), "%lu",
7
+                   this->period_frames != 0 ? this->buffer_frames / this->period_frames : 0);
8
            itemsn_items++ = SPA_DICT_ITEM_INIT("api.alsa.period-num", nperiods);
9
            snprintf(headroom, sizeof(headroom), "%u", this->headroom);
10
            itemsn_items++ = SPA_DICT_ITEM_INIT("api.alsa.headroom", headroom);
11
pipewire-0.3.70.tar.gz/spa/plugins/alsa/alsa-pcm.c -> pipewire-0.3.71.tar.gz/spa/plugins/alsa/alsa-pcm.c Changed
22
 
1
@@ -1062,6 +1062,10 @@
2
            }
3
        }
4
    }
5
+   if (j > 1)
6
+       choice->body.type = SPA_CHOICE_Enum;
7
+   spa_pod_builder_pop(b, &f1);
8
+
9
    if (j == 0) {
10
        char buf1024;
11
        int i, r, offs;
12
@@ -1090,9 +1094,6 @@
13
        spa_log_warn(state->log, "%s: access:%s", state->props.device, buf);
14
        return -ENOTSUP;
15
    }
16
-   if (j > 1)
17
-       choice->body.type = SPA_CHOICE_Enum;
18
-   spa_pod_builder_pop(b, &f1);
19
 
20
    if ((res = add_rate(state, 1, 1, false, index & 0xffff, next, 0, params, b)) != 1)
21
        return res;
22
pipewire-0.3.70.tar.gz/spa/plugins/audioconvert/audioadapter.c -> pipewire-0.3.71.tar.gz/spa/plugins/audioconvert/audioadapter.c Changed
135
 
1
@@ -87,6 +87,7 @@
2
    unsigned int add_listener:1;
3
    unsigned int have_format:1;
4
    unsigned int started:1;
5
+   unsigned int ready:1;
6
    unsigned int driver:1;
7
    unsigned int async:1;
8
    unsigned int passthrough:1;
9
@@ -842,14 +843,16 @@
10
            return res;
11
        if ((res = negotiate_buffers(this)) < 0)
12
            return res;
13
-       this->started = true;
14
+       this->ready = true;
15
        break;
16
    case SPA_NODE_COMMAND_Suspend:
17
        this->started = false;
18
+       this->ready = false;
19
        spa_log_debug(this->log, "%p: suspending", this);
20
        break;
21
    case SPA_NODE_COMMAND_Pause:
22
        this->started = false;
23
+       this->ready = false;
24
        spa_log_debug(this->log, "%p: pausing", this);
25
        break;
26
    case SPA_NODE_COMMAND_Flush:
27
@@ -864,20 +867,25 @@
28
        spa_log_error(this->log, "%p: can't send command %d: %s",
29
                this, SPA_NODE_COMMAND_ID(command),
30
                spa_strerror(res));
31
-       return res;
32
    }
33
 
34
-   if (this->target != this->follower) {
35
+   if (res >= 0 && this->target != this->follower) {
36
        if ((res = spa_node_send_command(this->follower, command)) < 0) {
37
            spa_log_error(this->log, "%p: can't send command %d: %s",
38
                    this, SPA_NODE_COMMAND_ID(command),
39
                    spa_strerror(res));
40
-           return res;
41
        }
42
    }
43
    switch (SPA_NODE_COMMAND_ID(command)) {
44
    case SPA_NODE_COMMAND_Start:
45
-       spa_log_debug(this->log, "%p: started", this);
46
+       if (res < 0) {
47
+           spa_log_debug(this->log, "%p: start failed", this);
48
+           this->ready = false;
49
+           configure_format(this, 0, NULL);
50
+       } else {
51
+           this->started = true;
52
+           spa_log_debug(this->log, "%p: started", this);
53
+       }
54
        break;
55
    case SPA_NODE_COMMAND_Suspend:
56
        configure_format(this, 0, NULL);
57
@@ -1211,7 +1219,7 @@
58
 
59
    spa_log_trace_fp(this->log, "%p: ready %d", this, status);
60
 
61
-   if (!this->started) {
62
+   if (!this->ready) {
63
        spa_log_info(this->log, "%p: ready stopped node", this);
64
        return -EIO;
65
    }
66
@@ -1222,12 +1230,12 @@
67
        if (this->direction == SPA_DIRECTION_OUTPUT) {
68
            int retry = 8;
69
            while (retry--) {
70
-               status = spa_node_process(this->convert);
71
+               status = spa_node_process_fast(this->convert);
72
                if (status & SPA_STATUS_HAVE_DATA)
73
                    break;
74
 
75
                if (status & SPA_STATUS_NEED_DATA) {
76
-                   status = spa_node_process(this->follower);
77
+                   status = spa_node_process_fast(this->follower);
78
                    if (!(status & SPA_STATUS_HAVE_DATA))
79
                        break;
80
                }
81
@@ -1472,7 +1480,7 @@
82
    if (this->target == this->follower) {
83
        if (this->io_position)
84
            this->io_rate_match.size = this->io_position->clock.duration;
85
-       return spa_node_process(this->follower);
86
+       return spa_node_process_fast(this->follower);
87
    }
88
 
89
    if (this->direction == SPA_DIRECTION_INPUT) {
90
@@ -1480,7 +1488,7 @@
91
         * First we run the converter to process the input for the follower
92
         * then if it produced data, we run the follower. */
93
        while (retry--) {
94
-           status = spa_node_process(this->convert);
95
+           status = spa_node_process_fast(this->convert);
96
            /* schedule the follower when the converter needed
97
             * a recycled buffer */
98
            if (status == -EPIPE || status == 0)
99
@@ -1491,7 +1499,7 @@
100
            if (status & (SPA_STATUS_HAVE_DATA | SPA_STATUS_DRAINED)) {
101
                /* as long as the converter produced something or
102
                 * is drained, process the follower. */
103
-               fstatus = spa_node_process(this->follower);
104
+               fstatus = spa_node_process_fast(this->follower);
105
                if (fstatus < 0) {
106
                    status = fstatus;
107
                    break;
108
@@ -1512,7 +1520,7 @@
109
            /* output node (source). First run the converter to make
110
             * sure we push out any queued data. Then when it needs
111
             * more data, schedule the follower. */
112
-           status = spa_node_process(this->convert);
113
+           status = spa_node_process_fast(this->convert);
114
            if (status == 0)
115
                status = SPA_STATUS_NEED_DATA;
116
            else if (status < 0)
117
@@ -1529,7 +1537,7 @@
118
            if (status & SPA_STATUS_NEED_DATA) {
119
                /* the converter needs more data, schedule the
120
                 * follower */
121
-               fstatus = spa_node_process(this->follower);
122
+               fstatus = spa_node_process_fast(this->follower);
123
                if (fstatus < 0) {
124
                    status = fstatus;
125
                    break;
126
@@ -1548,7 +1556,7 @@
127
            spa_node_call_xrun(&this->callbacks, 0, 0, NULL);
128
 
129
    } else {
130
-       status = spa_node_process(this->follower);
131
+       status = spa_node_process_fast(this->follower);
132
    }
133
    spa_log_trace_fp(this->log, "%p: process status:%d", this, status);
134
 
135
pipewire-0.3.70.tar.gz/spa/plugins/audioconvert/audioconvert.c -> pipewire-0.3.71.tar.gz/spa/plugins/audioconvert/audioconvert.c Changed
114
 
1
@@ -218,6 +218,7 @@
2
    unsigned int ramp_volume:1;
3
    unsigned int drained:1;
4
    unsigned int rate_adjust:1;
5
+   unsigned int port_ignore_latency:1;
6
 
7
    uint32_t empty_size;
8
    float *empty;
9
@@ -265,7 +266,7 @@
10
    if (full)
11
        port->info.change_mask = port->info_all;
12
    if (port->info.change_mask) {
13
-       struct spa_dict_item items3;
14
+       struct spa_dict_item items4;
15
        uint32_t n_items = 0;
16
 
17
        if (PORT_IS_DSP(this, port->direction, port->id)) {
18
@@ -273,6 +274,8 @@
19
            itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_AUDIO_CHANNEL, port->position);
20
            if (port->is_monitor)
21
                itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_PORT_MONITOR, "true");
22
+           if (this->port_ignore_latency)
23
+               itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_PORT_IGNORE_LATENCY, "true");
24
        } else if (PORT_IS_CONTROL(this, port->direction, port->id)) {
25
            itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_PORT_NAME, "control");
26
            itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_FORMAT_DSP, "8 bit raw midi");
27
@@ -1563,7 +1566,7 @@
28
            out->format.info.raw.channels,
29
            out->format.info.raw.rate);
30
 
31
-   if (this->props.resample_disabled &&
32
+   if (this->props.resample_disabled && !this->resample_peaks &&
33
        in->format.info.raw.rate != out->format.info.raw.rate)
34
        return -EPERM;
35
 
36
@@ -1698,7 +1701,7 @@
37
    if (!in->have_format || !out->have_format)
38
        return -EINVAL;
39
 
40
-   rate = this->io_position ?  this->io_position->clock.rate.denom : DEFAULT_RATE;
41
+   rate = this->io_position ? this->io_position->clock.target_rate.denom : DEFAULT_RATE;
42
 
43
    /* in DSP mode we always convert to the DSP rate */
44
    if (in->mode == SPA_PARAM_PORT_CONFIG_MODE_dsp)
45
@@ -1853,7 +1856,7 @@
46
                SPA_FORMAT_mediaSubtype,   SPA_POD_Id(SPA_MEDIA_SUBTYPE_control));
47
        } else {
48
            uint32_t rate = this->io_position ?
49
-               this->io_position->clock.rate.denom : DEFAULT_RATE;
50
+               this->io_position->clock.target_rate.denom : DEFAULT_RATE;
51
 
52
            *param = spa_pod_builder_add_object(builder,
53
                SPA_TYPE_OBJECT_Format, SPA_PARAM_EnumFormat,
54
@@ -1974,7 +1977,7 @@
55
            /* collect the other port rate */
56
            dir = &this->dirSPA_DIRECTION_REVERSE(direction);
57
            if (dir->mode == SPA_PARAM_PORT_CONFIG_MODE_dsp)
58
-               orate = this->io_position ?  this->io_position->clock.rate.denom : DEFAULT_RATE;
59
+               orate = this->io_position ?  this->io_position->clock.target_rate.denom : DEFAULT_RATE;
60
            else
61
                orate = dir->format.info.raw.rate;
62
 
63
@@ -2672,11 +2675,21 @@
64
        }
65
    }
66
 
67
+   resample_passthrough = resample_is_passthrough(this);
68
+
69
    /* calculate how many samples we are going to produce. */
70
    if (this->direction == SPA_DIRECTION_INPUT) {
71
        /* in split mode we need to output exactly the size of the
72
         * duration so we don't try to flush early */
73
        max_out = quant_samples;
74
+       if (!in_avail || this->drained) {
75
+           n_out = max_out - SPA_MIN(max_out, this->out_offset);
76
+           /* no input, ask for more, update rate-match first */
77
+           resample_update_rate_match(this, resample_passthrough, n_out, 0);
78
+           spa_log_trace_fp(this->log, "%p: no input drained:%d", this, this->drained);
79
+           res |= this->drained ? SPA_STATUS_DRAINED : SPA_STATUS_NEED_DATA;
80
+           return res;
81
+       }
82
        flush_out = false;
83
    } else {
84
        /* in merge mode we consume one duration of samples and
85
@@ -2777,18 +2790,9 @@
86
    /* we only need to output the remaining samples */
87
    n_out = max_out - SPA_MIN(max_out, this->out_offset);
88
 
89
-   resample_passthrough = resample_is_passthrough(this);
90
-
91
    /* calculate how many samples we are going to consume. */
92
    if (this->direction == SPA_DIRECTION_INPUT) {
93
-       if (!in_avail || this->drained) {
94
-           /* no input, ask for more, update rate-match first */
95
-           resample_update_rate_match(this, resample_passthrough, n_out, 0);
96
-           spa_log_trace_fp(this->log, "%p: no input drained:%d", this, this->drained);
97
-           res |= this->drained ? SPA_STATUS_DRAINED : SPA_STATUS_NEED_DATA;
98
-           return res;
99
-       }
100
-       /* else figure out how much input samples we need to consume */
101
+       /* figure out how much input samples we need to consume */
102
        n_samples = SPA_MIN(n_samples,
103
                resample_get_in_size(this, resample_passthrough, n_out));
104
    } else {
105
@@ -3144,6 +3148,8 @@
106
            if (s != NULL)
107
                            this->props.n_channels = parse_position(this->props.channel_map, s, strlen(s));
108
        }
109
+       else if (spa_streq(k, SPA_KEY_PORT_IGNORE_LATENCY))
110
+           this->port_ignore_latency = spa_atob(s);
111
        else
112
            audioconvert_set_param(this, k, s);
113
    }
114
pipewire-0.3.70.tar.gz/spa/plugins/audiomixer/audiomixer.c -> pipewire-0.3.71.tar.gz/spa/plugins/audiomixer/audiomixer.c Changed
33
 
1
@@ -29,7 +29,7 @@
2
 #define DEFAULT_CHANNELS   2
3
 
4
 #define MAX_BUFFERS     64
5
-#define MAX_PORTS       128
6
+#define MAX_PORTS       512
7
 #define MAX_CHANNELS    64
8
 #define MAX_ALIGN  MIX_OPS_MAX_ALIGN
9
 
10
@@ -103,6 +103,9 @@
11
    struct port *in_portsMAX_PORTS;
12
    struct port out_ports1;
13
 
14
+   struct buffer *mix_buffersMAX_PORTS;
15
+   const void *mix_datasMAX_PORTS;
16
+
17
    int n_formats;
18
    struct spa_audio_info format;
19
 
20
@@ -737,9 +740,9 @@
21
        outio->buffer_id = SPA_ID_INVALID;
22
    }
23
 
24
-   buffers = alloca(MAX_PORTS * sizeof(struct buffer *));
25
-        datas = alloca(MAX_PORTS * sizeof(void *));
26
-        n_buffers = 0;
27
+   buffers = this->mix_buffers;
28
+   datas = this->mix_datas;
29
+   n_buffers = 0;
30
 
31
    maxsize = UINT32_MAX;
32
 
33
pipewire-0.3.70.tar.gz/spa/plugins/audiomixer/mixer-dsp.c -> pipewire-0.3.71.tar.gz/spa/plugins/audiomixer/mixer-dsp.c Changed
31
 
1
@@ -26,7 +26,7 @@
2
 static struct spa_log_topic *log_topic = &SPA_LOG_TOPIC(0, "spa.mixer-dsp");
3
 
4
 #define MAX_BUFFERS    64
5
-#define MAX_PORTS  128
6
+#define MAX_PORTS  512
7
 #define MAX_ALIGN  MIX_OPS_MAX_ALIGN
8
 
9
 #define PORT_DEFAULT_VOLUME    1.0
10
@@ -100,6 +100,9 @@
11
    struct port *in_portsMAX_PORTS;
12
    struct port out_ports1;
13
 
14
+   struct buffer *mix_buffersMAX_PORTS;
15
+   const void *mix_datasMAX_PORTS;
16
+
17
    int n_formats;
18
    struct spa_audio_info format;
19
    uint32_t stride;
20
@@ -673,8 +676,8 @@
21
        outio->buffer_id = SPA_ID_INVALID;
22
    }
23
 
24
-   buffers = alloca(MAX_PORTS * sizeof(struct buffer *));
25
-   datas = alloca(MAX_PORTS * sizeof(void *));
26
+   buffers = this->mix_buffers;
27
+   datas = this->mix_datas;
28
    n_buffers = 0;
29
 
30
    maxsize = UINT32_MAX;
31
pipewire-0.3.70.tar.gz/spa/plugins/bluez5/a2dp-codec-aac.c -> pipewire-0.3.71.tar.gz/spa/plugins/bluez5/a2dp-codec-aac.c Changed
16
 
1
@@ -193,12 +193,12 @@
2
            spa_pod_builder_int(b, f->value);
3
        }
4
    }
5
-   if (i == 0)
6
-       return -EINVAL;
7
    if (i > 1)
8
        choice->body.type = SPA_CHOICE_Enum;
9
    spa_pod_builder_pop(b, &f1);
10
 
11
+   if (i == 0)
12
+       return -EINVAL;
13
 
14
    if (SPA_FLAG_IS_SET(conf.channels, AAC_CHANNELS_1 | AAC_CHANNELS_2)) {
15
        spa_pod_builder_add(b,
16
pipewire-0.3.70.tar.gz/spa/plugins/bluez5/a2dp-codec-aptx.c -> pipewire-0.3.71.tar.gz/spa/plugins/bluez5/a2dp-codec-aptx.c Changed
17
 
1
@@ -247,12 +247,13 @@
2
            spa_pod_builder_int(b, 16000);
3
        spa_pod_builder_int(b, 16000);
4
    }
5
-   if (i == 0)
6
-       return -EINVAL;
7
    if (i > 1)
8
        choice->body.type = SPA_CHOICE_Enum;
9
    spa_pod_builder_pop(b, &f1);
10
 
11
+   if (i == 0)
12
+       return -EINVAL;
13
+
14
    if (SPA_FLAG_IS_SET(conf.channel_mode, APTX_CHANNEL_MODE_MONO | APTX_CHANNEL_MODE_STEREO)) {
15
        spa_pod_builder_add(b,
16
                SPA_FORMAT_AUDIO_channels, SPA_POD_CHOICE_RANGE_Int(2, 1, 2),
17
pipewire-0.3.70.tar.gz/spa/plugins/bluez5/a2dp-codec-faststream.c -> pipewire-0.3.71.tar.gz/spa/plugins/bluez5/a2dp-codec-faststream.c Changed
15
 
1
@@ -148,11 +148,11 @@
2
            spa_pod_builder_int(b, 44100);
3
        spa_pod_builder_int(b, 44100);
4
    }
5
-   if (i == 0)
6
-       return -EINVAL;
7
    if (i > 1)
8
        choice->body.type = SPA_CHOICE_Enum;
9
    spa_pod_builder_pop(b, &f1);
10
+   if (i == 0)
11
+       return -EINVAL;
12
 
13
    position0 = SPA_AUDIO_CHANNEL_FL;
14
    position1 = SPA_AUDIO_CHANNEL_FR;
15
pipewire-0.3.70.tar.gz/spa/plugins/bluez5/a2dp-codec-ldac.c -> pipewire-0.3.71.tar.gz/spa/plugins/bluez5/a2dp-codec-ldac.c Changed
17
 
1
@@ -184,12 +184,13 @@
2
            spa_pod_builder_int(b, 96000);
3
        spa_pod_builder_int(b, 96000);
4
    }
5
-   if (i == 0)
6
-       return -EINVAL;
7
    if (i > 1)
8
        choice->body.type = SPA_CHOICE_Enum;
9
    spa_pod_builder_pop(b, &f1);
10
 
11
+   if (i == 0)
12
+       return -EINVAL;
13
+
14
    if (conf.channel_mode & LDACBT_CHANNEL_MODE_MONO &&
15
        conf.channel_mode & (LDACBT_CHANNEL_MODE_STEREO |
16
            LDACBT_CHANNEL_MODE_DUAL_CHANNEL)) {
17
pipewire-0.3.70.tar.gz/spa/plugins/bluez5/backend-hsphfpd.c -> pipewire-0.3.71.tar.gz/spa/plugins/bluez5/backend-hsphfpd.c Changed
30
 
1
@@ -495,7 +495,6 @@
2
 {
3
    const char *interface;
4
    DBusMessageIter iter, array, dict, data;
5
-   const char *agent_codec_key = "AgentCodec";
6
    const char *agent_codec;
7
    DBusMessage *r = NULL;
8
 
9
@@ -527,9 +526,9 @@
10
        return DBUS_HANDLER_RESULT_NEED_MEMORY;
11
    dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "{sv}", &array);
12
    dbus_message_iter_open_container(&array, DBUS_TYPE_DICT_ENTRY, NULL, &dict);
13
-   dbus_message_iter_append_basic(&dict, DBUS_TYPE_STRING, &agent_codec_key);
14
+   dbus_message_iter_append_basic(&dict, DBUS_TYPE_STRING, &(const char *){ "AgentCodec" });
15
    dbus_message_iter_open_container(&dict, DBUS_TYPE_VARIANT, "s", &data);
16
-   dbus_message_iter_append_basic(&data, DBUS_TYPE_BOOLEAN, &agent_codec);
17
+   dbus_message_iter_append_basic(&data, DBUS_TYPE_STRING, &agent_codec);
18
    dbus_message_iter_close_container(&dict, &data);
19
    dbus_message_iter_close_container(&array, &dict);
20
    dbus_message_iter_close_container(&iter, &array);
21
@@ -1036,7 +1035,7 @@
22
 
23
            case DBUS_TYPE_BOOLEAN:
24
                {
25
-                   bool value;
26
+                   dbus_bool_t value;
27
                    dbus_message_iter_get_basic(&value_i, &value);
28
                    if (spa_streq(key, "Connected"))
29
                        endpoint->connected = value;
30
pipewire-0.3.70.tar.gz/spa/plugins/bluez5/backend-native.c -> pipewire-0.3.71.tar.gz/spa/plugins/bluez5/backend-native.c Changed
56
 
1
@@ -684,6 +684,21 @@
2
 
3
 static int codec_switch_start_timer(struct rfcomm *rfcomm, int timeout_msec);
4
 
5
+static void process_xevent_indicator(struct rfcomm *rfcomm, unsigned int level, unsigned int nlevels)
6
+{
7
+   struct impl *backend = rfcomm->backend;
8
+   uint8_t perc;
9
+
10
+   spa_log_debug(backend->log, "AT+XEVENT level:%u nlevels:%u", level, nlevels);
11
+
12
+   if (nlevels <= 1)
13
+       return;
14
+
15
+   /* 0 <= level < nlevels */
16
+   perc = SPA_MIN(level, nlevels - 1) * 100 / (nlevels - 1);
17
+   spa_bt_device_report_battery_level(rfcomm->device, perc);
18
+}
19
+
20
 static void process_iphoneaccev_indicator(struct rfcomm *rfcomm, unsigned int key, unsigned int value)
21
 {
22
    struct impl *backend = rfcomm->backend;
23
@@ -760,6 +775,8 @@
24
    unsigned int indicator;
25
    unsigned int indicator_value;
26
    unsigned int value;
27
+   unsigned int xevent_level;
28
+   unsigned int xevent_nlevels;
29
    int xapl_vendor;
30
    int xapl_product;
31
    int xapl_features;
32
@@ -1069,6 +1086,14 @@
33
            rfcomm_send_reply(rfcomm, "+XAPL=iPhone,%u", SPA_BT_HFP_HF_XAPL_FEATURE_BATTERY_REPORTING);
34
        }
35
        rfcomm_send_reply(rfcomm, "OK");
36
+   } else if (spa_strstartswith(buf, "AT+XEVENT=USER-AGENT")) {
37
+       rfcomm_send_reply(rfcomm, "OK");
38
+   } else if (sscanf(buf, "AT+XEVENT=BATTERY,%u,%u,%*u,%*u", &xevent_level, &xevent_nlevels) == 2) {
39
+       process_xevent_indicator(rfcomm, xevent_level, xevent_nlevels);
40
+       rfcomm_send_reply(rfcomm, "OK");
41
+   } else if (sscanf(buf, "AT+XEVENT=BATTERY,%u", &xevent_level) == 1) {
42
+       process_xevent_indicator(rfcomm, xevent_level + 1, 11);
43
+       rfcomm_send_reply(rfcomm, "OK");
44
    } else if (sscanf(buf, "AT+IPHONEACCEV=%u%n", &count, &r) == 1) {
45
        if (count < 1 || count > 100)
46
            return false;
47
@@ -2769,7 +2794,7 @@
48
    sco_close(backend);
49
 
50
    if (backend->modemmanager) {
51
-       mm_unregister(backend);
52
+       mm_unregister(backend->modemmanager);
53
        backend->modemmanager = NULL;
54
    }
55
 
56
pipewire-0.3.70.tar.gz/spa/plugins/bluez5/bap-codec-lc3.c -> pipewire-0.3.71.tar.gz/spa/plugins/bluez5/bap-codec-lc3.c Changed
194
 
1
@@ -13,6 +13,7 @@
2
 
3
 #include <spa/param/audio/format.h>
4
 #include <spa/param/audio/format-utils.h>
5
+#include <spa/utils/string.h>
6
 
7
 #include <lc3.h>
8
 
9
@@ -37,6 +38,7 @@
10
 struct pac_data {
11
    const uint8_t *data;
12
    size_t size;
13
+   uint32_t locations;
14
 };
15
 
16
 typedef struct {
17
@@ -47,6 +49,40 @@
18
    uint8_t n_blks;
19
 } bap_lc3_t;
20
 
21
+static const struct {
22
+   uint32_t bit;
23
+   enum spa_audio_channel channel;
24
+} channel_bits = {
25
+   { LC3_CONFIG_CHNL_FL,   SPA_AUDIO_CHANNEL_FL },
26
+   { LC3_CONFIG_CHNL_FR,   SPA_AUDIO_CHANNEL_FR },
27
+   { LC3_CONFIG_CHNL_FC,   SPA_AUDIO_CHANNEL_FC },
28
+   { LC3_CONFIG_CHNL_LFE,  SPA_AUDIO_CHANNEL_LFE },
29
+   { LC3_CONFIG_CHNL_BL,   SPA_AUDIO_CHANNEL_RL },
30
+   { LC3_CONFIG_CHNL_BR,   SPA_AUDIO_CHANNEL_RR },
31
+   { LC3_CONFIG_CHNL_FLC,  SPA_AUDIO_CHANNEL_FLC },
32
+   { LC3_CONFIG_CHNL_FRC,  SPA_AUDIO_CHANNEL_FRC },
33
+   { LC3_CONFIG_CHNL_BC,   SPA_AUDIO_CHANNEL_BC },
34
+   { LC3_CONFIG_CHNL_LFE2, SPA_AUDIO_CHANNEL_LFE2 },
35
+   { LC3_CONFIG_CHNL_SL,   SPA_AUDIO_CHANNEL_SL },
36
+   { LC3_CONFIG_CHNL_SR,   SPA_AUDIO_CHANNEL_SR },
37
+   { LC3_CONFIG_CHNL_TFL,  SPA_AUDIO_CHANNEL_TFL },
38
+   { LC3_CONFIG_CHNL_TFR,  SPA_AUDIO_CHANNEL_TFR },
39
+   { LC3_CONFIG_CHNL_TFC,  SPA_AUDIO_CHANNEL_TFC },
40
+   { LC3_CONFIG_CHNL_TC,   SPA_AUDIO_CHANNEL_TC },
41
+   { LC3_CONFIG_CHNL_TBL,  SPA_AUDIO_CHANNEL_TRL },
42
+   { LC3_CONFIG_CHNL_TBR,  SPA_AUDIO_CHANNEL_TRR },
43
+   { LC3_CONFIG_CHNL_TSL,  SPA_AUDIO_CHANNEL_TSL },
44
+   { LC3_CONFIG_CHNL_TSR,  SPA_AUDIO_CHANNEL_TSR },
45
+   { LC3_CONFIG_CHNL_TBC,  SPA_AUDIO_CHANNEL_TRC },
46
+   { LC3_CONFIG_CHNL_BFC,  SPA_AUDIO_CHANNEL_BC },
47
+   { LC3_CONFIG_CHNL_BFL,  SPA_AUDIO_CHANNEL_BLC },
48
+   { LC3_CONFIG_CHNL_BFR,  SPA_AUDIO_CHANNEL_BRC },
49
+   { LC3_CONFIG_CHNL_FLW,  SPA_AUDIO_CHANNEL_FLW },
50
+   { LC3_CONFIG_CHNL_FRW,  SPA_AUDIO_CHANNEL_FRW },
51
+   { LC3_CONFIG_CHNL_LS,   SPA_AUDIO_CHANNEL_SL }, /* is it the right mapping? */
52
+   { LC3_CONFIG_CHNL_RS,   SPA_AUDIO_CHANNEL_SR }, /* is it the right mapping? */
53
+};
54
+
55
 static int write_ltv(uint8_t *dest, uint8_t type, void* value, size_t len)
56
 {
57
    struct ltv *ltv = (struct ltv *)dest;
58
@@ -136,6 +172,36 @@
59
    return num;
60
 }
61
 
62
+static int select_channels(uint8_t channels, uint32_t locations, uint32_t *mapping)
63
+{
64
+   unsigned int i, num;
65
+
66
+   if (channels & LC3_CHAN_2)
67
+       num = 2;
68
+   else if (channels & LC3_CHAN_1)
69
+       num = 1;
70
+   else
71
+       return -1;
72
+
73
+   if (!locations) {
74
+       *mapping = 0;  /* mono (omit Audio_Channel_Allocation) */
75
+       return 0;
76
+   }
77
+
78
+   /* XXX: select some channels, but upper level should tell us what */
79
+   *mapping = 0;
80
+   for (i = 0; i < SPA_N_ELEMENTS(channel_bits); ++i) {
81
+       if (locations & channel_bitsi.bit) {
82
+           *mapping |= channel_bitsi.bit;
83
+           --num;
84
+           if (num == 0)
85
+               break;
86
+       }
87
+   }
88
+
89
+   return 0;
90
+}
91
+
92
 static bool select_config(bap_lc3_t *conf, const struct pac_data *pac)
93
 {
94
    const uint8_t *data = pac->data;
95
@@ -191,12 +257,8 @@
96
            spa_return_val_if_fail(ltv->len == 2, false);
97
            {
98
                uint8_t channels = ltv->value0;
99
-               /* XXX: we hardcode mono or stereo stream */
100
-               if (channels & LC3_CHAN_2)
101
-                   conf->channels = LC3_CONFIG_CHNL_FR | LC3_CONFIG_CHNL_FL;
102
-               else if (channels & LC3_CHAN_1)
103
-                   conf->channels = 0;   /* mono (omit Audio_Channel_Allocation) */
104
-               else
105
+
106
+               if (select_channels(channels, pac->locations, &conf->channels) < 0)
107
                    return false;
108
            }
109
            break;
110
@@ -369,10 +431,18 @@
111
    int npacs;
112
    bap_lc3_t conf;
113
    uint8_t *data = config;
114
+   uint32_t locations = 0;
115
+   int i;
116
 
117
    if (caps == NULL)
118
        return -EINVAL;
119
 
120
+   if (settings) {
121
+       for (i = 0; i < (int)settings->n_items; ++i)
122
+           if (spa_streq(settings->itemsi.key, "bluez5.bap.locations"))
123
+               sscanf(settings->itemsi.value, "%"PRIu32, &locations);
124
+   }
125
+
126
    /* Select best conf from those possible */
127
    npacs = parse_bluez_pacs(caps, caps_size, pacs);
128
    if (npacs < 0)
129
@@ -380,6 +450,9 @@
130
    else if (npacs == 0)
131
        return -EINVAL;
132
 
133
+   for (i = 0; i < npacs; ++i)
134
+       pacsi.locations = locations;
135
+
136
    qsort(pacs, npacs, sizeof(struct pac_data), pac_cmp);
137
 
138
    if (!select_config(&conf, &pacs0))
139
@@ -405,8 +478,8 @@
140
    int res1, res2;
141
 
142
    /* Order selected configurations by preference */
143
-   res1 = codec->select_config(codec, 0, caps1, caps1_size, info, NULL, (uint8_t *)&conf1);
144
-   res2 = codec->select_config(codec, 0, caps2, caps2_size, info , NULL, (uint8_t *)&conf2);
145
+   res1 = codec->select_config(codec, 0, caps1, caps1_size, info, global_settings, (uint8_t *)&conf1);
146
+   res2 = codec->select_config(codec, 0, caps2, caps2_size, info, global_settings, (uint8_t *)&conf2);
147
 
148
    return conf_cmp(&conf1, res1, &conf2, res2);
149
 }
150
@@ -422,38 +495,11 @@
151
        position0 = SPA_AUDIO_CHANNEL_MONO;
152
        n_positions = 1;
153
    } else {
154
-#define CHANNEL_2_SPACHANNEL(channel,spa_channel)  if (channels & channel) positionn_positions++ = spa_channel;
155
-
156
-       CHANNEL_2_SPACHANNEL(LC3_CONFIG_CHNL_FL,   SPA_AUDIO_CHANNEL_FL);
157
-       CHANNEL_2_SPACHANNEL(LC3_CONFIG_CHNL_FR,   SPA_AUDIO_CHANNEL_FR);
158
-       CHANNEL_2_SPACHANNEL(LC3_CONFIG_CHNL_FC,   SPA_AUDIO_CHANNEL_FC);
159
-       CHANNEL_2_SPACHANNEL(LC3_CONFIG_CHNL_LFE,  SPA_AUDIO_CHANNEL_LFE);
160
-       CHANNEL_2_SPACHANNEL(LC3_CONFIG_CHNL_BL,   SPA_AUDIO_CHANNEL_RL);
161
-       CHANNEL_2_SPACHANNEL(LC3_CONFIG_CHNL_BR,   SPA_AUDIO_CHANNEL_RR);
162
-       CHANNEL_2_SPACHANNEL(LC3_CONFIG_CHNL_FLC,  SPA_AUDIO_CHANNEL_FLC);
163
-       CHANNEL_2_SPACHANNEL(LC3_CONFIG_CHNL_FRC,  SPA_AUDIO_CHANNEL_FRC);
164
-       CHANNEL_2_SPACHANNEL(LC3_CONFIG_CHNL_BC,   SPA_AUDIO_CHANNEL_BC);
165
-       CHANNEL_2_SPACHANNEL(LC3_CONFIG_CHNL_LFE2, SPA_AUDIO_CHANNEL_LFE2);
166
-       CHANNEL_2_SPACHANNEL(LC3_CONFIG_CHNL_SL,   SPA_AUDIO_CHANNEL_SL);
167
-       CHANNEL_2_SPACHANNEL(LC3_CONFIG_CHNL_SR,   SPA_AUDIO_CHANNEL_SR);
168
-       CHANNEL_2_SPACHANNEL(LC3_CONFIG_CHNL_TFL,  SPA_AUDIO_CHANNEL_TFL);
169
-       CHANNEL_2_SPACHANNEL(LC3_CONFIG_CHNL_TFR,  SPA_AUDIO_CHANNEL_TFR);
170
-       CHANNEL_2_SPACHANNEL(LC3_CONFIG_CHNL_TFC,  SPA_AUDIO_CHANNEL_TFC);
171
-       CHANNEL_2_SPACHANNEL(LC3_CONFIG_CHNL_TC,   SPA_AUDIO_CHANNEL_TC);
172
-       CHANNEL_2_SPACHANNEL(LC3_CONFIG_CHNL_TBL,  SPA_AUDIO_CHANNEL_TRL);
173
-       CHANNEL_2_SPACHANNEL(LC3_CONFIG_CHNL_TBR,  SPA_AUDIO_CHANNEL_TRR);
174
-       CHANNEL_2_SPACHANNEL(LC3_CONFIG_CHNL_TSL,  SPA_AUDIO_CHANNEL_TSL);
175
-       CHANNEL_2_SPACHANNEL(LC3_CONFIG_CHNL_TSR,  SPA_AUDIO_CHANNEL_TSR);
176
-       CHANNEL_2_SPACHANNEL(LC3_CONFIG_CHNL_TBC,  SPA_AUDIO_CHANNEL_TRC);
177
-       CHANNEL_2_SPACHANNEL(LC3_CONFIG_CHNL_BFC,  SPA_AUDIO_CHANNEL_BC);
178
-       CHANNEL_2_SPACHANNEL(LC3_CONFIG_CHNL_BFL,  SPA_AUDIO_CHANNEL_BLC);
179
-       CHANNEL_2_SPACHANNEL(LC3_CONFIG_CHNL_BFR,  SPA_AUDIO_CHANNEL_BRC);
180
-       CHANNEL_2_SPACHANNEL(LC3_CONFIG_CHNL_FLW,  SPA_AUDIO_CHANNEL_FLW);
181
-       CHANNEL_2_SPACHANNEL(LC3_CONFIG_CHNL_FRW,  SPA_AUDIO_CHANNEL_FRW);
182
-       CHANNEL_2_SPACHANNEL(LC3_CONFIG_CHNL_LS,   SPA_AUDIO_CHANNEL_LLFE); /* is it the right mapping? */
183
-       CHANNEL_2_SPACHANNEL(LC3_CONFIG_CHNL_RS,   SPA_AUDIO_CHANNEL_RLFE); /* is it the right mapping? */
184
-
185
-#undef CHANNEL_2_SPACHANNEL
186
+       unsigned int i;
187
+
188
+       for (i = 0; i < SPA_N_ELEMENTS(channel_bits); ++i)
189
+           if (channels & channel_bitsi.bit)
190
+               positionn_positions++ = channel_bitsi.channel;
191
    }
192
 
193
    if (n_positions != n_channels)
194
pipewire-0.3.70.tar.gz/spa/plugins/bluez5/bluez-hardware.conf -> pipewire-0.3.71.tar.gz/spa/plugins/bluez5/bluez-hardware.conf Changed
9
 
1
@@ -44,6 +44,7 @@
2
     { name = "SoundCore 2", no-features =  sbc-xq  },  # #pipewire-2291
3
     { name = "Tribit MAXSound Plus", no-features =  hw-volume  },  # #pipewire-1592
4
     { name = "Urbanista Stockholm Plus", no-features =  msbc-alt1, msbc-alt1-rtl  },
5
+    { name = "WorkTunes Connect", no-features =  hw-volume  },  # 3M WorkTunes Connect
6
 
7
     { address = "~^44:5e:cd:", no-features =  faststream, a2dp-duplex },  # #pipewire-1756
8
 
9
pipewire-0.3.70.tar.gz/spa/plugins/bluez5/bluez5-dbus.c -> pipewire-0.3.71.tar.gz/spa/plugins/bluez5/bluez5-dbus.c Changed
201
 
1
@@ -39,8 +39,6 @@
2
 #include "iso-io.h"
3
 #include "defs.h"
4
 
5
-#include "bap-codec-caps.h"
6
-
7
 static struct spa_log_topic log_topic = SPA_LOG_TOPIC(0, "spa.bluez5");
8
 #undef SPA_LOG_TOPIC_DEFAULT
9
 #define SPA_LOG_TOPIC_DEFAULT &log_topic
10
@@ -632,10 +630,14 @@
11
    const struct media_codec *codec;
12
    bool sink;
13
    const char *err_msg = "Unknown error";
14
+   struct spa_dict settings;
15
+   struct spa_dict_item setting_itemsSPA_N_ELEMENTS(monitor->global_setting_items) + 1;
16
+   int i;
17
 
18
    const char *endpoint_path = NULL;
19
    uint8_t capsA2DP_MAX_CAPS_SIZE;
20
    uint8_t configA2DP_MAX_CAPS_SIZE;
21
+   char locations64 = {0};
22
    int caps_size = 0;
23
    int conf_size;
24
    DBusMessageIter dict;
25
@@ -751,6 +753,8 @@
26
                endpoint_qos.preferred_delay_min = v;
27
            else if (spa_streq(key, "PreferredMaximumDelay"))
28
                endpoint_qos.preferred_delay_max = v;
29
+           else if (spa_streq(key, "Location"))
30
+               spa_scnprintf(locations, sizeof(locations), "%"PRIu32, v);
31
            else
32
                spa_log_info(monitor->log, "Unknown property %s", key);
33
        } else {
34
@@ -775,10 +779,12 @@
35
        ep->acceptor = true;
36
    }
37
 
38
-   /* TODO: determine which device the SelectConfiguration() call is associated
39
-    * with; it's known here based on the remote endpoint.
40
-    */
41
-   conf_size = codec->select_config(codec, 0, caps, caps_size, &monitor->default_audio_info, NULL, config);
42
+   for (i = 0; i < (int)monitor->global_settings.n_items; ++i)
43
+       setting_itemsi = monitor->global_settings.itemsi;
44
+   setting_itemsi = SPA_DICT_ITEM_INIT("bluez5.bap.locations", locations);
45
+   settings = SPA_DICT_INIT(setting_items, monitor->global_settings.n_items + 1);
46
+
47
+   conf_size = codec->select_config(codec, 0, caps, caps_size, &monitor->default_audio_info, &settings, config);
48
    if (conf_size < 0) {
49
        spa_log_error(monitor->log, "can't select config: %d (%s)",
50
                conf_size, spa_strerror(conf_size));
51
@@ -2626,9 +2632,16 @@
52
 
53
    if (transport->acquire_call) {
54
        dbus_pending_call_cancel(transport->acquire_call);
55
+       dbus_pending_call_unref(transport->acquire_call);
56
        transport->acquire_call = NULL;
57
    }
58
 
59
+   if (transport->volume_call) {
60
+       dbus_pending_call_cancel(transport->volume_call);
61
+       dbus_pending_call_unref(transport->volume_call);
62
+       transport->volume_call = NULL;
63
+   }
64
+
65
    if (transport->fd >= 0) {
66
        spa_bt_player_set_state(transport->device->adapter->dummy_player, SPA_BT_PLAYER_STOPPED);
67
 
68
@@ -3189,38 +3202,62 @@
69
            else
70
                transport->bap_cis = value;
71
        }
72
-       else if (spa_streq(key, "Location")) {
73
-           uint32_t value;
74
-
75
-           if (type != DBUS_TYPE_UINT32)
76
-               goto next;
77
-           dbus_message_iter_get_basic(&it1, &value);
78
-
79
-           spa_log_debug(monitor->log, "transport %p: %s=%d", transport, key, (int)value);
80
-           transport->bap_location = value;
81
-       }
82
 next:
83
        dbus_message_iter_next(props_iter);
84
    }
85
    return 0;
86
 }
87
 
88
-static int transport_set_property_volume(struct spa_bt_transport *transport, uint16_t value)
89
+static void transport_set_property_volume_reply(DBusPendingCall *pending, void *user_data)
90
+{
91
+   struct spa_bt_transport *transport = user_data;
92
+   struct spa_bt_monitor *monitor = transport->monitor;
93
+   DBusError err = DBUS_ERROR_INIT;
94
+   DBusMessage *r;
95
+
96
+   r = dbus_pending_call_steal_reply(pending);
97
+
98
+   spa_assert(transport->volume_call == pending);
99
+   dbus_pending_call_unref(pending);
100
+   transport->volume_call = NULL;
101
+
102
+   if (dbus_set_error_from_message(&err, r)) {
103
+       spa_log_info(monitor->log, "transport %p: set volume failed for transport %s: %s",
104
+               transport, transport->path, err.message);
105
+       dbus_error_free(&err);
106
+   } else {
107
+       spa_log_debug(monitor->log, "transport %p: set volume complete",
108
+               transport);
109
+   }
110
+
111
+   dbus_message_unref(r);
112
+}
113
+
114
+static void transport_set_property_volume(struct spa_bt_transport *transport, uint16_t value)
115
 {
116
    struct spa_bt_monitor *monitor = transport->monitor;
117
-   DBusMessage *m, *r;
118
+   DBusMessage *m;
119
    DBusMessageIter it2;
120
    DBusError err;
121
    const char *interface = BLUEZ_MEDIA_TRANSPORT_INTERFACE;
122
    const char *name = "Volume";
123
    int res = 0;
124
+   dbus_bool_t ret;
125
+
126
+   if (transport->volume_call) {
127
+       dbus_pending_call_cancel(transport->volume_call);
128
+       dbus_pending_call_unref(transport->volume_call);
129
+       transport->volume_call = NULL;
130
+   }
131
 
132
    m = dbus_message_new_method_call(BLUEZ_SERVICE,
133
                     transport->path,
134
                                     DBUS_INTERFACE_PROPERTIES,
135
                     "Set");
136
-   if (m == NULL)
137
-       return -ENOMEM;
138
+   if (m == NULL) {
139
+       res = -ENOMEM;
140
+       goto fail;
141
+   }
142
 
143
    dbus_message_iter_init_append(m, &it0);
144
    dbus_message_iter_append_basic(&it0, DBUS_TYPE_STRING, &interface);
145
@@ -3232,25 +3269,27 @@
146
 
147
    dbus_error_init(&err);
148
 
149
-   r = dbus_connection_send_with_reply_and_block(monitor->conn, m, -1, &err);
150
-
151
+   ret = dbus_connection_send_with_reply(monitor->conn, m, &transport->volume_call, -1);
152
    dbus_message_unref(m);
153
 
154
-   if (r == NULL) {
155
-       spa_log_error(monitor->log, "set volume %u failed for transport %s (%s)",
156
-               value, transport->path, err.message);
157
-       dbus_error_free(&err);
158
-       return -EIO;
159
+   if (!ret || !transport->volume_call) {
160
+       res = -EIO;
161
+       goto fail;
162
    }
163
 
164
-   if (dbus_message_get_type(r) == DBUS_MESSAGE_TYPE_ERROR)
165
+   ret = dbus_pending_call_set_notify(transport->volume_call,
166
+           transport_set_property_volume_reply, transport, NULL);
167
+   if (!ret) {
168
        res = -EIO;
169
+       goto fail;
170
+   }
171
 
172
-   dbus_message_unref(r);
173
-
174
-   spa_log_debug(monitor->log, "transport %p: set volume to %d", transport, value);
175
+   spa_log_debug(monitor->log, "transport %p: setting volume to %d", transport, value);
176
+   return;
177
 
178
-   return res;
179
+fail:
180
+   spa_log_debug(monitor->log, "transport %p: failed to set volume %d: %s",
181
+           transport, value, spa_strerror(res));
182
 }
183
 
184
 static int transport_set_volume(void *data, int id, float volume)
185
@@ -3555,6 +3594,7 @@
186
 
187
    if (transport->acquire_call) {
188
        dbus_pending_call_cancel(transport->acquire_call);
189
+       dbus_pending_call_unref(transport->acquire_call);
190
        transport->acquire_call = NULL;
191
    }
192
 
193
@@ -4187,64 +4227,6 @@
194
    return spa_bt_backend_supports_codec(monitor->backend, device, codec);
195
 }
196
 
197
-static void bap_update_codec_location(struct spa_bt_transport *t)
198
-{
199
-   uint8_t *data = t->configuration;
200
-   size_t size = t->configuration_len;
201
pipewire-0.3.70.tar.gz/spa/plugins/bluez5/dbus-monitor.h -> pipewire-0.3.71.tar.gz/spa/plugins/bluez5/dbus-monitor.h Changed
7
 
1
@@ -61,4 +61,4 @@
2
 
3
 void dbus_monitor_clear(struct dbus_monitor *monitor);
4
 
5
-#endif DBUS_MONITOR_H_
6
+#endif // DBUS_MONITOR_H_
7
pipewire-0.3.70.tar.gz/spa/plugins/bluez5/defs.h -> pipewire-0.3.71.tar.gz/spa/plugins/bluez5/defs.h Changed
17
 
1
@@ -630,7 +630,6 @@
2
    unsigned int latency_us;
3
    uint8_t bap_cig;
4
    uint8_t bap_cis;
5
-   uint32_t bap_location;
6
    uint32_t bap_interval;
7
 
8
    struct spa_bt_iso_io *iso_io;
9
@@ -639,6 +638,7 @@
10
    struct spa_source volume_timer;
11
    struct spa_source release_timer;
12
    DBusPendingCall *acquire_call;
13
+   DBusPendingCall *volume_call;
14
 
15
    struct spa_hook_list listener_list;
16
    struct spa_callbacks impl;
17
pipewire-0.3.70.tar.gz/spa/plugins/bluez5/media-source.c -> pipewire-0.3.71.tar.gz/spa/plugins/bluez5/media-source.c Changed
137
 
1
@@ -123,7 +123,6 @@
2
    unsigned int is_input:1;
3
    unsigned int is_duplex:1;
4
    unsigned int is_internal:1;
5
-   unsigned int use_duplex_source:1;
6
 
7
    unsigned int node_latency;
8
 
9
@@ -148,9 +147,6 @@
10
    uint8_t buffer_read4096;
11
    struct timespec now;
12
    uint64_t sample_count;
13
-
14
-   int duplex_timerfd;
15
-   uint64_t duplex_timeout;
16
 };
17
 
18
 #define CHECK_PORT(this,d,p)    ((d) == SPA_DIRECTION_OUTPUT && (p) == 0)
19
@@ -532,34 +528,6 @@
20
        spa_loop_remove_source(this->data_loop, &this->source);
21
 }
22
 
23
-static int set_duplex_timeout(struct impl *this, uint64_t timeout)
24
-{
25
-   struct itimerspec ts;
26
-   ts.it_value.tv_sec = timeout / SPA_NSEC_PER_SEC;
27
-   ts.it_value.tv_nsec = timeout % SPA_NSEC_PER_SEC;
28
-   ts.it_interval.tv_sec = 0;
29
-   ts.it_interval.tv_nsec = 0;
30
-   return spa_system_timerfd_settime(this->data_system,
31
-           this->duplex_timerfd, 0, &ts, NULL);
32
-}
33
-
34
-static void media_on_duplex_timeout(struct spa_source *source)
35
-{
36
-   struct impl *this = source->data;
37
-   uint64_t exp;
38
-   int res;
39
-
40
-   if ((res = spa_system_timerfd_read(this->data_system, this->duplex_timerfd, &exp)) < 0) {
41
-       if (res != -EAGAIN)
42
-           spa_log_warn(this->log, "error reading timerfd: %s", spa_strerror(res));
43
-       return;
44
-   }
45
-
46
-   set_duplex_timeout(this, this->duplex_timeout);
47
-
48
-   media_on_ready_read(source);
49
-}
50
-
51
 static int setup_matching(struct impl *this)
52
 {
53
    struct port *port = &this->port;
54
@@ -703,33 +671,13 @@
55
 
56
    this->source.data = this;
57
 
58
-   if (!this->use_duplex_source) {
59
-       this->source.fd = this->fd;
60
-       this->source.func = media_on_ready_read;
61
-       this->source.mask = SPA_IO_IN;
62
-       this->source.rmask = 0;
63
-       spa_loop_add_source(this->data_loop, &this->source);
64
-   } else {
65
-       /*
66
-        * XXX: For an unknown reason (on Linux 5.13.10), the socket when working with
67
-        * XXX: "duplex" stream sometimes stops waking up from the poll, even though
68
-        * XXX: you can recv() from the socket with no problem.
69
-        * XXX:
70
-        * XXX: The reason for this should be found and fixed.
71
-        * XXX: To work around this, for now we just do the stupid thing and poll
72
-        * XXX: on a timer, chosen so that it's fast enough for the aptX-LL codec
73
-        * XXX: we currently support (which sends mSBC data), and also for Opus
74
-        * XXX: forward stream.
75
-        */
76
-       this->source.fd = this->duplex_timerfd;
77
-       this->source.func = media_on_duplex_timeout;
78
-       this->source.mask = SPA_IO_IN;
79
-       this->source.rmask = 0;
80
-       spa_loop_add_source(this->data_loop, &this->source);
81
-
82
-       this->duplex_timeout = SPA_NSEC_PER_MSEC * 25/10;
83
-       set_duplex_timeout(this, this->duplex_timeout);
84
-   }
85
+   this->source.fd = this->fd;
86
+   this->source.func = media_on_ready_read;
87
+   this->source.mask = SPA_IO_IN;
88
+   this->source.rmask = 0;
89
+   if ((res = spa_loop_add_source(this->data_loop, &this->source)) < 0)
90
+       spa_log_error(this->log, "%p: failed to add poll source: %s", this,
91
+               spa_strerror(res));
92
 
93
    this->sample_count = 0;
94
 
95
@@ -807,8 +755,6 @@
96
 
97
    this->transport_started = false;
98
 
99
-   set_duplex_timeout(this, 0);
100
-
101
    if (this->source.loop)
102
        spa_loop_remove_source(this->data_loop, &this->source);
103
 
104
@@ -1633,10 +1579,6 @@
105
    if (this->transport)
106
        spa_hook_remove(&this->transport_listener);
107
    spa_system_close(this->data_system, this->timerfd);
108
-   if (this->duplex_timerfd >= 0) {
109
-       spa_system_close(this->data_system, this->duplex_timerfd);
110
-       this->duplex_timerfd = -1;
111
-   }
112
    spa_bt_decode_buffer_clear(&port->buffer);
113
    return 0;
114
 }
115
@@ -1762,7 +1704,6 @@
116
        this->codec = this->codec->duplex_codec;
117
        this->is_input = true;
118
    }
119
-   this->use_duplex_source = this->is_duplex || (this->codec->duplex_codec != NULL);
120
 
121
    if (this->codec->bap)
122
        this->is_input = this->transport->bap_initiator;
123
@@ -1778,13 +1719,6 @@
124
    this->timerfd = spa_system_timerfd_create(this->data_system,
125
            CLOCK_MONOTONIC, SPA_FD_CLOEXEC | SPA_FD_NONBLOCK);
126
 
127
-   if (this->use_duplex_source) {
128
-       this->duplex_timerfd = spa_system_timerfd_create(this->data_system,
129
-               CLOCK_MONOTONIC, SPA_FD_CLOEXEC | SPA_FD_NONBLOCK);
130
-   } else {
131
-       this->duplex_timerfd = -1;
132
-   }
133
-
134
    this->node_latency = 512;
135
 
136
    set_latency(this, false);
137
pipewire-0.3.70.tar.gz/spa/plugins/bluez5/modemmanager.c -> pipewire-0.3.71.tar.gz/spa/plugins/bluez5/modemmanager.c Changed
201
 
1
@@ -18,8 +18,6 @@
2
 };
3
 
4
 struct impl {
5
-   struct spa_bt_monitor *monitor;
6
-
7
    struct spa_log *log;
8
    DBusConnection *conn;
9
 
10
@@ -44,26 +42,31 @@
11
 static bool mm_dbus_connection_send_with_reply(struct impl *this, DBusMessage *m, DBusPendingCall **pending_return,
12
                                                DBusPendingCallNotifyFunction function, void *user_data)
13
 {
14
-   dbus_bool_t dbus_ret;
15
-
16
    spa_assert(*pending_return == NULL);
17
 
18
-   dbus_ret = dbus_connection_send_with_reply(this->conn, m, pending_return, -1);
19
-   if (!dbus_ret || *pending_return == NULL) {
20
+   DBusPendingCall *pending_call;
21
+   bool ret = dbus_connection_send_with_reply(this->conn, m, &pending_call, -1);
22
+   if (!ret) {
23
        spa_log_debug(this->log, "dbus call failure");
24
-       return false;
25
+       goto out;
26
    }
27
 
28
-   dbus_ret = dbus_pending_call_set_notify(*pending_return, function, user_data, NULL);
29
-   if (!dbus_ret) {
30
+   spa_assert(pending_call);
31
+
32
+   ret = dbus_pending_call_set_notify(pending_call, function, user_data, NULL);
33
+   if (!ret) {
34
        spa_log_debug(this->log, "dbus set notify failure");
35
-       dbus_pending_call_cancel(*pending_return);
36
-       dbus_pending_call_unref(*pending_return);
37
-       *pending_return = NULL;
38
-       return false;
39
+       dbus_pending_call_cancel(pending_call);
40
+       dbus_pending_call_unref(pending_call);
41
+       goto out;
42
    }
43
 
44
-   return true;
45
+   *pending_return = pending_call;
46
+
47
+out:
48
+   dbus_message_unref(m);
49
+
50
+   return ret;
51
 }
52
 
53
 static int mm_state_to_clcc(struct impl *this, MMCallState state)
54
@@ -122,10 +125,10 @@
55
    MMCallState state;
56
 
57
    spa_assert(call->pending == pending);
58
-   dbus_pending_call_unref(pending);
59
    call->pending = NULL;
60
 
61
    r = dbus_pending_call_steal_reply(pending);
62
+   dbus_pending_call_unref(pending);
63
    if (r == NULL)
64
        return;
65
 
66
@@ -421,10 +424,10 @@
67
    DBusMessageIter i, array_i;
68
 
69
    spa_assert(this->pending == pending);
70
-   dbus_pending_call_unref(pending);
71
    this->pending = NULL;
72
 
73
    r = dbus_pending_call_steal_reply(pending);
74
+   dbus_pending_call_unref(pending);
75
    if (r == NULL)
76
        return;
77
 
78
@@ -541,8 +544,6 @@
79
    } else if (dbus_message_is_signal(m, DBUS_INTERFACE_OBJECTMANAGER, DBUS_SIGNAL_INTERFACES_ADDED)) {
80
        DBusMessageIter arg_i;
81
 
82
-       spa_log_warn(this->log, "sender: %s", dbus_message_get_sender(m));
83
-
84
        if (!dbus_message_iter_init(m, &arg_i) || !spa_streq(dbus_message_get_signature(m), "oa{sa{sv}}")) {
85
                spa_log_error(this->log, "Invalid signature found in InterfacesAdded");
86
                goto finish;
87
@@ -644,7 +645,6 @@
88
        dbus_message_append_args(m, DBUS_TYPE_STRING, &mm_call_interface, DBUS_TYPE_INVALID);
89
        if (!mm_dbus_connection_send_with_reply(this, m, &call_object->pending, mm_get_call_properties_reply, call_object)) {
90
            spa_log_error(this->log, "dbus call failure");
91
-           dbus_message_unref(m);
92
            goto finish;
93
        }
94
    } else if (dbus_message_is_signal(m, MM_DBUS_INTERFACE_MODEM_VOICE, MM_MODEM_VOICE_SIGNAL_CALLDELETED)) {
95
@@ -761,49 +761,6 @@
96
    return -EIO;
97
 }
98
 
99
-static bool is_dbus_service_available(struct impl *this, const char *service)
100
-{
101
-   DBusMessage *m, *r;
102
-   DBusError err;
103
-   bool success = false;
104
-
105
-   m = dbus_message_new_method_call("org.freedesktop.DBus", "/org/freedesktop/DBus",
106
-                                     "org.freedesktop.DBus", "NameHasOwner");
107
-   if (m == NULL)
108
-       return false;
109
-   dbus_message_append_args(m, DBUS_TYPE_STRING, &service, DBUS_TYPE_INVALID);
110
-
111
-   dbus_error_init(&err);
112
-   r = dbus_connection_send_with_reply_and_block(this->conn, m, -1, &err);
113
-   dbus_message_unref(m);
114
-   m = NULL;
115
-
116
-   if (r == NULL) {
117
-       spa_log_info(this->log, "NameHasOwner failed for %s", service);
118
-       dbus_error_free(&err);
119
-       goto finish;
120
-   }
121
-
122
-   if (dbus_message_get_type(r) == DBUS_MESSAGE_TYPE_ERROR) {
123
-       spa_log_error(this->log, "NameHasOwner() returned error: %s", dbus_message_get_error_name(r));
124
-       goto finish;
125
-   }
126
-
127
-   if (!dbus_message_get_args(r, &err,
128
-                  DBUS_TYPE_BOOLEAN, &success,
129
-                  DBUS_TYPE_INVALID)) {
130
-       spa_log_error(this->log, "Failed to parse NameHasOwner() reply: %s", err.message);
131
-       dbus_error_free(&err);
132
-       goto finish;
133
-   }
134
-
135
-finish:
136
-   if (r)
137
-       dbus_message_unref(r);
138
-
139
-   return success;
140
-}
141
-
142
 bool mm_is_available(void *modemmanager)
143
 {
144
    struct impl *this = modemmanager;
145
@@ -830,10 +787,10 @@
146
    free(data);
147
 
148
    spa_assert(call->pending == pending);
149
-   dbus_pending_call_unref(pending);
150
    call->pending = NULL;
151
 
152
    r = dbus_pending_call_steal_reply(pending);
153
+   dbus_pending_call_unref(pending);
154
    if (r == NULL)
155
        return;
156
 
157
@@ -863,10 +820,10 @@
158
    free(data);
159
 
160
    spa_assert(this->voice_pending == pending);
161
-   dbus_pending_call_unref(pending);
162
    this->voice_pending = NULL;
163
 
164
    r = dbus_pending_call_steal_reply(pending);
165
+   dbus_pending_call_unref(pending);
166
    if (r == NULL)
167
        return;
168
 
169
@@ -925,7 +882,6 @@
170
    }
171
    if (!mm_dbus_connection_send_with_reply(this, m, &call_object->pending, mm_get_call_simple_reply, data)) {
172
        spa_log_error(this->log, "dbus call failure");
173
-       dbus_message_unref(m);
174
        if (error)
175
            *error = CMEE_AG_FAILURE;
176
        return false;
177
@@ -983,7 +939,6 @@
178
    }
179
    if (!mm_dbus_connection_send_with_reply(this, m, &call_object->pending, mm_get_call_simple_reply, data)) {
180
        spa_log_error(this->log, "dbus call failure");
181
-       dbus_message_unref(m);
182
        if (error)
183
            *error = CMEE_AG_FAILURE;
184
        return false;
185
@@ -1049,7 +1004,6 @@
186
    dbus_message_iter_close_container(&iter, &dict);
187
    if (!mm_dbus_connection_send_with_reply(this, m, &this->voice_pending, mm_get_call_create_reply, data)) {
188
        spa_log_error(this->log, "dbus call failure");
189
-       dbus_message_unref(m);
190
        if (error)
191
            *error = CMEE_AG_FAILURE;
192
        return false;
193
@@ -1109,7 +1063,6 @@
194
    dbus_message_append_args(m, DBUS_TYPE_STRING, &dtmf, DBUS_TYPE_INVALID);
195
    if (!mm_dbus_connection_send_with_reply(this, m, &call_object->pending, mm_get_call_simple_reply, data)) {
196
        spa_log_error(this->log, "dbus call failure");
197
-       dbus_message_unref(m);
198
        if (error)
199
            *error = CMEE_AG_FAILURE;
200
        return false;
201
pipewire-0.3.70.tar.gz/spa/plugins/bluez5/upower.c -> pipewire-0.3.71.tar.gz/spa/plugins/bluez5/upower.c Changed
112
 
1
@@ -44,6 +44,7 @@
2
    DBusMessageIter i, variant_i;
3
 
4
    r = dbus_pending_call_steal_reply(pending);
5
+   dbus_pending_call_unref(pending);
6
    if (r == NULL)
7
        return;
8
 
9
@@ -188,49 +189,6 @@
10
    return -EIO;
11
 }
12
 
13
-static bool is_dbus_service_available(struct impl *this, const char *service)
14
-{
15
-   DBusMessage *m, *r;
16
-   DBusError err;
17
-   bool success = false;
18
-
19
-   m = dbus_message_new_method_call("org.freedesktop.DBus", "/org/freedesktop/DBus",
20
-                                     "org.freedesktop.DBus", "NameHasOwner");
21
-   if (m == NULL)
22
-       return false;
23
-   dbus_message_append_args(m, DBUS_TYPE_STRING, &service, DBUS_TYPE_INVALID);
24
-
25
-   dbus_error_init(&err);
26
-   r = dbus_connection_send_with_reply_and_block(this->conn, m, -1, &err);
27
-   dbus_message_unref(m);
28
-   m = NULL;
29
-
30
-   if (r == NULL) {
31
-       spa_log_info(this->log, "NameHasOwner failed for %s", service);
32
-       dbus_error_free(&err);
33
-       goto finish;
34
-   }
35
-
36
-   if (dbus_message_get_type(r) == DBUS_MESSAGE_TYPE_ERROR) {
37
-       spa_log_error(this->log, "NameHasOwner() returned error: %s", dbus_message_get_error_name(r));
38
-       goto finish;
39
-   }
40
-
41
-   if (!dbus_message_get_args(r, &err,
42
-                  DBUS_TYPE_BOOLEAN, &success,
43
-                  DBUS_TYPE_INVALID)) {
44
-       spa_log_error(this->log, "Failed to parse NameHasOwner() reply: %s", err.message);
45
-       dbus_error_free(&err);
46
-       goto finish;
47
-   }
48
-
49
-finish:
50
-   if (r)
51
-       dbus_message_unref(r);
52
-
53
-   return success;
54
-}
55
-
56
 void *upower_register(struct spa_log *log,
57
                       void *dbus_connection,
58
                       void (*set_battery_level)(unsigned int level, void *user_data),
59
@@ -250,33 +208,33 @@
60
    this->log = log;
61
    this->conn = dbus_connection;
62
    this->set_battery_level = set_battery_level;
63
-    this->user_data = user_data;
64
+   this->user_data = user_data;
65
 
66
    if (add_filters(this) < 0) {
67
        goto fail4;
68
    }
69
 
70
-   if (is_dbus_service_available(this, UPOWER_SERVICE)) {
71
-       DBusMessage *m;
72
-       DBusPendingCall *call;
73
-       static const char* upower_device_interface = UPOWER_DEVICE_INTERFACE;
74
-       static const char* percentage_property = "Percentage";
75
-
76
-       m = dbus_message_new_method_call(UPOWER_SERVICE, UPOWER_DISPLAY_DEVICE_OBJECT, DBUS_INTERFACE_PROPERTIES, "Get");
77
-       if (m == NULL)
78
-           goto fail4;
79
-       dbus_message_append_args(m, DBUS_TYPE_STRING, &upower_device_interface,
80
-                                DBUS_TYPE_STRING, &percentage_property, DBUS_TYPE_INVALID);
81
-       dbus_connection_send_with_reply(this->conn, m, &call, -1);
82
-       dbus_pending_call_set_notify(call, upower_get_percentage_properties_reply, this, NULL);
83
-       dbus_message_unref(m);
84
-   }
85
+   DBusMessage *m;
86
+   DBusPendingCall *call;
87
 
88
-    return this;
89
+   m = dbus_message_new_method_call(UPOWER_SERVICE, UPOWER_DISPLAY_DEVICE_OBJECT, DBUS_INTERFACE_PROPERTIES, "Get");
90
+   if (m == NULL)
91
+       goto fail4;
92
+
93
+   dbus_message_append_args(m,
94
+                DBUS_TYPE_STRING, &(const char *){ UPOWER_DEVICE_INTERFACE },
95
+                DBUS_TYPE_STRING, &(const char *){ "Percentage" },
96
+                DBUS_TYPE_INVALID);
97
+   dbus_message_set_auto_start(m, false);
98
+   dbus_connection_send_with_reply(this->conn, m, &call, -1);
99
+   dbus_pending_call_set_notify(call, upower_get_percentage_properties_reply, this, NULL);
100
+   dbus_message_unref(m);
101
+
102
+   return this;
103
 
104
 fail4:
105
-    free(this);
106
-    return NULL;
107
+   free(this);
108
+   return NULL;
109
 }
110
 
111
 void upower_unregister(void *data)
112
pipewire-0.3.70.tar.gz/spa/plugins/control/mixer.c -> pipewire-0.3.71.tar.gz/spa/plugins/control/mixer.c Changed
33
 
1
@@ -23,7 +23,7 @@
2
 #define NAME "control-mixer"
3
 
4
 #define MAX_BUFFERS     64
5
-#define MAX_PORTS       128
6
+#define MAX_PORTS       512
7
 
8
 struct buffer {
9
    uint32_t id;
10
@@ -70,6 +70,9 @@
11
    struct port *in_portsMAX_PORTS;
12
    struct port out_ports1;
13
 
14
+   struct spa_pod_control *mix_ctrlMAX_PORTS;
15
+   struct spa_pod_sequence *mix_seqMAX_PORTS;
16
+
17
    int n_formats;
18
 
19
    unsigned int have_format:1;
20
@@ -624,9 +627,9 @@
21
                 return -EPIPE;
22
         }
23
 
24
-   ctrl = alloca(MAX_PORTS * sizeof(struct spa_pod_control *));
25
-   seq = alloca(MAX_PORTS * sizeof(struct spa_pod_sequence *));
26
-        n_seq = 0;
27
+   ctrl = this->mix_ctrl;
28
+   seq = this->mix_seq;
29
+   n_seq = 0;
30
 
31
    /* collect all sequence pod on input ports */
32
    for (i = 0; i < this->last_port; i++) {
33
pipewire-0.3.70.tar.gz/spa/plugins/jack/jack-client.c -> pipewire-0.3.71.tar.gz/spa/plugins/jack/jack-client.c Changed
37
 
1
@@ -18,6 +18,8 @@
2
 
3
    client->buffer_size = nframes;
4
 
5
+   spa_log_trace_fp(client->log, "frames %u", nframes);
6
+
7
    spa_jack_client_emit_process(client);
8
 
9
    return 0;
10
@@ -27,6 +29,8 @@
11
 {
12
    struct spa_jack_client *client = arg;
13
 
14
+   spa_log_warn(client->log, "%p", client);
15
+
16
    spa_jack_client_emit_shutdown(client);
17
 
18
    spa_hook_list_init(&client->listener_list);
19
@@ -67,6 +71,8 @@
20
 
21
    spa_hook_list_init(&client->listener_list);
22
 
23
+   spa_log_info(client->log, "%p: %s", client, client_name);
24
+
25
    jack_set_process_callback(client->client, jack_process, client);
26
    jack_on_shutdown(client->client, jack_shutdown, client);
27
    client->frame_rate = jack_get_sample_rate(client->client);
28
@@ -81,6 +87,8 @@
29
    if (client->client == NULL)
30
        return 0;
31
 
32
+   spa_log_info(client->log, "%p:", client);
33
+
34
    spa_jack_client_emit_destroy(client);
35
 
36
    if (jack_client_close(client->client) != 0)
37
pipewire-0.3.70.tar.gz/spa/plugins/jack/jack-device.c -> pipewire-0.3.71.tar.gz/spa/plugins/jack/jack-device.c Changed
9
 
1
@@ -393,6 +393,7 @@
2
    this = (struct impl *) handle;
3
 
4
    this->log = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_Log);
5
+   this->client.log = this->log;
6
 
7
    this->device.iface = SPA_INTERFACE_INIT(
8
            SPA_TYPE_INTERFACE_Device,
9
pipewire-0.3.70.tar.gz/spa/plugins/jack/jack-sink.c -> pipewire-0.3.71.tar.gz/spa/plugins/jack/jack-sink.c Changed
65
 
1
@@ -67,7 +67,6 @@
2
    struct spa_node node;
3
 
4
    struct spa_log *log;
5
-   struct spa_loop *data_loop;
6
 
7
    uint64_t info_all;
8
    struct spa_node_info info;
9
@@ -169,6 +168,11 @@
10
    return 0;
11
 }
12
 
13
+static inline bool is_following(struct impl *impl)
14
+{
15
+   return impl->position && impl->clock && impl->position->clock.id != impl->clock->id;
16
+}
17
+
18
 static int impl_node_set_io(void *object, uint32_t id, void *data, size_t size)
19
 {
20
    struct impl *this = object;
21
@@ -235,15 +239,18 @@
22
    if (full)
23
        this->info.change_mask = this->info_all;
24
    if (this->info.change_mask) {
25
-       struct spa_dict_item items5;
26
+       struct spa_dict_item items8;
27
        char latency64;
28
        snprintf(latency, sizeof(latency), "%d/%d",
29
                this->client->buffer_size, this->client->frame_rate);
30
        items0 = SPA_DICT_ITEM_INIT(SPA_KEY_MEDIA_CLASS, "Audio/Sink");
31
-       items1 = SPA_DICT_ITEM_INIT(SPA_KEY_NODE_NAME, "JACK System");
32
+       items1 = SPA_DICT_ITEM_INIT(SPA_KEY_NODE_NAME, "JACK Sink");
33
        items2 = SPA_DICT_ITEM_INIT(SPA_KEY_NODE_DRIVER, "true");
34
        items3 = SPA_DICT_ITEM_INIT(SPA_KEY_NODE_PAUSE_ON_IDLE, "false");
35
-       items4 = SPA_DICT_ITEM_INIT(SPA_KEY_NODE_LATENCY, latency);
36
+       items4 = SPA_DICT_ITEM_INIT(SPA_KEY_NODE_ALWAYS_PROCESS, "true");
37
+       items5 = SPA_DICT_ITEM_INIT("priority.driver", "30001");
38
+       items6 = SPA_DICT_ITEM_INIT("node.group", "jack-group");
39
+       items7 = SPA_DICT_ITEM_INIT(SPA_KEY_NODE_LATENCY, latency);
40
        this->info.props = &SPA_DICT_INIT_ARRAY(items);
41
        spa_node_emit_info(&this->hooks, &this->info);
42
        this->info.change_mask = old;
43
@@ -319,6 +326,9 @@
44
 {
45
    struct impl *this = data;
46
 
47
+   if (is_following(this))
48
+       return;
49
+
50
    if (this->clock) {
51
        struct spa_io_clock *c = this->clock;
52
        c->nsec = this->client->current_usecs * SPA_NSEC_PER_USEC;
53
@@ -755,10 +765,8 @@
54
        spa_memcpy(dst, src->data, n_frames * port->stride);
55
 
56
        io->status = SPA_STATUS_NEED_DATA;
57
-
58
-       res |= SPA_STATUS_NEED_DATA;
59
    }
60
-   return res;
61
+   return res | SPA_STATUS_NEED_DATA;
62
 }
63
 
64
 static const struct spa_node_methods impl_node = {
65
pipewire-0.3.70.tar.gz/spa/plugins/jack/jack-source.c -> pipewire-0.3.71.tar.gz/spa/plugins/jack/jack-source.c Changed
67
 
1
@@ -69,7 +69,6 @@
2
    struct spa_node node;
3
 
4
    struct spa_log *log;
5
-   struct spa_loop *data_loop;
6
 
7
    uint64_t info_all;
8
    struct spa_node_info info;
9
@@ -263,15 +262,18 @@
10
    if (full)
11
        this->info.change_mask = this->info_all;
12
    if (this->info.change_mask) {
13
-       struct spa_dict_item items5;
14
+       struct spa_dict_item items8;
15
        char latency64;
16
        snprintf(latency, sizeof(latency), "%d/%d",
17
                this->client->buffer_size, this->client->frame_rate);
18
        items0 = SPA_DICT_ITEM_INIT(SPA_KEY_MEDIA_CLASS, "Audio/Source");
19
-       items1 = SPA_DICT_ITEM_INIT(SPA_KEY_NODE_NAME, "JACK System");
20
+       items1 = SPA_DICT_ITEM_INIT(SPA_KEY_NODE_NAME, "JACK Source");
21
        items2 = SPA_DICT_ITEM_INIT(SPA_KEY_NODE_DRIVER, "true");
22
        items3 = SPA_DICT_ITEM_INIT(SPA_KEY_NODE_PAUSE_ON_IDLE, "false");
23
-       items4 = SPA_DICT_ITEM_INIT(SPA_KEY_NODE_LATENCY, latency);
24
+       items4 = SPA_DICT_ITEM_INIT(SPA_KEY_NODE_ALWAYS_PROCESS, "true");
25
+       items5 = SPA_DICT_ITEM_INIT("priority.driver", "30000");
26
+       items6 = SPA_DICT_ITEM_INIT("node.group", "jack-group");
27
+       items7 = SPA_DICT_ITEM_INIT(SPA_KEY_NODE_LATENCY, latency);
28
        this->info.props = &SPA_DICT_INIT_ARRAY(items);
29
        spa_node_emit_info(&this->hooks, &this->info);
30
        this->info.change_mask = old;
31
@@ -342,15 +344,24 @@
32
    return 0;
33
 }
34
 
35
+static inline bool is_following(struct impl *impl)
36
+{
37
+   return impl->position && impl->clock && impl->position->clock.id != impl->clock->id;
38
+}
39
+
40
 static void client_process(void *data)
41
 {
42
    struct impl *this = data;
43
    int res;
44
 
45
+   if (is_following(this))
46
+       return;
47
+
48
+   spa_log_trace_fp(this->log, "%p, process", this);
49
+
50
    res = spa_node_process(&this->node);
51
 
52
-   if (res != SPA_STATUS_OK)
53
-       spa_node_call_ready(&this->callbacks, res);
54
+   spa_node_call_ready(&this->callbacks, res);
55
 }
56
 
57
 static const struct spa_jack_client_events client_events = {
58
@@ -780,7 +791,7 @@
59
 
60
        res |= SPA_STATUS_HAVE_DATA;
61
    }
62
-   return res;
63
+   return res | SPA_STATUS_HAVE_DATA;
64
 }
65
 
66
 static const struct spa_node_methods impl_node = {
67
pipewire-0.3.70.tar.gz/spa/plugins/support/loop.c -> pipewire-0.3.71.tar.gz/spa/plugins/support/loop.c Changed
128
 
1
@@ -319,6 +319,8 @@
2
          void *data)
3
 {
4
    struct impl *impl = object;
5
+   spa_return_if_fail(SPA_CALLBACK_CHECK(hooks, before, 0));
6
+   spa_return_if_fail(SPA_CALLBACK_CHECK(hooks, after, 0));
7
    spa_hook_list_append(&impl->hooks_list, hook, hooks, data);
8
 }
9
 
10
@@ -336,7 +338,7 @@
11
        spa_return_if_fail(pthread_equal(impl->thread, thread_id));
12
        impl->enter_count++;
13
    }
14
-   spa_log_trace(impl->log, "%p: enter %p", impl, (void *) impl->thread);
15
+   spa_log_trace_fp(impl->log, "%p: enter %p", impl, (void *) impl->thread);
16
 }
17
 
18
 static void loop_leave(void *object)
19
@@ -347,7 +349,7 @@
20
    spa_return_if_fail(impl->enter_count > 0);
21
    spa_return_if_fail(pthread_equal(impl->thread, thread_id));
22
 
23
-   spa_log_trace(impl->log, "%p: leave %p", impl, (void *) impl->thread);
24
+   spa_log_trace_fp(impl->log, "%p: leave %p", impl, (void *) impl->thread);
25
 
26
    if (--impl->enter_count == 0) {
27
        impl->thread = 0;
28
@@ -397,7 +399,7 @@
29
    }
30
 }
31
 
32
-static int loop_iterate(void *object, int timeout)
33
+static int loop_iterate_cancel(void *object, int timeout)
34
 {
35
    struct impl *impl = object;
36
    struct spa_poll_event epMAX_EP, *e;
37
@@ -444,6 +446,52 @@
38
    return nfds;
39
 }
40
 
41
+static int loop_iterate(void *object, int timeout)
42
+{
43
+   struct impl *impl = object;
44
+   struct spa_poll_event epMAX_EP, *e;
45
+   int i, nfds;
46
+
47
+   impl->polling = true;
48
+   spa_loop_control_hook_before(&impl->hooks_list);
49
+
50
+   nfds = spa_system_pollfd_wait(impl->system, impl->poll_fd, ep, SPA_N_ELEMENTS(ep), timeout);
51
+
52
+   spa_loop_control_hook_after(&impl->hooks_list);
53
+   impl->polling = false;
54
+
55
+   /* first we set all the rmasks, then call the callbacks. The reason is that
56
+    * some callback might also want to look at other sources it manages and
57
+    * can then reset the rmask to suppress the callback */
58
+   for (i = 0; i < nfds; i++) {
59
+       struct spa_source *s = epi.data;
60
+
61
+       s->rmask = epi.events;
62
+       /* already active in another iteration of the loop,
63
+        * remove it from that iteration */
64
+       if (SPA_UNLIKELY(e = s->priv))
65
+           e->data = NULL;
66
+       s->priv = &epi;
67
+   }
68
+
69
+   if (SPA_UNLIKELY(!spa_list_is_empty(&impl->destroy_list)))
70
+       process_destroy(impl);
71
+
72
+   for (i = 0; i < nfds; i++) {
73
+       struct spa_source *s = epi.data;
74
+       if (SPA_LIKELY(s && s->rmask))
75
+           s->func(s);
76
+   }
77
+   for (i = 0; i < nfds; i++) {
78
+       struct spa_source *s = epi.data;
79
+       if (SPA_LIKELY(s)) {
80
+           s->rmask = 0;
81
+           s->priv = NULL;
82
+       }
83
+   }
84
+   return nfds;
85
+}
86
+
87
 static void source_io_func(struct spa_source *source)
88
 {
89
    struct source_impl *s = SPA_CONTAINER_OF(source, struct source_impl, source);
90
@@ -826,6 +874,16 @@
91
    .invoke = loop_invoke,
92
 };
93
 
94
+static const struct spa_loop_control_methods impl_loop_control_cancel = {
95
+   SPA_VERSION_LOOP_CONTROL_METHODS,
96
+   .get_fd = loop_get_fd,
97
+   .add_hook = loop_add_hook,
98
+   .enter = loop_enter,
99
+   .leave = loop_leave,
100
+   .iterate = loop_iterate_cancel,
101
+   .check = loop_check,
102
+};
103
+
104
 static const struct spa_loop_control_methods impl_loop_control = {
105
    SPA_VERSION_LOOP_CONTROL_METHODS,
106
    .get_fd = loop_get_fd,
107
@@ -908,6 +966,7 @@
108
      uint32_t n_support)
109
 {
110
    struct impl *impl;
111
+   const char *str;
112
    int res;
113
 
114
    spa_return_val_if_fail(factory != NULL, -EINVAL);
115
@@ -930,6 +989,12 @@
116
            SPA_VERSION_LOOP_UTILS,
117
            &impl_loop_utils, impl);
118
 
119
+   if (info) {
120
+       if ((str = spa_dict_lookup(info, "loop.cancel")) != NULL &&
121
+           spa_atob(str))
122
+           impl->control.iface.cb.funcs = &impl_loop_control_cancel;
123
+   }
124
+
125
    impl->log = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_Log);
126
    spa_log_topic_init(impl->log, &log_topic);
127
    impl->system = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_System);
128
pipewire-0.3.70.tar.gz/spa/plugins/support/null-audio-sink.c -> pipewire-0.3.71.tar.gz/spa/plugins/support/null-audio-sink.c Changed
46
 
1
@@ -41,6 +41,7 @@
2
    uint32_t posSPA_AUDIO_MAX_CHANNELS;
3
    char clock_name64;
4
    unsigned int debug:1;
5
+   unsigned int driver:1;
6
 };
7
 
8
 static void reset_props(struct props *props)
9
@@ -51,6 +52,7 @@
10
    props->n_pos = 0;
11
    strncpy(props->clock_name, DEFAULT_CLOCK_NAME, sizeof(props->clock_name));
12
    props->debug = false;
13
+   props->driver = true;
14
 }
15
 
16
 #define DEFAULT_CHANNELS   2
17
@@ -356,9 +358,6 @@
18
    return 0;
19
 }
20
 
21
-static const struct spa_dict_item node_info_items = {
22
-   { SPA_KEY_NODE_DRIVER, "true" },
23
-};
24
 
25
 static void emit_node_info(struct impl *this, bool full)
26
 {
27
@@ -366,6 +365,9 @@
28
    if (full)
29
        this->info.change_mask = this->info_all;
30
    if (this->info.change_mask) {
31
+       const struct spa_dict_item node_info_items = {
32
+           { SPA_KEY_NODE_DRIVER, this->props.driver ? "true" : "false" },
33
+       };
34
        this->info.props = &SPA_DICT_INIT_ARRAY(node_info_items);
35
        spa_node_emit_info(&this->hooks, &this->info);
36
        this->info.change_mask = old;
37
@@ -979,6 +981,8 @@
38
            this->props.channels = atoi(s);
39
        } else if (spa_streq(k, SPA_KEY_AUDIO_RATE)) {
40
            this->props.rate = atoi(s);
41
+       } else if (spa_streq(k, SPA_KEY_NODE_DRIVER)) {
42
+           this->props.driver = spa_atob(s);
43
        } else if (spa_streq(k, SPA_KEY_AUDIO_POSITION)) {
44
            parse_position(this, s, strlen(s));
45
        } else if (spa_streq(k, "clock.name")) {
46
pipewire-0.3.70.tar.gz/src/daemon/jack.conf.in -> pipewire-0.3.71.tar.gz/src/daemon/jack.conf.in Changed
18
 
1
@@ -72,6 +72,7 @@
2
      #node.force-quantum = 0
3
      #jack.show-monitor  = true
4
      #jack.merge-monitor = true
5
+     #jack.show-midi     = true
6
      #jack.short-name    = false
7
      #jack.filter-name   = false
8
      #jack.filter-char   = " "
9
@@ -86,6 +87,8 @@
10
      #jack.default-as-system = false
11
      #jack.fix-midi-events   = true
12
      #jack.global-buffer-size = false
13
+     #jack.max-client-ports   = 768
14
+     #jack.fill-aliases       = false
15
 }
16
 
17
 # client specific properties
18
pipewire-0.3.70.tar.gz/src/daemon/pipewire-aes67.conf.in -> pipewire-0.3.71.tar.gz/src/daemon/pipewire-aes67.conf.in Changed
39
 
1
@@ -36,17 +36,29 @@
2
     { name = libpipewire-module-protocol-native }
3
     { name = libpipewire-module-client-node }
4
     { name = libpipewire-module-adapter }
5
-    { name = libpipewire-module-rtp-source
6
+    { name = libpipewire-module-rtp-sap
7
         args = {
8
+            local.ifname = eth0
9
             sap.ip = 239.255.255.255
10
             sap.port = 9875
11
-            sess.latency.msec = 10
12
-            local.ifname = eth0
13
-            stream.props = {
14
-               media.class = "Audio/Source"
15
-               node.virtual = false
16
-               device.api = aes67
17
-            }
18
+
19
+            stream.rules = 
20
+                {
21
+                    matches = 
22
+                        {
23
+                            rtp.session = "~.*"
24
+                        }
25
+                    
26
+                    actions = {
27
+                        create-stream = {
28
+                            node.virtual = false
29
+                            media.class = "Audio/Source"
30
+                            device.api = aes67
31
+                            sess.latency.msec = 10
32
+                        }
33
+                    }
34
+                }
35
+            
36
         }
37
     }
38
 
39
pipewire-0.3.70.tar.gz/src/daemon/pipewire-pulse.conf.in -> pipewire-0.3.71.tar.gz/src/daemon/pipewire-pulse.conf.in Changed
9
 
1
@@ -95,6 +95,7 @@
2
         #  client.access = "restricted"     # permissions for clients
3
         #}
4
     
5
+    #server.dbus-name       = "org.pulseaudio.Server"
6
     #pulse.min.req          = 128/48000     # 2.7ms
7
     #pulse.default.req      = 960/48000     # 20 milliseconds
8
     #pulse.min.frag         = 128/48000     # 2.7ms
9
pipewire-0.3.70.tar.gz/src/modules/meson.build -> pipewire-0.3.71.tar.gz/src/modules/meson.build Changed
52
 
1
@@ -15,6 +15,8 @@
2
   'module-example-source.c',
3
   'module-fallback-sink.c',
4
   'module-filter-chain.c',
5
+  'module-jack-tunnel.c',
6
+  'module-jackdbus-detect.c',
7
   'module-link-factory.c',
8
   'module-loopback.c',
9
   'module-metadata.c',
10
@@ -158,6 +160,33 @@
11
   dependencies : mathlib, dl_lib, pipewire_dep, audioconvert_dep,
12
 )
13
 
14
+build_module_jack_tunnel = jack_dep.found()
15
+if build_module_jack_tunnel
16
+  pipewire_module_jack_tunnel = shared_library('pipewire-module-jack-tunnel',
17
+     'module-jack-tunnel.c' ,
18
+    include_directories : configinc,
19
+    install : true,
20
+    install_dir : modules_install_dir,
21
+    install_rpath: modules_install_dir,
22
+    dependencies : mathlib, dl_lib, pipewire_dep,
23
+  )
24
+  build_module_jackdbus_detect = dbus_dep.found()
25
+  if build_module_jackdbus_detect
26
+    pipewire_module_jack_tunnel = shared_library('pipewire-module-jackdbus-detect',
27
+       'module-jackdbus-detect.c' ,
28
+      include_directories : configinc,
29
+      install : true,
30
+      install_dir : modules_install_dir,
31
+      install_rpath: modules_install_dir,
32
+      dependencies : mathlib, dl_lib, pipewire_dep, dbus_dep,
33
+    )
34
+  endif
35
+endif
36
+
37
+summary({'jack-tunnel': build_module_jack_tunnel}, bool_yn: true, section: 'Optional Modules')
38
+
39
+
40
+
41
 pipewire_module_profiler = shared_library('pipewire-module-profiler',
42
    'module-profiler.c',
43
     'module-profiler/protocol-native.c', ,
44
@@ -273,6 +302,7 @@
45
   'module-protocol-pulse/modules/module-always-sink.c',
46
   'module-protocol-pulse/modules/module-combine-sink.c',
47
   'module-protocol-pulse/modules/module-echo-cancel.c',
48
+  'module-protocol-pulse/modules/module-jackdbus-detect.c',
49
   'module-protocol-pulse/modules/module-ladspa-sink.c',
50
   'module-protocol-pulse/modules/module-ladspa-source.c',
51
   'module-protocol-pulse/modules/module-loopback.c',
52
pipewire-0.3.70.tar.gz/src/modules/module-access.c -> pipewire-0.3.71.tar.gz/src/modules/module-access.c Changed
138
 
1
@@ -9,6 +9,7 @@
2
 #include <sys/stat.h>
3
 #include <fcntl.h>
4
 #include <unistd.h>
5
+#include <limits.h>
6
 
7
 #include "config.h"
8
 
9
@@ -131,41 +132,57 @@
10
    struct spa_hook module_listener;
11
 };
12
 
13
-static int check_cmdline(struct pw_impl_client *client, int pid, const char *str)
14
+static int get_exe_name(int pid, char *buf, size_t buf_size)
15
 {
16
-   char path2048, key1024;
17
-   ssize_t len;
18
-   int fd, res;
19
-   struct spa_json it2;
20
+   char path256;
21
+   struct stat s1, s2;
22
+   int res;
23
+
24
+   /*
25
+    * Find executable name, checking it is an existing file
26
+    * (in the current namespace).
27
+    */
28
+
29
+#if defined(__linux__)
30
+   spa_scnprintf(path, sizeof(path), "/proc/%u/exe", pid);
31
+#elif defined(__FreeBSD__) || defined(__MidnightBSD__)
32
+   spa_scnprintf(path, sizeof(path), "/proc/%u/file", pid);
33
+#else
34
+   return -ENOTSUP;
35
+#endif
36
 
37
-   sprintf(path, "/proc/%u/cmdline", pid);
38
+   res = readlink(path, buf, buf_size);
39
+   if (res < 0)
40
+       return -errno;
41
+   if ((size_t)res >= buf_size)
42
+       return -E2BIG;
43
+   bufres = '\0';
44
 
45
-   fd = open(path, O_RDONLY);
46
-   if (fd < 0) {
47
-       res = -errno;
48
-       goto exit;
49
-   }
50
-   if ((len = read(fd, path, sizeof(path)-1)) < 0) {
51
-       res = -errno;
52
-       goto exit_close;
53
-   }
54
-   pathlen = '\0';
55
+   /* Check the file exists (= not deleted, and is in current namespace) */
56
+   if (stat(path, &s1) != 0 || stat(buf, &s2) != 0)
57
+       return -errno;
58
+   if (s1.st_dev != s2.st_dev || s1.st_ino != s2.st_ino)
59
+       return -ENXIO;
60
+
61
+   return 0;
62
+}
63
+
64
+static int check_exe(struct pw_impl_client *client, const char *path, const char *str)
65
+{
66
+   char key1024;
67
+   int res;
68
+   struct spa_json it2;
69
 
70
    spa_json_init(&it0, str, strlen(str));
71
    if ((res = spa_json_enter_array(&it0, &it1)) <= 0)
72
-       goto exit_close;
73
+       return res;
74
 
75
    while (spa_json_get_string(&it1, key, sizeof(key)) > 0) {
76
-       if (spa_streq(path, key)) {
77
-           res = 1;
78
-           goto exit_close;
79
-       }
80
+       if (spa_streq(path, key))
81
+           return 1;
82
    }
83
-   res = 0;
84
-exit_close:
85
-   close(fd);
86
-exit:
87
-   return res;
88
+
89
+   return 0;
90
 }
91
 
92
 static void
93
@@ -174,6 +191,7 @@
94
    struct impl *impl = data;
95
    struct pw_permission permissions1;
96
    struct spa_dict_item items2;
97
+   char exe_pathPATH_MAX;
98
    const struct pw_properties *props;
99
    const char *str, *access;
100
    char *flatpak_app_id = NULL;
101
@@ -195,10 +213,17 @@
102
        goto granted;
103
    } else {
104
        pw_log_info("client %p has trusted pid %d", client, pid);
105
+       if ((res = get_exe_name(pid, exe_path, sizeof(exe_path))) >= 0) {
106
+           pw_log_info("client %p has trusted exe path '%s'", client, exe_path);
107
+       } else {
108
+           pw_log_info("client %p has no trusted exe path: %s",
109
+                   client, spa_strerror(res));
110
+           exe_path0 = '\0';
111
+       }
112
    }
113
 
114
    if (impl->properties && (str = pw_properties_get(impl->properties, "access.allowed")) != NULL) {
115
-       res = check_cmdline(client, pid, str);
116
+       res = check_exe(client, exe_path, str);
117
        if (res < 0) {
118
            pw_log_warn("%p: client %p allowed check failed: %s",
119
                impl, client, spa_strerror(res));
120
@@ -209,7 +234,7 @@
121
    }
122
 
123
    if (impl->properties && (str = pw_properties_get(impl->properties, "access.rejected")) != NULL) {
124
-       res = check_cmdline(client, pid, str);
125
+       res = check_exe(client, exe_path, str);
126
        if (res < 0) {
127
            pw_log_warn("%p: client %p rejected check failed: %s",
128
                impl, client, spa_strerror(res));
129
@@ -221,7 +246,7 @@
130
    }
131
 
132
    if (impl->properties && (str = pw_properties_get(impl->properties, "access.restricted")) != NULL) {
133
-       res = check_cmdline(client, pid, str);
134
+       res = check_exe(client, exe_path, str);
135
        if (res < 0) {
136
            pw_log_warn("%p: client %p restricted check failed: %s",
137
                impl, client, spa_strerror(res));
138
pipewire-0.3.70.tar.gz/src/modules/module-client-node/client-node.c -> pipewire-0.3.71.tar.gz/src/modules/module-client-node/client-node.c Changed
201
 
1
@@ -31,13 +31,14 @@
2
 #define MAX_BUFFERS    64
3
 #define MAX_METAS  16u
4
 #define MAX_DATAS  64u
5
-#define MAX_AREAS  2048
6
+#define AREA_SIZE  (4096u / sizeof(struct spa_io_buffers))
7
+#define MAX_AREAS  32
8
 
9
-#define CHECK_FREE_PORT(this,d,p)  (p <= pw_map_get_size(&this->portsd) && !CHECK_PORT(this,d,p))
10
-#define CHECK_PORT(this,d,p)       (pw_map_lookup(&this->portsd, p) != NULL)
11
-#define GET_PORT(this,d,p)     (pw_map_lookup(&this->portsd, p))
12
+#define CHECK_FREE_PORT(impl,d,p)  (p <= pw_map_get_size(&impl->portsd) && !CHECK_PORT(impl,d,p))
13
+#define CHECK_PORT(impl,d,p)       (pw_map_lookup(&impl->portsd, p) != NULL)
14
+#define GET_PORT(impl,d,p)     (pw_map_lookup(&impl->portsd, p))
15
 
16
-#define CHECK_PORT_BUFFER(this,b,p)      (b < p->n_buffers)
17
+#define CHECK_PORT_BUFFER(impl,b,p)      (b < p->n_buffers)
18
 
19
 struct buffer {
20
    struct spa_buffer *outbuf;
21
@@ -63,7 +64,6 @@
22
 
23
 struct port {
24
    struct pw_impl_port *port;
25
-   struct node *node;
26
    struct impl *impl;
27
 
28
    enum spa_direction direction;
29
@@ -82,10 +82,12 @@
30
    struct pw_array mix;
31
 };
32
 
33
-struct node {
34
-   struct spa_node node;
35
+struct impl {
36
+   struct pw_impl_client_node this;
37
 
38
-   struct impl *impl;
39
+   struct pw_context *context;
40
+
41
+   struct spa_node node;
42
 
43
    struct spa_log *log;
44
    struct spa_loop *data_loop;
45
@@ -98,24 +100,15 @@
46
    struct pw_impl_client *client;
47
 
48
    struct spa_source data_source;
49
-   int writefd;
50
 
51
    struct pw_map ports2;
52
 
53
    struct port dummy;
54
 
55
    struct params params;
56
-};
57
-
58
-struct impl {
59
-   struct pw_impl_client_node this;
60
-
61
-   struct pw_context *context;
62
-
63
-   struct node node;
64
 
65
    struct pw_map io_map;
66
-   struct pw_memblock *io_areas;
67
+   struct pw_array io_areas;
68
 
69
    struct pw_memblock *activation;
70
 
71
@@ -127,9 +120,6 @@
72
 
73
    uint32_t bind_node_version;
74
    uint32_t bind_node_id;
75
-
76
-   int fds2;
77
-   int other_fds2;
78
 };
79
 
80
 #define pw_client_node_resource(r,m,v,...) \
81
@@ -239,10 +229,8 @@
82
    return mix;
83
 }
84
 
85
-static void clear_data(struct node *this, struct spa_data *d)
86
+static void clear_data(struct impl *impl, struct spa_data *d)
87
 {
88
-   struct impl *impl = this->impl;
89
-
90
    switch (d->type) {
91
    case SPA_DATA_MemId:
92
    {
93
@@ -250,7 +238,7 @@
94
        struct pw_memblock *m;
95
 
96
        id = SPA_PTR_TO_UINT32(d->data);
97
-       m = pw_mempool_find_id(this->client->pool, id);
98
+       m = pw_mempool_find_id(impl->client->pool, id);
99
        if (m) {
100
            pw_log_debug("%p: mem %d", impl, m->id);
101
            pw_memblock_unref(m);
102
@@ -267,18 +255,18 @@
103
    }
104
 }
105
 
106
-static int clear_buffers(struct node *this, struct mix *mix)
107
+static int clear_buffers(struct impl *impl, struct mix *mix)
108
 {
109
    uint32_t i, j;
110
 
111
    for (i = 0; i < mix->n_buffers; i++) {
112
        struct buffer *b = &mix->buffersi;
113
 
114
-       spa_log_debug(this->log, "%p: clear buffer %d", this, i);
115
+       spa_log_debug(impl->log, "%p: clear buffer %d", impl, i);
116
 
117
        for (j = 0; j < b->buffer.n_datas; j++) {
118
            struct spa_data *d = &b->datasj;
119
-           clear_data(this, d);
120
+           clear_data(impl, d);
121
        }
122
        pw_memblock_unref(b->mem);
123
    }
124
@@ -286,13 +274,13 @@
125
    return 0;
126
 }
127
 
128
-static void mix_clear(struct node *this, struct mix *mix)
129
+static void mix_clear(struct impl *impl, struct mix *mix)
130
 {
131
    struct port *port = mix->port;
132
 
133
    if (!mix->valid)
134
        return;
135
-   do_port_use_buffers(this->impl, port->direction, port->id,
136
+   do_port_use_buffers(impl, port->direction, port->id,
137
            mix->id, 0, NULL, 0);
138
    mix->valid = false;
139
 }
140
@@ -301,14 +289,14 @@
141
                 uint32_t id, uint32_t start, uint32_t num,
142
                 const struct spa_pod *filter)
143
 {
144
-   struct node *this = object;
145
+   struct impl *impl = object;
146
    uint8_t buffer1024;
147
    struct spa_pod_dynamic_builder b;
148
    struct spa_result_node_params result;
149
    uint32_t count = 0;
150
    bool found = false;
151
 
152
-   spa_return_val_if_fail(this != NULL, -EINVAL);
153
+   spa_return_val_if_fail(impl != NULL, -EINVAL);
154
    spa_return_val_if_fail(num != 0, -EINVAL);
155
 
156
    result.id = id;
157
@@ -318,10 +306,10 @@
158
        struct spa_pod *param;
159
 
160
        result.index = result.next++;
161
-       if (result.index >= this->params.n_params)
162
+       if (result.index >= impl->params.n_params)
163
            break;
164
 
165
-       param = this->params.paramsresult.index;
166
+       param = impl->params.paramsresult.index;
167
 
168
        if (param == NULL || !spa_pod_is_object_id(param, id))
169
            continue;
170
@@ -333,8 +321,8 @@
171
 
172
        spa_pod_dynamic_builder_init(&b, buffer, sizeof(buffer), 4096);
173
        if (spa_pod_filter(&b.b, &result.param, param, filter) == 0) {
174
-           pw_log_debug("%p: %d param %u", this, seq, result.index);
175
-           spa_node_emit_result(&this->hooks, seq, 0, SPA_RESULT_TYPE_NODE_PARAMS, &result);
176
+           pw_log_debug("%p: %d param %u", impl, seq, result.index);
177
+           spa_node_emit_result(&impl->hooks, seq, 0, SPA_RESULT_TYPE_NODE_PARAMS, &result);
178
            count++;
179
        }
180
        spa_pod_dynamic_builder_clean(&b);
181
@@ -348,20 +336,19 @@
182
 static int impl_node_set_param(void *object, uint32_t id, uint32_t flags,
183
                   const struct spa_pod *param)
184
 {
185
-   struct node *this = object;
186
+   struct impl *impl = object;
187
 
188
-   spa_return_val_if_fail(this != NULL, -EINVAL);
189
+   spa_return_val_if_fail(impl != NULL, -EINVAL);
190
 
191
-   if (this->resource == NULL)
192
+   if (impl->resource == NULL)
193
        return param == NULL ? 0 : -EIO;
194
 
195
-   return pw_client_node_resource_set_param(this->resource, id, flags, param);
196
+   return pw_client_node_resource_set_param(impl->resource, id, flags, param);
197
 }
198
 
199
 static int impl_node_set_io(void *object, uint32_t id, void *data, size_t size)
200
 {
201
pipewire-0.3.70.tar.gz/src/modules/module-client-node/remote-node.c -> pipewire-0.3.71.tar.gz/src/modules/module-client-node/remote-node.c Changed
201
 
1
@@ -47,6 +47,8 @@
2
 
3
 struct node_data {
4
    struct pw_context *context;
5
+   struct pw_loop *data_loop;
6
+   struct spa_system *data_system;
7
 
8
    struct pw_mempool *pool;
9
 
10
@@ -69,6 +71,9 @@
11
    struct spa_hook proxy_client_node_listener;
12
 
13
    struct spa_list links;
14
+
15
+   struct spa_io_clock *clock;
16
+   struct spa_io_position *position;
17
 };
18
 
19
 struct link {
20
@@ -77,7 +82,6 @@
21
    struct pw_memmap *map;
22
    struct pw_node_target target;
23
    uint32_t node_id;
24
-   int signalfd;
25
 };
26
 
27
 /** \endcond */
28
@@ -105,12 +109,11 @@
29
 
30
 static void clear_link(struct node_data *data, struct link *link)
31
 {
32
-   struct pw_context *context = data->context;
33
    pw_log_debug("link %p", link);
34
-   pw_loop_invoke(context->data_loop,
35
+   pw_loop_invoke(data->data_loop,
36
        do_deactivate_link, SPA_ID_INVALID, NULL, 0, true, link);
37
    pw_memmap_free(link->map);
38
-   spa_system_close(context->data_system, link->signalfd);
39
+   spa_system_close(link->target.system, link->target.fd);
40
    spa_list_remove(&link->link);
41
    free(link);
42
 }
43
@@ -137,7 +140,7 @@
44
    pw_memmap_free(data->activation);
45
    data->node->rt.activation = data->node->activation->map->ptr;
46
 
47
-   spa_system_close(data->context->data_system, data->rtwritefd);
48
+   spa_system_close(data->data_system, data->rtwritefd);
49
    data->have_transport = false;
50
 }
51
 
52
@@ -166,7 +169,7 @@
53
 {
54
    if (mix->active) {
55
        pw_log_debug("node %p: mix %p deactivate", data, mix);
56
-       pw_loop_invoke(data->context->data_loop,
57
+       pw_loop_invoke(data->data_loop,
58
                        do_deactivate_mix, SPA_ID_INVALID, NULL, 0, true, mix);
59
        mix->active = false;
60
    }
61
@@ -188,7 +191,7 @@
62
 {
63
    if (!mix->active) {
64
        pw_log_debug("node %p: mix %p activate", data, mix);
65
-       pw_loop_invoke(data->context->data_loop,
66
+       pw_loop_invoke(data->data_loop,
67
                        do_activate_mix, SPA_ID_INVALID, NULL, 0, false, mix);
68
        mix->active = true;
69
    }
70
@@ -243,6 +246,7 @@
71
            int readfd, int writefd, uint32_t mem_id, uint32_t offset, uint32_t size)
72
 {
73
    struct node_data *data = _data;
74
+   struct pw_impl_node *node = data->node;
75
    struct pw_proxy *proxy = (struct pw_proxy*)data->client_node;
76
 
77
    clean_transport(data);
78
@@ -254,18 +258,18 @@
79
        return -errno;
80
    }
81
 
82
-   data->node->rt.activation = data->activation->ptr;
83
+   node->rt.activation = data->activation->ptr;
84
 
85
    pw_log_debug("remote-node %p: fds:%d %d node:%u activation:%p",
86
        proxy, readfd, writefd, data->remote_id, data->activation->ptr);
87
 
88
    data->rtwritefd = writefd;
89
-   spa_system_close(data->context->data_system, data->node->source.fd);
90
-   data->node->source.fd = readfd;
91
+   spa_system_close(data->data_system, node->source.fd);
92
+   node->source.fd = readfd;
93
 
94
    data->have_transport = true;
95
 
96
-   if (data->node->active)
97
+   if (node->active)
98
        pw_client_node_set_active(data->client_node, true);
99
 
100
    return 0;
101
@@ -466,6 +470,17 @@
102
    pw_log_debug("node %p: set io %s %p", proxy,
103
            spa_debug_type_find_name(spa_type_io, id), ptr);
104
 
105
+   switch(id) {
106
+   case SPA_IO_Clock:
107
+       data->clock = size >= sizeof(*data->clock) ? ptr : NULL;
108
+       break;
109
+   case SPA_IO_Position:
110
+       data->position = size >= sizeof(*data->position) ? ptr : NULL;
111
+       break;
112
+   }
113
+   data->node->driving = data->clock && data->position &&
114
+       data->position->clock.id == data->clock->id;
115
+
116
    res =  spa_node_set_io(data->node->node, id, ptr, size);
117
 
118
    pw_memmap_free(old);
119
@@ -479,7 +494,9 @@
120
 
121
 static int client_node_event(void *data, const struct spa_event *event)
122
 {
123
-   pw_log_warn("unhandled node event %d", SPA_EVENT_TYPE(event));
124
+   uint32_t id = SPA_NODE_EVENT_ID(event);
125
+   pw_log_warn("unhandled node event %d (%s)", id,
126
+           spa_debug_type_find_name(spa_type_node_event_id, id));
127
    return -ENOTSUP;
128
 }
129
 
130
@@ -488,11 +505,13 @@
131
    struct node_data *data = _data;
132
    struct pw_proxy *proxy = (struct pw_proxy*)data->client_node;
133
    int res;
134
+   uint32_t id = SPA_NODE_COMMAND_ID(command);
135
 
136
-   switch (SPA_NODE_COMMAND_ID(command)) {
137
-   case SPA_NODE_COMMAND_Pause:
138
-       pw_log_debug("node %p: pause", proxy);
139
+   pw_log_debug("%p: got command %d (%s)", proxy, id,
140
+           spa_debug_type_find_name(spa_type_node_command_id, id));
141
 
142
+   switch (id) {
143
+   case SPA_NODE_COMMAND_Pause:
144
        if ((res = pw_impl_node_set_state(data->node, PW_NODE_STATE_IDLE)) < 0) {
145
            pw_log_warn("node %p: pause failed", proxy);
146
            pw_proxy_error(proxy, res, "pause failed");
147
@@ -500,8 +519,6 @@
148
 
149
        break;
150
    case SPA_NODE_COMMAND_Start:
151
-       pw_log_debug("node %p: start", proxy);
152
-
153
        if ((res = pw_impl_node_set_state(data->node, PW_NODE_STATE_RUNNING)) < 0) {
154
            pw_log_warn("node %p: start failed", proxy);
155
            pw_proxy_error(proxy, res, "start failed");
156
@@ -509,7 +526,6 @@
157
        break;
158
 
159
    case SPA_NODE_COMMAND_Suspend:
160
-       pw_log_debug("node %p: suspend", proxy);
161
        if ((res = pw_impl_node_set_state(data->node, PW_NODE_STATE_SUSPENDED)) < 0) {
162
            pw_log_warn("node %p: suspend failed", proxy);
163
            pw_proxy_error(proxy, res, "suspend failed");
164
@@ -519,9 +535,11 @@
165
        res = pw_impl_node_send_command(data->node, command);
166
        break;
167
    default:
168
-       pw_log_warn("unhandled node command %d", SPA_NODE_COMMAND_ID(command));
169
+       pw_log_warn("unhandled node command %d (%s)", id,
170
+               spa_debug_type_find_name(spa_type_node_command_id, id));
171
        res = -ENOTSUP;
172
-       pw_proxy_errorf(proxy, res, "command %d not supported", SPA_NODE_COMMAND_ID(command));
173
+       pw_proxy_errorf(proxy, res, "command %d (%s) not supported", id,
174
+               spa_debug_type_find_name(spa_type_node_command_id, id));
175
    }
176
    return res;
177
 }
178
@@ -833,18 +851,6 @@
179
    return res;
180
 }
181
 
182
-static int link_signal_func(void *user_data)
183
-{
184
-   struct link *link = user_data;
185
-   struct spa_system *data_system = link->data->context->data_system;
186
-
187
-   pw_log_trace_fp("link %p: signal %p", link, link->target.activation);
188
-   if (SPA_UNLIKELY(spa_system_eventfd_write(data_system, link->signalfd, 1) < 0))
189
-       pw_log_warn("link %p: write failed %m", link);
190
-
191
-   return 0;
192
-}
193
-
194
 static int
195
 do_activate_link(struct spa_loop *loop,
196
                 bool async, uint32_t seq, const void *data, size_t size, void *user_data)
197
@@ -875,7 +881,7 @@
198
    if (data->remote_id == node_id) {
199
        pw_log_debug("node %p: our activation %u: %u %u %u", node, node_id,
200
                memid, offset, size);
201
pipewire-0.3.70.tar.gz/src/modules/module-combine-stream.c -> pipewire-0.3.71.tar.gz/src/modules/module-combine-stream.c Changed
201
 
1
@@ -24,6 +24,7 @@
2
 #include <spa/pod/builder.h>
3
 #include <spa/param/audio/format-utils.h>
4
 #include <spa/param/audio/raw.h>
5
+#include <spa/param/latency-utils.h>
6
 
7
 #include <pipewire/impl.h>
8
 #include <pipewire/i18n.h>
9
@@ -44,6 +45,7 @@
10
  * - `node.name`: a unique name for the stream
11
  * - `node.description`: a human readable name for the stream
12
  * - `combine.mode` = capture | playback | sink | source, default sink
13
+ * - `combine.latency-compensate`: use delay buffers to match stream latencies
14
  * - `combine.props = {}`: properties to be passed to the sink/source
15
  * - `stream.props = {}`: properties to be passed to the streams
16
  * - `stream.rules = {}`: rules for matching streams, use create-stream actions
17
@@ -79,6 +81,7 @@
18
  *         combine.mode = sink
19
  *         node.name = "combine_sink"
20
  *         node.description = "My Combine Sink"
21
+ *         combine.latency-compensate = false
22
  *         combine.props = {
23
  *             audio.position =  FL FR 
24
  *         }
25
@@ -118,6 +121,7 @@
26
  *         combine.mode = sink
27
  *         node.name = "combine_sink_5_1"
28
  *         node.description = "My 5.1 Combine Sink"
29
+ *         combine.latency-compensate = false
30
  *         combine.props = {
31
  *             audio.position =  FL FR FC LFE SL SR 
32
  *         }
33
@@ -213,6 +217,8 @@
34
            "( stream.props=<properties> ) "                    \
35
            "( stream.rules=<properties> ) "
36
 
37
+#define DELAYBUF_MAX_SIZE  (20 * sizeof(float) * 96000)
38
+
39
 
40
 static const struct spa_dict_item module_props = {
41
    { PW_KEY_MODULE_AUTHOR, "Wim Taymans <wim.taymans@gmail.com>" },
42
@@ -223,6 +229,7 @@
43
 
44
 struct impl {
45
    struct pw_context *context;
46
+   struct pw_loop *main_loop;
47
    struct pw_data_loop *data_loop;
48
 
49
    struct pw_properties *props;
50
@@ -243,6 +250,8 @@
51
    struct pw_registry *registry;
52
    struct spa_hook registry_listener;
53
 
54
+   struct spa_source *update_delay_event;
55
+
56
    struct pw_properties *combine_props;
57
    struct pw_stream *combine;
58
    struct spa_hook combine_listener;
59
@@ -251,14 +260,25 @@
60
 
61
    struct pw_properties *stream_props;
62
 
63
+   struct spa_latency_info latency;
64
+
65
+   int64_t latency_offset;
66
+
67
    struct spa_audio_info_raw info;
68
 
69
    unsigned int do_disconnect:1;
70
+   unsigned int latency_compensate:1;
71
 
72
    struct spa_list streams;
73
    uint32_t n_streams;
74
 };
75
 
76
+struct ringbuffer {
77
+   void *buf;
78
+   uint32_t idx;
79
+   uint32_t size;
80
+};
81
+
82
 struct stream {
83
    uint32_t id;
84
 
85
@@ -269,11 +289,21 @@
86
    struct spa_hook stream_listener;
87
    struct pw_stream_events stream_events;
88
 
89
+   struct spa_latency_info latency;
90
+
91
    struct spa_audio_info_raw info;
92
    uint32_t remapSPA_AUDIO_MAX_CHANNELS;
93
+   uint32_t rate;
94
+
95
+   void *delaybuf;
96
+   struct ringbuffer delaySPA_AUDIO_MAX_CHANNELS;
97
+
98
+   int64_t delay_nsec;     /* for main loop */
99
+   int64_t data_delay_nsec;    /* for data loop */
100
 
101
    unsigned int ready:1;
102
    unsigned int added:1;
103
+   unsigned int have_latency:1;
104
 };
105
 
106
 static uint32_t channel_from_name(const char *name)
107
@@ -316,6 +346,53 @@
108
        parse_position(info, DEFAULT_POSITION, strlen(DEFAULT_POSITION));
109
 }
110
 
111
+static void ringbuffer_init(struct ringbuffer *r, void *buf, uint32_t size)
112
+{
113
+   r->buf = buf;
114
+   r->idx = 0;
115
+   r->size = size;
116
+}
117
+
118
+static void ringbuffer_memcpy(struct ringbuffer *r, void *dst, void *src, uint32_t size)
119
+{
120
+   uint32_t avail;
121
+
122
+   avail = SPA_MIN(size, r->size);
123
+
124
+   /* buf to dst */
125
+   if (dst && avail > 0) {
126
+       spa_ringbuffer_read_data(NULL, r->buf, r->size, r->idx, dst, avail);
127
+       dst = SPA_PTROFF(dst, avail, void);
128
+   }
129
+
130
+   /* src to dst */
131
+   if (size > avail) {
132
+       if (dst)
133
+           memcpy(dst, src, size - avail);
134
+       src = SPA_PTROFF(src, size - avail, void);
135
+   }
136
+
137
+   /* src to buf */
138
+   if (avail > 0) {
139
+       spa_ringbuffer_write_data(NULL, r->buf, r->size, r->idx, src, avail);
140
+       r->idx = (r->idx + avail) % r->size;
141
+   }
142
+}
143
+
144
+static void ringbuffer_copy(struct ringbuffer *dst, struct ringbuffer *src)
145
+{
146
+   uint32_t l0, l1;
147
+
148
+   if (dst->size == 0 || src->size == 0)
149
+       return;
150
+
151
+   l0 = src->size - src->idx;
152
+   l1 = src->idx;
153
+
154
+   ringbuffer_memcpy(dst, NULL, SPA_PTROFF(src->buf, src->idx, void), l0);
155
+   ringbuffer_memcpy(dst, NULL, src->buf, l1);
156
+}
157
+
158
 static struct stream *find_stream(struct impl *impl, uint32_t id)
159
 {
160
    struct stream *s;
161
@@ -325,6 +402,192 @@
162
    return NULL;
163
 }
164
 
165
+static enum pw_direction get_combine_direction(struct impl *impl)
166
+{
167
+   if (impl->mode == MODE_SINK || impl->mode == MODE_CAPTURE)
168
+       return PW_DIRECTION_INPUT;
169
+   else
170
+       return PW_DIRECTION_OUTPUT;
171
+}
172
+
173
+static void apply_latency_offset(struct spa_latency_info *latency, int64_t offset)
174
+{
175
+   latency->min_ns += SPA_MAX(offset, -(int64_t)latency->min_ns);
176
+   latency->max_ns += SPA_MAX(offset, -(int64_t)latency->max_ns);
177
+}
178
+
179
+static int64_t get_stream_delay(struct stream *s)
180
+{
181
+   struct pw_time t;
182
+
183
+   if (pw_stream_get_time_n(s->stream, &t, sizeof(t)) < 0 ||
184
+           t.rate.denom == 0)
185
+       return INT64_MIN;
186
+
187
+   return t.delay * SPA_NSEC_PER_SEC * t.rate.num / t.rate.denom;
188
+}
189
+
190
+static void update_latency(struct impl *impl)
191
+{
192
+   struct spa_latency_info latency;
193
+   struct stream *s;
194
+
195
+   if (impl->combine == NULL)
196
+       return;
197
+
198
+   if (!impl->latency_compensate) {
199
+       spa_latency_info_combine_start(&latency, get_combine_direction(impl));
200
+
201
pipewire-0.3.70.tar.gz/src/modules/module-echo-cancel.c -> pipewire-0.3.71.tar.gz/src/modules/module-echo-cancel.c Changed
30
 
1
@@ -639,7 +639,6 @@
2
 {
3
    struct spa_pod_parser prs;
4
    struct spa_pod_frame f;
5
-   int changed = 0;
6
 
7
    spa_pod_parser_pod(&prs, params);
8
    if (spa_pod_parser_push_struct(&prs, &f) < 0)
9
@@ -668,7 +667,6 @@
10
        if (spa_streq(name, "debug.aec.wav-path")) {
11
            spa_scnprintf(impl->wav_path,
12
                sizeof(impl->wav_path), "%s", value);
13
-           changed++;
14
        }
15
    }
16
    spa_audio_aec_set_params(impl->aec, params);
17
@@ -1429,9 +1427,9 @@
18
 
19
        res = spa_audio_aec_init(impl->aec, &aec_props->dict, &info);
20
 
21
-       impl->rec_info.channels = info.channels;
22
-       impl->out_info.channels = info.channels;
23
-       impl->play_info.channels = info.channels;
24
+       impl->rec_info = info;
25
+       impl->out_info = info;
26
+       impl->play_info = info;
27
    }
28
 
29
    pw_properties_free(aec_props);
30
pipewire-0.3.70.tar.gz/src/modules/module-filter-chain/builtin_plugin.c -> pipewire-0.3.71.tar.gz/src/modules/module-filter-chain/builtin_plugin.c Changed
53
 
1
@@ -9,6 +9,7 @@
2
 #ifdef HAVE_SNDFILE
3
 #include <sndfile.h>
4
 #endif
5
+#include <unistd.h>
6
 
7
 #include <spa/utils/json.h>
8
 #include <spa/utils/result.h>
9
@@ -686,10 +687,11 @@
10
 
11
    int diff = INT_MAX;
12
    uint32_t best = 0, i;
13
+   float *samples = NULL;
14
 
15
    for (i = 0; i < MAX_RATES && filenamesi && filenamesi0; i++) {
16
        fsi = sf_open(filenamesi, SFM_READ, &infosi);
17
-       if (!fsi)
18
+       if (fsi == NULL)
19
            continue;
20
 
21
        if (labs((long)infosi.samplerate - (long)*rate) < diff) {
22
@@ -698,13 +700,24 @@
23
            pw_log_debug("new closest match: %d", infosi.samplerate);
24
        }
25
    }
26
-
27
-   pw_log_debug("loading %s", filenamesbest);
28
-   float *samples = read_samples_from_sf(fsbest, infosbest, gain, delay,
29
-       offset, length, channel, rate, n_samples);
30
-
31
+   if (fsbest != NULL) {
32
+       pw_log_info("loading best rate:%u %s", infosbest.samplerate, filenamesbest);
33
+       samples = read_samples_from_sf(fsbest, infosbest, gain, delay,
34
+           offset, length, channel, rate, n_samples);
35
+   } else {
36
+       char bufPATH_MAX;
37
+       pw_log_error("Can't open any sample file (CWD %s):",
38
+               getcwd(buf, sizeof(buf)));
39
+       for (i = 0; i < MAX_RATES && filenamesi && filenamesi0; i++) {
40
+           fsi = sf_open(filenamesi, SFM_READ, &infosi);
41
+           if (fsi == NULL)
42
+               pw_log_error(" failed file %s: %s", filenamesi, sf_strerror(fsi));
43
+           else
44
+               pw_log_warn(" unexpectedly opened file %s", filenamesi);
45
+       }
46
+   }
47
    for (i = 0; i < MAX_RATES; i++)
48
-       if (fsi)
49
+       if (fsi != NULL)
50
            sf_close(fsi);
51
 
52
    return samples;
53
pipewire-0.3.71.tar.gz/src/modules/module-jack-tunnel Added
2
 
1
+(directory)
2
pipewire-0.3.71.tar.gz/src/modules/module-jack-tunnel.c Added
201
 
1
@@ -0,0 +1,1155 @@
2
+/* PipeWire */
3
+/* SPDX-FileCopyrightText: Copyright © 2021 Wim Taymans */
4
+/* SPDX-License-Identifier: MIT */
5
+
6
+#include <string.h>
7
+#include <stdio.h>
8
+#include <errno.h>
9
+#include <sys/types.h>
10
+#include <sys/stat.h>
11
+#include <fcntl.h>
12
+#include <unistd.h>
13
+#include <stdlib.h>
14
+#include <signal.h>
15
+#include <limits.h>
16
+#include <math.h>
17
+
18
+#include "config.h"
19
+
20
+#include <spa/utils/result.h>
21
+#include <spa/utils/string.h>
22
+#include <spa/utils/json.h>
23
+#include <spa/debug/types.h>
24
+#include <spa/pod/builder.h>
25
+#include <spa/param/audio/format-utils.h>
26
+#include <spa/param/latency-utils.h>
27
+#include <spa/param/audio/raw.h>
28
+
29
+#include <pipewire/impl.h>
30
+#include <pipewire/i18n.h>
31
+#include <pipewire/private.h>
32
+
33
+#include "module-jack-tunnel/weakjack.h"
34
+
35
+/** \page page_module_jack_tunnel PipeWire Module: JACK Tunnel
36
+ *
37
+ * The jack-tunnel module provides a source or sink that tunnels all audio to
38
+ * a JACK server.
39
+ *
40
+ * This module is usually used together with \ref page_module_jackdbus_detect that will
41
+ * automatically load the tunnel with the right parameters based on dbus
42
+ * information.
43
+ *
44
+ * ## Module Options
45
+ *
46
+ * - `jack.library`: the libjack to load, by default libjack.so.0 is searched in
47
+ *         JACK_PATH directories and then some standard library paths.
48
+ *         Can be an absolute path.
49
+ * - `jack.server`: the name of the JACK server to tunnel to.
50
+ * - `jack.client-name`: the name of the JACK client.
51
+ * - `jack.connect`: if jack ports should be connected automatically. Can also be
52
+ *                   placed per stream.
53
+ * - `tunnel.mode`: the tunnel mode, sink|source|duplex, default duplex
54
+ * - `midi.ports`: the number of midi ports. Can also be added to the stream props.
55
+ * - `source.props`: Extra properties for the source filter.
56
+ * - `sink.props`: Extra properties for the sink filter.
57
+ *
58
+ * ## General options
59
+ *
60
+ * Options with well-known behavior.
61
+ *
62
+ * - \ref PW_KEY_REMOTE_NAME
63
+ * - \ref PW_KEY_AUDIO_CHANNELS
64
+ * - \ref SPA_KEY_AUDIO_POSITION
65
+ * - \ref PW_KEY_NODE_NAME
66
+ * - \ref PW_KEY_NODE_DESCRIPTION
67
+ * - \ref PW_KEY_NODE_GROUP
68
+ * - \ref PW_KEY_NODE_VIRTUAL
69
+ * - \ref PW_KEY_MEDIA_CLASS
70
+ * - \ref PW_KEY_TARGET_OBJECT to specify the remote node.name or serial.id to link to
71
+ *
72
+ * ## Example configuration of a duplex sink/source
73
+ *
74
+ *\code{.unparsed}
75
+ * context.modules = 
76
+ * {   name = libpipewire-module-jack-tunnel
77
+ *     args = {
78
+ *         #jack.library     = libjack.so.0
79
+ *         #jack.server      = null
80
+ *         #jack.client-name = PipeWire
81
+ *         #jack.connect     = true
82
+ *         #tunnel.mode      = duplex
83
+ *         #midi.ports       = 0
84
+ *         #audio.channels   = 2
85
+ *         #audio.position   =  FL FR 
86
+ *         source.props = {
87
+ *             # extra sink properties
88
+ *         }
89
+ *         sink.props = {
90
+ *             # extra sink properties
91
+ *         }
92
+ *     }
93
+ * }
94
+ * 
95
+ *\endcode
96
+ */
97
+
98
+#define NAME "jack-tunnel"
99
+
100
+PW_LOG_TOPIC_STATIC(mod_topic, "mod." NAME);
101
+#define PW_LOG_TOPIC_DEFAULT mod_topic
102
+
103
+#define MAX_PORTS  128
104
+
105
+#define DEFAULT_CLIENT_NAME    "PipeWire"
106
+#define DEFAULT_CHANNELS   2
107
+#define DEFAULT_POSITION   " FL FR "
108
+#define DEFAULT_MIDI_PORTS 1
109
+
110
+#define MODULE_USAGE   "( remote.name=<remote>  "              \
111
+           "( jack.library=<jack library path> ) "         \
112
+           "( jack.server=<server name> ) "            \
113
+           "( jack.client-name=<name of the JACK client>  "    \
114
+           "( jack.connect=<bool, autoconnect ports>  "        \
115
+           "( tunnel.mode=<sink|source|duplex>  "          \
116
+           "( midi.ports=<number of midi ports>  "     \
117
+           "( audio.channels=<number of channels>  "       \
118
+           "( audio.position=<channel map>  "          \
119
+           "( source.props=<properties> ) "            \
120
+           "( sink.props=<properties> ) "
121
+
122
+
123
+static const struct spa_dict_item module_props = {
124
+   { PW_KEY_MODULE_AUTHOR, "Wim Taymans <wim.taymans@gmail.com>" },
125
+   { PW_KEY_MODULE_DESCRIPTION, "Create a JACK tunnel" },
126
+   { PW_KEY_MODULE_USAGE, MODULE_USAGE },
127
+   { PW_KEY_MODULE_VERSION, PACKAGE_VERSION },
128
+};
129
+
130
+static struct weakjack jack;
131
+
132
+struct port {
133
+   jack_port_t *jack_port;
134
+
135
+   enum spa_direction direction;
136
+   struct spa_latency_info latency2;
137
+   bool latency_changed2;
138
+   unsigned int is_midi:1;
139
+};
140
+
141
+struct volume {
142
+   bool mute;
143
+   uint32_t n_volumes;
144
+   float volumesSPA_AUDIO_MAX_CHANNELS;
145
+};
146
+
147
+struct stream {
148
+   struct impl *impl;
149
+
150
+   enum spa_direction direction;
151
+   struct pw_properties *props;
152
+   struct pw_filter *filter;
153
+   struct spa_hook listener;
154
+   struct spa_audio_info_raw info;
155
+   uint32_t n_midi;
156
+   uint32_t n_ports;
157
+   struct port *portsMAX_PORTS;
158
+   struct volume volume;
159
+
160
+   unsigned int running:1;
161
+   unsigned int connect:1;
162
+};
163
+
164
+struct impl {
165
+   struct pw_context *context;
166
+   struct pw_loop *main_loop;
167
+   struct spa_system *system;
168
+
169
+#define MODE_SINK  (1<<0)
170
+#define MODE_SOURCE    (1<<1)
171
+#define MODE_DUPLEX    (MODE_SINK|MODE_SOURCE)
172
+   uint32_t mode;
173
+   struct pw_properties *props;
174
+
175
+   struct pw_impl_module *module;
176
+
177
+   struct spa_hook module_listener;
178
+
179
+   struct pw_core *core;
180
+   struct spa_hook core_proxy_listener;
181
+   struct spa_hook core_listener;
182
+
183
+   struct spa_io_position *position;
184
+
185
+   struct stream source;
186
+   struct stream sink;
187
+
188
+   uint32_t samplerate;
189
+
190
+   jack_client_t *client;
191
+   jack_nframes_t frame_time;
192
+
193
+   uint32_t pw_xrun;
194
+   uint32_t jack_xrun;
195
+
196
+   unsigned int do_disconnect:1;
197
+   unsigned int done:1;
198
+   unsigned int new_xrun:1;
199
+   unsigned int fix_midi:1;
200
+};
201
pipewire-0.3.71.tar.gz/src/modules/module-jack-tunnel/weakjack.h Added
197
 
1
@@ -0,0 +1,195 @@
2
+/* PipeWire */
3
+/* SPDX-FileCopyrightText: Copyright © 2023 Wim Taymans */
4
+/* SPDX-License-Identifier: MIT */
5
+
6
+#ifndef PIPEWIRE_WEAK_JACK_H
7
+#define PIPEWIRE_WEAK_JACK_H
8
+
9
+#ifdef __cplusplus
10
+extern "C" {
11
+#endif
12
+
13
+#include "config.h"
14
+
15
+#include <dlfcn.h>
16
+
17
+#include <jack/jack.h>
18
+#include <jack/transport.h>
19
+#include <jack/midiport.h>
20
+
21
+struct weakjack {
22
+   jack_nframes_t (*cycle_wait) (jack_client_t* client);
23
+   void (*cycle_signal) (jack_client_t* client, int status);
24
+
25
+   jack_nframes_t (*frame_time) (const jack_client_t *);
26
+   int (*get_cycle_times) (const jack_client_t *client,
27
+           jack_nframes_t *current_frames,
28
+           jack_time_t    *current_usecs,
29
+           jack_time_t    *next_usecs,
30
+           float          *period_usecs);
31
+   jack_transport_state_t (*transport_query) (const jack_client_t *client,
32
+                                             jack_position_t *pos);
33
+
34
+   jack_client_t * (*client_open) (const char *client_name,
35
+                                  jack_options_t options,
36
+                                  jack_status_t *status, ...);
37
+   int (*client_close) (jack_client_t *client);
38
+
39
+
40
+   int (*activate) (jack_client_t *client);
41
+   int (*deactivate) (jack_client_t *client);
42
+
43
+   jack_nframes_t (*get_sample_rate) (jack_client_t *);
44
+
45
+   int (*recompute_total_latencies) (jack_client_t *client);
46
+
47
+   jack_port_t * (*port_register) (jack_client_t *client,
48
+           const char *port_name,
49
+           const char *port_type,
50
+           unsigned long flags,
51
+           unsigned long buffer_size);
52
+   int (*port_unregister) (jack_client_t *client, jack_port_t *port);
53
+   void * (*port_get_buffer) (jack_port_t *port, jack_nframes_t);
54
+   const char * (*port_name) (const jack_port_t *port);
55
+
56
+   void (*port_get_latency_range) (jack_port_t *port,
57
+           jack_latency_callback_mode_t mode,
58
+           jack_latency_range_t *range);
59
+   void (*port_set_latency_range) (jack_port_t *port,
60
+           jack_latency_callback_mode_t mode,
61
+           jack_latency_range_t *range);
62
+
63
+
64
+   int (*connect) (jack_client_t *client,
65
+           const char *source_port,
66
+           const char *destination_port);
67
+   int (*disconnect) (jack_client_t *client,
68
+           const char *source_port,
69
+           const char *destination_port);
70
+
71
+   const char ** (*get_ports) (jack_client_t *client,
72
+           const char *port_name_pattern,
73
+           const char *type_name_pattern,
74
+           unsigned long flags);
75
+   void (*free) (void* ptr);
76
+
77
+   int (*set_process_thread) (jack_client_t* client,
78
+           JackThreadCallback thread_callback, void *arg);
79
+   int (*set_xrun_callback) (jack_client_t *client,
80
+           JackXRunCallback xrun_callback, void *arg);
81
+   void (*on_info_shutdown) (jack_client_t *client,
82
+           JackInfoShutdownCallback shutdown_callback, void *arg);
83
+   int (*set_latency_callback) (jack_client_t *client,
84
+           JackLatencyCallback latency_callback, void *arg);
85
+
86
+   void (*midi_clear_buffer) (void *port_buffer);
87
+   int (*midi_event_write) (void *port_buffer,
88
+           jack_nframes_t time,
89
+           const jack_midi_data_t *data,
90
+           size_t data_size);
91
+   uint32_t (*midi_get_event_count) (void* port_buffer);
92
+   int (*midi_event_get) (jack_midi_event_t *event, void *port_buffer,
93
+           uint32_t event_index);
94
+
95
+};
96
+
97
+
98
+static inline int weakjack_load_by_path(struct weakjack *jack, const char *path)
99
+{
100
+   void *hnd;
101
+
102
+   hnd = dlopen(path, RTLD_NOW);
103
+   if (hnd == NULL)
104
+       return -errno;
105
+
106
+   pw_log_info("opened libjack: %s", path);
107
+
108
+#define LOAD_SYM(name) ({                  \
109
+   if ((jack->name =  dlsym(hnd, "jack_"#name )) == NULL)  \
110
+       return -ENOSYS;                 \
111
+})
112
+   spa_zero(*jack);
113
+   LOAD_SYM(cycle_wait);
114
+   LOAD_SYM(cycle_signal);
115
+   LOAD_SYM(frame_time);
116
+   LOAD_SYM(get_cycle_times);
117
+   LOAD_SYM(transport_query);
118
+
119
+   LOAD_SYM(client_open);
120
+   LOAD_SYM(client_close);
121
+
122
+   LOAD_SYM(activate);
123
+   LOAD_SYM(deactivate);
124
+
125
+   LOAD_SYM(get_sample_rate);
126
+
127
+   LOAD_SYM(recompute_total_latencies);
128
+
129
+   LOAD_SYM(port_register);
130
+   LOAD_SYM(port_unregister);
131
+   LOAD_SYM(port_get_buffer);
132
+   LOAD_SYM(port_name);
133
+
134
+   LOAD_SYM(port_get_latency_range);
135
+   LOAD_SYM(port_set_latency_range);
136
+
137
+   LOAD_SYM(connect);
138
+   LOAD_SYM(disconnect);
139
+
140
+   LOAD_SYM(get_ports);
141
+   LOAD_SYM(free);
142
+
143
+   LOAD_SYM(set_process_thread);
144
+   LOAD_SYM(set_xrun_callback);
145
+   LOAD_SYM(on_info_shutdown);
146
+   LOAD_SYM(set_latency_callback);
147
+
148
+   LOAD_SYM(midi_clear_buffer);
149
+   LOAD_SYM(midi_event_write);
150
+   LOAD_SYM(midi_get_event_count);
151
+   LOAD_SYM(midi_event_get);
152
+#undef LOAD_SYM
153
+
154
+   return 0;
155
+}
156
+
157
+static inline int weakjack_load(struct weakjack *jack, const char *lib)
158
+{
159
+   int res = -ENOENT;
160
+
161
+   if (lib0 != '/') {
162
+       const char *search_dirs, *p, *state = NULL;
163
+       char pathPATH_MAX;
164
+       size_t len;
165
+
166
+       search_dirs = getenv("LIBJACK_PATH");
167
+       if (!search_dirs)
168
+           search_dirs = PREFIX "/lib64/:" PREFIX "/lib/:"
169
+               "/usr/lib64/:/usr/lib/:" LIBDIR;
170
+
171
+       while ((p = pw_split_walk(search_dirs, ":", &len, &state))) {
172
+           int pathlen;
173
+
174
+           if (len >= sizeof(path)) {
175
+               res = -ENAMETOOLONG;
176
+               continue;
177
+           }
178
+           pathlen = snprintf(path, sizeof(path), "%.*s/%s", (int) len, p, lib);
179
+           if (pathlen < 0 || (size_t) pathlen >= sizeof(path)) {
180
+               res = -ENAMETOOLONG;
181
+               continue;
182
+           }
183
+           if ((res = weakjack_load_by_path(jack, path)) == 0)
184
+               break;
185
+       }
186
+   } else {
187
+       res = weakjack_load_by_path(jack, lib);
188
+   }
189
+   return res;
190
+}
191
+
192
+#ifdef __cplusplus
193
+}
194
+#endif
195
+
196
+#endif /* PIPEWIRE_WEAK_JACK_H */
197
pipewire-0.3.71.tar.gz/src/modules/module-jackdbus-detect.c Added
201
 
1
@@ -0,0 +1,394 @@
2
+/* PipeWire */
3
+/* SPDX-FileCopyrightText: Copyright © 2016 Wim Taymans <wim.taymans@gmail.com> */
4
+/* SPDX-FileCopyrightText: Copyright © 2019 Red Hat Inc. */
5
+/* SPDX-License-Identifier: MIT */
6
+
7
+#include <string.h>
8
+#include <stdio.h>
9
+#include <errno.h>
10
+#include <sys/types.h>
11
+#include <sys/stat.h>
12
+#include <fcntl.h>
13
+#include <unistd.h>
14
+
15
+#include "config.h"
16
+
17
+#include <dbus/dbus.h>
18
+
19
+#include <spa/utils/string.h>
20
+#include <spa/utils/result.h>
21
+#include <spa/support/dbus.h>
22
+
23
+#include "pipewire/context.h"
24
+#include "pipewire/impl-client.h"
25
+#include "pipewire/log.h"
26
+#include "pipewire/module.h"
27
+#include "pipewire/utils.h"
28
+
29
+/** \page page_module_jackdbus_detect PipeWire Module: JACK DBus detect
30
+ *
31
+ * Automaticall creates a sink/source when a jackdbus server is started
32
+ * and connect to JACK.
33
+ *
34
+ * ## Module Options
35
+ *
36
+ * There are no module-specific options, all arguments are passed to
37
+ * \ref page_module_jack_tunnel.
38
+ *
39
+ * ## Example configuration
40
+ *\code{.unparsed}
41
+ * context.modules = 
42
+ *  {   name = libpipewire-jackdbus-detect
43
+ *      args {
44
+ *         #jack.server    = null
45
+ *         #tunnel.mode    = duplex
46
+ *         #audio.channels = 2
47
+ *         #audio.position =  FL FR 
48
+ *         source.props = {
49
+ *             # extra sink properties
50
+ *         }
51
+ *         sink.props = {
52
+ *             # extra sink properties
53
+ *         }
54
+ *      }
55
+ *  }
56
+ * 
57
+ *\endcode
58
+ *
59
+ */
60
+
61
+#define NAME "jackdbus-detect"
62
+
63
+#define JACK_SERVICE_NAME "org.jackaudio.service"
64
+#define JACK_INTERFACE_NAME "org.jackaudio.JackControl"
65
+#define JACK_INTERFACE_PATH "/org/jackaudio/Controller"
66
+
67
+PW_LOG_TOPIC_STATIC(mod_topic, "mod." NAME);
68
+#define PW_LOG_TOPIC_DEFAULT mod_topic
69
+
70
+struct impl {
71
+   struct pw_context *context;
72
+   struct pw_properties *properties;
73
+
74
+   struct spa_dbus_connection *conn;
75
+   DBusConnection *bus;
76
+
77
+   struct spa_hook module_listener;
78
+
79
+   DBusPendingCall *pending_call;
80
+   bool is_started;
81
+
82
+   struct pw_impl_module *jack_tunnel;
83
+   struct spa_hook tunnel_listener;
84
+};
85
+
86
+static void tunnelmodule_destroy(void *data)
87
+{
88
+   struct impl *impl = data;
89
+   spa_hook_remove(&impl->tunnel_listener);
90
+   impl->jack_tunnel = NULL;
91
+}
92
+
93
+static const struct pw_impl_module_events tunnelmodule_events = {
94
+   PW_VERSION_IMPL_MODULE_EVENTS,
95
+   .destroy = tunnelmodule_destroy,
96
+};
97
+
98
+static int load_jack_tunnel(struct impl *impl)
99
+{
100
+   FILE *f;
101
+   char *args;
102
+   size_t size;
103
+   int res = 0;
104
+
105
+   if ((f = open_memstream(&args, &size)) == NULL) {
106
+       res = -errno;
107
+       pw_log_error("Can't open memstream: %m");
108
+       goto done;
109
+   }
110
+
111
+   fprintf(f, "{");
112
+   if (impl->properties != NULL)
113
+       pw_properties_serialize_dict(f, &impl->properties->dict, 0);
114
+   fprintf(f, " }");
115
+   fclose(f);
116
+
117
+   pw_log_info("loading module args:'%s'", args);
118
+   impl->jack_tunnel = pw_context_load_module(impl->context,
119
+           "libpipewire-module-jack-tunnel",
120
+           args, NULL);
121
+   free(args);
122
+
123
+   if (impl->jack_tunnel == NULL) {
124
+       res = -errno;
125
+       pw_log_error("Can't create tunnel: %m");
126
+       goto done;
127
+   }
128
+
129
+   pw_impl_module_add_listener(impl->jack_tunnel,
130
+           &impl->tunnel_listener, &tunnelmodule_events, impl);
131
+done:
132
+   return res;
133
+}
134
+
135
+static void unload_jack_tunnel(struct impl *impl)
136
+{
137
+   if (impl->jack_tunnel) {
138
+       pw_impl_module_destroy(impl->jack_tunnel);
139
+       impl->jack_tunnel = NULL;
140
+   }
141
+}
142
+static void set_started(struct impl *impl, bool started)
143
+{
144
+   if (impl->is_started != started) {
145
+       pw_log_info("New state %d", started);
146
+       impl->is_started = started;
147
+       if (started)
148
+           load_jack_tunnel(impl);
149
+       else
150
+           unload_jack_tunnel(impl);
151
+   }
152
+}
153
+
154
+static void impl_free(struct impl *impl)
155
+{
156
+   set_started(impl, false);
157
+
158
+   if (impl->bus)
159
+       dbus_connection_unref(impl->bus);
160
+   spa_dbus_connection_destroy(impl->conn);
161
+
162
+   pw_properties_free(impl->properties);
163
+
164
+   free(impl);
165
+}
166
+
167
+static void module_destroy(void *data)
168
+{
169
+   struct impl *impl = data;
170
+   spa_hook_remove(&impl->module_listener);
171
+   impl_free(impl);
172
+}
173
+
174
+static const struct pw_impl_module_events module_events = {
175
+   PW_VERSION_IMPL_MODULE_EVENTS,
176
+   .destroy = module_destroy,
177
+};
178
+
179
+static void set_pending_call(struct impl *impl, DBusPendingCall *pending)
180
+{
181
+   if (impl->pending_call != NULL) {
182
+       dbus_pending_call_cancel(impl->pending_call);
183
+       dbus_pending_call_unref(impl->pending_call);
184
+   }
185
+   impl->pending_call = pending;
186
+}
187
+
188
+static void on_is_started_received(DBusPendingCall *pending,
189
+                  void *user_data)
190
+{
191
+   struct impl *impl = user_data;
192
+   DBusMessage *m;
193
+   DBusError error;
194
+   dbus_bool_t started = false;
195
+
196
+   m = dbus_pending_call_steal_reply(pending);
197
+   dbus_pending_call_unref(pending);
198
+   impl->pending_call = NULL;
199
+
200
+   dbus_error_init(&error);
201
pipewire-0.3.70.tar.gz/src/modules/module-pipe-tunnel.c -> pipewire-0.3.71.tar.gz/src/modules/module-pipe-tunnel.c Changed
33
 
1
@@ -43,16 +43,16 @@
2
  * - `stream.props`: Extra properties for the local stream.
3
  *
4
  * When `tunnel.mode` is `capture`, a capture stream on the default source is
5
- * created. Samples read from the pipe will be the contents of the captured source.
6
+ * created. The samples captured from the source will be written to the pipe.
7
  *
8
- * When `tunnel.mode` is `sink`, a sink node is created. Samples read from the
9
- * pipe will be the samples played on the sink.
10
+ * When `tunnel.mode` is `sink`, a sink node is created. Samples played on the
11
+ * sink will be written to the pipe.
12
  *
13
  * When `tunnel.mode` is `playback`, a playback stream on the default sink is
14
- * created. Samples written to the pipe will be played on the sink.
15
+ * created. The samples read from the pipe will be played on the sink.
16
  *
17
- * When `tunnel.mode` is `source`, a source node is created. Samples written to
18
- * the pipe will be made available to streams connected to the source.
19
+ * When `tunnel.mode` is `source`, a source node is created. Samples read from
20
+ * the the pipe will be made available on the source.
21
  *
22
  * When `pipe.filename` is not given, a default fifo in `/tmp/fifo_input` or
23
  * `/tmp/fifo_output` will be created that can be written and read respectively,
24
@@ -103,7 +103,7 @@
25
 
26
 #define NAME "pipe-tunnel"
27
 
28
-#define DEFAULT_CAPTURE_FILENAME   "/tmp/fifo_input"
29
+#define DEFAULT_CAPTURE_FILENAME   "/tmp/fifo_input"
30
 #define DEFAULT_PLAYBACK_FILENAME  "/tmp/fifo_output"
31
 
32
 #define DEFAULT_FORMAT "S16"
33
pipewire-0.3.70.tar.gz/src/modules/module-profiler.c -> pipewire-0.3.71.tar.gz/src/modules/module-profiler.c Changed
36
 
1
@@ -74,6 +74,8 @@
2
    struct pw_context *context;
3
    struct pw_properties *properties;
4
 
5
+   struct pw_loop *data_loop;
6
+
7
    struct spa_hook context_listener;
8
    struct spa_hook module_listener;
9
 
10
@@ -284,7 +286,7 @@
11
 static void stop_listener(struct impl *impl)
12
 {
13
    if (impl->listening) {
14
-       pw_loop_invoke(impl->context->data_loop,
15
+       pw_loop_invoke(impl->data_loop,
16
                        do_stop, SPA_ID_INVALID, NULL, 0, true, impl);
17
        impl->listening = false;
18
    }
19
@@ -338,7 +340,7 @@
20
 
21
    if (++impl->busy == 1) {
22
        pw_log_info("%p: starting profiler", impl);
23
-       pw_loop_invoke(impl->context->data_loop,
24
+       pw_loop_invoke(impl->data_loop,
25
                        do_start, SPA_ID_INVALID, NULL, 0, false, impl);
26
        impl->listening = true;
27
    }
28
@@ -411,6 +413,7 @@
29
 
30
    impl->context = context;
31
    impl->properties = props;
32
+   impl->data_loop = pw_context_get_data_loop(impl->context)->loop;
33
 
34
    spa_ringbuffer_init(&impl->buffer);
35
 
36
pipewire-0.3.70.tar.gz/src/modules/module-protocol-native.c -> pipewire-0.3.71.tar.gz/src/modules/module-protocol-native.c Changed
20
 
1
@@ -199,8 +199,8 @@
2
 {
3
    struct spa_pod *pod;
4
    pw_logt_debug(mod_topic_connection,
5
-             "%s: id:%d op:%d size:%d seq:%d", prefix,
6
-             msg->id, msg->opcode, msg->size, msg->seq);
7
+             "%s: id:%d op:%d size:%d seq:%d fds:%d", prefix,
8
+             msg->id, msg->opcode, msg->size, msg->seq, msg->n_fds);
9
 
10
    if ((pod = get_first_pod_from_data(msg->data, msg->size, 0)) != NULL)
11
        spa_debug_pod(0, NULL, pod);
12
@@ -285,6 +285,7 @@
13
            break;
14
 
15
        if (client->core_resource == NULL) {
16
+           pw_log_debug("%p: no core resource", client);
17
            res = -EPROTO;
18
            goto error;
19
        }
20
pipewire-0.3.70.tar.gz/src/modules/module-protocol-native/connection.c -> pipewire-0.3.71.tar.gz/src/modules/module-protocol-native/connection.c Changed
51
 
1
@@ -258,10 +258,12 @@
2
    return -errno;
3
 
4
 cmsgs_truncated:
5
+   pw_log_debug("connection %p: cmsg truncated", conn);
6
    close_all_fds(&msg, CMSG_FIRSTHDR(&msg));
7
    return -EPROTO;
8
 
9
 too_many_fds:
10
+   pw_log_debug("connection %p: too many fds", conn);
11
    close_all_fds(&msg, cmsg);
12
    return -EPROTO;
13
 }
14
@@ -269,16 +271,22 @@
15
 static void clear_buffer(struct buffer *buf, bool fds)
16
 {
17
    uint32_t i;
18
+
19
+   pw_log_debug("clear fds:%d", fds);
20
    if (fds) {
21
        for (i = 0; i < buf->n_fds; i++) {
22
            pw_log_debug("%p: close fd:%d", buf, buf->fdsi);
23
            close(buf->fdsi);
24
        }
25
+       buf->n_fds = 0;
26
+       buf->fds_offset = 0;
27
+   } else {
28
+       buf->n_fds -= SPA_MIN(buf->fds_offset, buf->n_fds);
29
+       memmove(buf->fds, &buf->fdsbuf->fds_offset, buf->n_fds * sizeof(int));
30
+       buf->fds_offset = 0;
31
    }
32
-   buf->n_fds = 0;
33
    buf->buffer_size = 0;
34
    buf->offset = 0;
35
-   buf->fds_offset = 0;
36
 }
37
 
38
 /** Prepare connection for calling from reentered context.
39
@@ -709,8 +717,9 @@
40
 
41
    if (mod_topic_connection->level >= SPA_LOG_LEVEL_DEBUG) {
42
        pw_logt_debug(mod_topic_connection,
43
-           ">>>>>>>>> out: id:%d op:%d size:%d seq:%d",
44
-               buf->msg.id, buf->msg.opcode, size, buf->msg.seq);
45
+           ">>>>>>>>> out: id:%d op:%d size:%d seq:%d fds:%d",
46
+               buf->msg.id, buf->msg.opcode, size, buf->msg.seq,
47
+               buf->msg.n_fds);
48
            spa_debug_pod(0, NULL, SPA_PTROFF(p, impl->hdr_size, struct spa_pod));
49
        pw_logt_debug(mod_topic_connection,
50
            ">>>>>>>>> out: done");
51
pipewire-0.3.70.tar.gz/src/modules/module-protocol-native/v0/protocol-native.c -> pipewire-0.3.71.tar.gz/src/modules/module-protocol-native/v0/protocol-native.c Changed
58
 
1
@@ -400,7 +400,7 @@
2
 static int remap_from_v2(uint32_t type, void *body, uint32_t size, struct pw_impl_client *client,
3
        struct spa_pod_builder *builder)
4
 {
5
-   int res;
6
+   int res = 0;
7
 
8
    switch (type) {
9
    case SPA_TYPE_Id:
10
@@ -445,17 +445,17 @@
11
        if (b->value.type == SPA_TYPE_Id) {
12
            uint32_t id;
13
            if ((res = spa_pod_get_id(&b->value, &id)) < 0)
14
-               return res;
15
+               goto done;
16
+
17
            spa_pod_builder_id(builder, pw_protocol_native0_type_from_v2(client, id));
18
            SPA_POD_PROP_ALTERNATIVE_FOREACH0(b, size, alt)
19
                if ((res = remap_from_v2(b->value.type, alt, b->value.size, client, builder)) < 0)
20
-                   return res;
21
+                   break;
22
        } else {
23
            spa_pod_builder_raw(builder, &b->value, size - sizeof(struct spa_pod));
24
        }
25
-
26
+done:
27
        spa_pod_builder_pop(builder, &f);
28
-
29
        break;
30
    }
31
    case SPA_TYPE_Object:
32
@@ -493,7 +493,7 @@
33
                        SPA_POD_BODY(p),
34
                        p->size,
35
                        client, builder)) < 0)
36
-               return res;
37
+               break;
38
        }
39
        spa_pod_builder_pop(builder, &f);
40
        break;
41
@@ -506,14 +506,14 @@
42
        spa_pod_builder_push_struct(builder, &f);
43
        SPA_POD_FOREACH(b, size, p)
44
            if ((res = remap_from_v2(p->type, SPA_POD_BODY(p), p->size, client, builder)) < 0)
45
-               return res;
46
+               break;
47
        spa_pod_builder_pop(builder, &f);
48
        break;
49
    }
50
    default:
51
        break;
52
    }
53
-   return 0;
54
+   return res;
55
 }
56
 
57
 static int remap_to_v2(struct pw_impl_client *client, const struct spa_type_info *info,
58
pipewire-0.3.70.tar.gz/src/modules/module-protocol-pulse/module.c -> pipewire-0.3.71.tar.gz/src/modules/module-protocol-pulse/module.c Changed
126
 
1
@@ -163,41 +163,48 @@
2
    free(s);
3
 }
4
 
5
-int module_args_to_audioinfo(struct impl *impl, struct pw_properties *props, struct spa_audio_info_raw *info)
6
+int module_args_to_audioinfo_keys(struct impl *impl, struct pw_properties *props,
7
+       const char *key_format, const char *key_rate,
8
+       const char *key_channels, const char *key_channel_map,
9
+       struct spa_audio_info_raw *info)
10
 {
11
    const char *str;
12
    uint32_t i;
13
 
14
-   /* We don't use any incoming format setting and use our native format */
15
-   spa_zero(*info);
16
-   info->flags = SPA_AUDIO_FLAG_UNPOSITIONED;
17
-   info->format = SPA_AUDIO_FORMAT_F32P;
18
-
19
-   if ((str = pw_properties_get(props, "channels")) != NULL) {
20
+   if (key_format && (str = pw_properties_get(props, key_format)) != NULL) {
21
+       info->format = format_paname2id(str, strlen(str));
22
+       if (info->format == SPA_AUDIO_FORMAT_UNKNOWN) {
23
+           pw_log_error("invalid %s '%s'", key_format, str);
24
+           return -EINVAL;
25
+       }
26
+       pw_properties_set(props, key_format, NULL);
27
+   }
28
+   if (key_channels && (str = pw_properties_get(props, key_channels)) != NULL) {
29
        info->channels = pw_properties_parse_int(str);
30
        if (info->channels == 0 || info->channels > SPA_AUDIO_MAX_CHANNELS) {
31
-           pw_log_error("invalid channels '%s'", str);
32
+           pw_log_error("invalid %s '%s'", key_channels, str);
33
            return -EINVAL;
34
        }
35
-       pw_properties_set(props, "channels", NULL);
36
+       pw_properties_set(props, key_channels, NULL);
37
    }
38
-   if ((str = pw_properties_get(props, "channel_map")) != NULL) {
39
+   if (key_channel_map && (str = pw_properties_get(props, key_channel_map)) != NULL) {
40
        struct channel_map map;
41
 
42
        channel_map_parse(str, &map);
43
        if (map.channels == 0 || map.channels > SPA_AUDIO_MAX_CHANNELS) {
44
-           pw_log_error("invalid channel_map '%s'", str);
45
+           pw_log_error("invalid %s '%s'", key_channel_map, str);
46
            return -EINVAL;
47
        }
48
        if (info->channels == 0)
49
            info->channels = map.channels;
50
        if (info->channels != map.channels) {
51
-           pw_log_error("Mismatched channel map");
52
+           pw_log_error("Mismatched %s and %s (%d vs %d)",
53
+                   key_channels, key_channel_map,
54
+                   info->channels, map.channels);
55
            return -EINVAL;
56
        }
57
        channel_map_to_positions(&map, info->position);
58
-       info->flags &= ~SPA_AUDIO_FLAG_UNPOSITIONED;
59
-       pw_properties_set(props, "channel_map", NULL);
60
+       pw_properties_set(props, key_channel_map, NULL);
61
    } else {
62
        if (info->channels == 0)
63
            info->channels = impl->defs.sample_spec.channels;
64
@@ -214,19 +221,25 @@
65
            for (i = 0; i < info->channels; i++)
66
                info->positioni = SPA_AUDIO_CHANNEL_UNKNOWN;
67
        }
68
-       if (info->position0 != SPA_AUDIO_CHANNEL_UNKNOWN)
69
-           info->flags &= ~SPA_AUDIO_FLAG_UNPOSITIONED;
70
+       if (info->position0 == SPA_AUDIO_CHANNEL_UNKNOWN)
71
+           info->flags |= SPA_AUDIO_FLAG_UNPOSITIONED;
72
    }
73
-
74
-   if ((str = pw_properties_get(props, "rate")) != NULL) {
75
+   if (key_rate && (str = pw_properties_get(props, key_rate)) != NULL) {
76
        info->rate = pw_properties_parse_int(str);
77
-       pw_properties_set(props, "rate", NULL);
78
-   } else {
79
-       info->rate = 0;
80
+       pw_properties_set(props, key_rate, NULL);
81
    }
82
    return 0;
83
 }
84
 
85
+int module_args_to_audioinfo(struct impl *impl, struct pw_properties *props, struct spa_audio_info_raw *info)
86
+{
87
+   /* We don't use any incoming format setting and use our native format */
88
+   spa_zero(*info);
89
+   info->format = SPA_AUDIO_FORMAT_F32P;
90
+   return module_args_to_audioinfo_keys(impl, props,
91
+           NULL, "rate", "channels", "channel_map", info);
92
+}
93
+
94
 bool module_args_parse_bool(const char *v)
95
 {
96
    if (spa_streq(v, "1") || !strcasecmp(v, "y") || !strcasecmp(v, "t") ||
97
@@ -235,6 +248,28 @@
98
    return false;
99
 }
100
 
101
+void audioinfo_to_properties(struct spa_audio_info_raw *info, struct pw_properties *props)
102
+{
103
+   uint32_t i;
104
+
105
+   if (info->format)
106
+       pw_properties_setf(props, SPA_KEY_AUDIO_FORMAT, "%s",
107
+                   format_id2name(info->format));
108
+   if (info->rate)
109
+       pw_properties_setf(props, SPA_KEY_AUDIO_RATE, "%u", info->rate);
110
+   if (info->channels) {
111
+       char *s, *p;
112
+
113
+       pw_properties_setf(props, SPA_KEY_AUDIO_CHANNELS, "%u", info->channels);
114
+
115
+       p = s = alloca(info->channels * 8);
116
+       for (i = 0; i < info->channels; i++)
117
+           p += spa_scnprintf(p, 8, "%s%s", i == 0 ? "" : ", ",
118
+                   channel_id2name(info->positioni));
119
+       pw_properties_setf(props, SPA_KEY_AUDIO_POSITION, " %s ", s);
120
+   }
121
+}
122
+
123
 static const struct module_info *find_module_info(const char *name)
124
 {
125
    extern const struct module_info __start_pw_mod_pulse_modules;
126
pipewire-0.3.70.tar.gz/src/modules/module-protocol-pulse/module.h -> pipewire-0.3.71.tar.gz/src/modules/module-protocol-pulse/module.h Changed
14
 
1
@@ -70,5 +70,12 @@
2
 void module_args_add_props(struct pw_properties *props, const char *str);
3
 int module_args_to_audioinfo(struct impl *impl, struct pw_properties *props, struct spa_audio_info_raw *info);
4
 bool module_args_parse_bool(const char *str);
5
+int module_args_to_audioinfo_keys(struct impl *impl, struct pw_properties *props,
6
+       const char *key_format, const char *key_rate,
7
+       const char *key_channels, const char *key_channel_map,
8
+       struct spa_audio_info_raw *info);
9
+
10
+void audioinfo_to_properties(struct spa_audio_info_raw *info, struct pw_properties *props);
11
+
12
 
13
 #endif
14
pipewire-0.3.70.tar.gz/src/modules/module-protocol-pulse/modules/module-combine-sink.c -> pipewire-0.3.71.tar.gz/src/modules/module-protocol-pulse/modules/module-combine-sink.c Changed
171
 
1
@@ -31,7 +31,8 @@
2
                "rate=<sample rate> "
3
                "channels=<number of channels> "
4
                "channel_map=<channel map> "
5
-               "remix=<remix channels> " },
6
+               "remix=<remix channels> "
7
+               "latency_compensate=<bool> " },
8
    { PW_KEY_MODULE_VERSION, PACKAGE_VERSION },
9
 };
10
 
11
@@ -48,16 +49,16 @@
12
    struct pw_impl_module *mod;
13
    struct spa_hook mod_listener;
14
 
15
-   char *sink_name;
16
    char **sink_names;
17
+   struct pw_properties *props;
18
    struct pw_properties *combine_props;
19
+   struct pw_properties *stream_props;
20
 
21
    struct spa_source *sinks_timeout;
22
 
23
    struct spa_audio_info_raw info;
24
 
25
    unsigned int sinks_pending;
26
-   unsigned int remix:1;
27
    unsigned int load_emitted:1;
28
    unsigned int start_error:1;
29
 };
30
@@ -148,31 +149,20 @@
31
    if (data->core == NULL)
32
        return -errno;
33
 
34
+   pw_properties_setf(data->combine_props, "pulse.module.id", "%u",
35
+           module->index);
36
+   pw_properties_setf(data->stream_props, "pulse.module.id", "%u",
37
+           module->index);
38
+
39
    if ((f = open_memstream(&args, &size)) == NULL)
40
        return -errno;
41
 
42
    fprintf(f, "{");
43
-   fprintf(f, " node.name = %s", data->sink_name);
44
-   fprintf(f, " node.description = %s", data->sink_name);
45
-   if (data->info.rate != 0)
46
-       fprintf(f, " audio.rate = %u", data->info.rate);
47
-   if (data->info.channels != 0) {
48
-       fprintf(f, " audio.channels = %u", data->info.channels);
49
-       if (!(data->info.flags & SPA_AUDIO_FLAG_UNPOSITIONED)) {
50
-           fprintf(f, " audio.position =  ");
51
-           for (i = 0; i < data->info.channels; i++)
52
-               fprintf(f, "%s%s", i == 0 ? "" : ",",
53
-                   channel_id2name(data->info.positioni));
54
-           fprintf(f, " ");
55
-       }
56
-   }
57
+   pw_properties_serialize_dict(f, &data->props->dict, 0);
58
    fprintf(f, " combine.props = {");
59
-   fprintf(f, " pulse.module.id = %u", module->index);
60
    pw_properties_serialize_dict(f, &data->combine_props->dict, 0);
61
    fprintf(f, " } stream.props = {");
62
-   if (!data->remix)
63
-       fprintf(f, "   "PW_KEY_STREAM_DONT_REMIX" = true");
64
-   fprintf(f, "   pulse.module.id = %u", module->index);
65
+   pw_properties_serialize_dict(f, &data->stream_props->dict, 0);
66
    fprintf(f, " } stream.rules = ");
67
    if (data->sink_names == NULL) {
68
        fprintf(f, "  { matches =  { media.class = \"Audio/Sink\" } ");
69
@@ -240,8 +230,9 @@
70
        pw_core_disconnect(d->core);
71
    }
72
    pw_free_strv(d->sink_names);
73
-   free(d->sink_name);
74
+   pw_properties_free(d->stream_props);
75
    pw_properties_free(d->combine_props);
76
+   pw_properties_free(d->props);
77
    return 0;
78
 }
79
 
80
@@ -249,37 +240,52 @@
81
 {
82
    struct module_combine_sink_data * const d = module->user_data;
83
    struct pw_properties * const props = module->props;
84
-   struct pw_properties *combine_props = NULL;
85
+   struct pw_properties *combine_props = NULL, *global_props = NULL, *stream_props = NULL;
86
    const char *str;
87
-   char *sink_name = NULL, **sink_names = NULL;
88
+   char **sink_names = NULL;
89
    struct spa_audio_info_raw info = { 0 };
90
    int res;
91
    int num_sinks = 0;
92
 
93
    PW_LOG_TOPIC_INIT(mod_topic);
94
 
95
+   global_props = pw_properties_new(NULL, NULL);
96
    combine_props = pw_properties_new(NULL, NULL);
97
+   stream_props = pw_properties_new(NULL, NULL);
98
+   if (global_props == NULL || combine_props == NULL || stream_props == NULL) {
99
+       res = -ENOMEM;
100
+       goto out;
101
+   }
102
 
103
    if ((str = pw_properties_get(props, "sink_name")) != NULL) {
104
-       sink_name = strdup(str);
105
+       pw_properties_set(global_props, PW_KEY_NODE_NAME, str);
106
+       pw_properties_set(global_props, PW_KEY_NODE_DESCRIPTION, str);
107
        pw_properties_set(props, "sink_name", NULL);
108
    } else {
109
-       sink_name = strdup("combined");
110
+       str = "combined";
111
+       pw_properties_set(global_props, PW_KEY_NODE_NAME, str);
112
+       pw_properties_set(global_props, PW_KEY_NODE_DESCRIPTION, str);
113
    }
114
 
115
-   if ((str = pw_properties_get(module->props, "sink_properties")) != NULL)
116
+   if ((str = pw_properties_get(props, "sink_properties")) != NULL)
117
        module_args_add_props(combine_props, str);
118
 
119
    if ((str = pw_properties_get(props, "slaves")) != NULL) {
120
        sink_names = pw_split_strv(str, ",", MAX_SINKS, &num_sinks);
121
        pw_properties_set(props, "slaves", NULL);
122
    }
123
-   d->remix = true;
124
    if ((str = pw_properties_get(props, "remix")) != NULL) {
125
-       d->remix = pw_properties_parse_bool(str);
126
+       pw_properties_set(stream_props, PW_KEY_STREAM_DONT_REMIX,
127
+               module_args_parse_bool(str) ? "false" : "true");
128
        pw_properties_set(props, "remix", NULL);
129
    }
130
 
131
+   if ((str = pw_properties_get(props, "latency_compensate")) != NULL) {
132
+       pw_properties_set(global_props, "combine.latency-compensate",
133
+               module_args_parse_bool(str) ? "true" : "false");
134
+       pw_properties_set(props, "latency_compensate", NULL);
135
+   }
136
+
137
    if ((str = pw_properties_get(props, "adjust_time")) != NULL) {
138
        pw_log_info("The `adjust_time` modarg is ignored");
139
        pw_properties_set(props, "adjust_time", NULL);
140
@@ -290,23 +296,27 @@
141
        pw_properties_set(props, "resample_method", NULL);
142
    }
143
 
144
-   if (module_args_to_audioinfo(module->impl, props, &info) < 0) {
145
+   if (module_args_to_audioinfo_keys(module->impl, props,
146
+           NULL, "rate", "channels", "channel_map", &info) < 0) {
147
        res = -EINVAL;
148
        goto out;
149
    }
150
+   audioinfo_to_properties(&info, global_props);
151
 
152
    d->module = module;
153
    d->info = info;
154
-   d->sink_name = sink_name;
155
    d->sink_names = sink_names;
156
    d->sinks_pending = (sink_names == NULL) ? 0 : num_sinks;
157
+   d->stream_props = stream_props;
158
    d->combine_props = combine_props;
159
+   d->props = global_props;
160
 
161
    return 0;
162
 out:
163
-   free(sink_name);
164
    pw_free_strv(sink_names);
165
+   pw_properties_free(stream_props);
166
    pw_properties_free(combine_props);
167
+   pw_properties_free(global_props);
168
 
169
    return res;
170
 }
171
pipewire-0.3.70.tar.gz/src/modules/module-protocol-pulse/modules/module-echo-cancel.c -> pipewire-0.3.71.tar.gz/src/modules/module-protocol-pulse/modules/module-echo-cancel.c Changed
111
 
1
@@ -22,6 +22,7 @@
2
    struct pw_impl_module *mod;
3
    struct spa_hook mod_listener;
4
 
5
+   struct pw_properties *global_props;
6
    struct pw_properties *props;
7
    struct pw_properties *capture_props;
8
    struct pw_properties *source_props;
9
@@ -46,38 +47,19 @@
10
 
11
 static int module_echo_cancel_load(struct module *module)
12
 {
13
-   struct pw_properties * const props = module->props;
14
    struct module_echo_cancel_data *data = module->user_data;
15
-   const char *method;
16
    FILE *f;
17
    char *args;
18
    size_t size;
19
-   uint32_t i;
20
 
21
    if ((f = open_memstream(&args, &size)) == NULL)
22
        return -errno;
23
 
24
    fprintf(f, "{");
25
-   if ((method = pw_properties_get(props, "aec_method")) == NULL)
26
-       method = "webrtc";
27
-
28
-   fprintf(f, " library.name = \"aec/libspa-aec-%s\"", method);
29
-
30
+   pw_properties_serialize_dict(f, &data->global_props->dict, 0);
31
    fprintf(f, " aec.args = {");
32
    pw_properties_serialize_dict(f, &data->props->dict, 0);
33
    fprintf(f, " }");
34
-   if (data->info.rate != 0)
35
-       fprintf(f, " audio.rate = %u", data->info.rate);
36
-   if (data->info.channels != 0) {
37
-       fprintf(f, " audio.channels = %u", data->info.channels);
38
-       if (!(data->info.flags & SPA_AUDIO_FLAG_UNPOSITIONED)) {
39
-           fprintf(f, " audio.position =  ");
40
-           for (i = 0; i < data->info.channels; i++)
41
-               fprintf(f, "%s%s", i == 0 ? "" : ",",
42
-                   channel_id2name(data->info.positioni));
43
-           fprintf(f, " ");
44
-       }
45
-   }
46
    fprintf(f, " capture.props = {");
47
    pw_properties_serialize_dict(f, &data->capture_props->dict, 0);
48
    fprintf(f, " } source.props = {");
49
@@ -114,6 +96,7 @@
50
        d->mod = NULL;
51
    }
52
 
53
+   pw_properties_free(d->global_props);
54
    pw_properties_free(d->props);
55
    pw_properties_free(d->capture_props);
56
    pw_properties_free(d->source_props);
57
@@ -231,22 +214,28 @@
58
    struct pw_properties * const props = module->props;
59
    struct pw_properties *aec_props = NULL, *sink_props = NULL, *source_props = NULL;
60
    struct pw_properties *playback_props = NULL, *capture_props = NULL;
61
+   struct pw_properties *global_props = NULL;
62
    const char *str, *method;
63
    struct spa_audio_info_raw info = { 0 };
64
    int res;
65
 
66
    PW_LOG_TOPIC_INIT(mod_topic);
67
 
68
+   global_props = pw_properties_new(NULL, NULL);
69
    aec_props = pw_properties_new(NULL, NULL);
70
    capture_props = pw_properties_new(NULL, NULL);
71
    source_props = pw_properties_new(NULL, NULL);
72
    sink_props = pw_properties_new(NULL, NULL);
73
    playback_props = pw_properties_new(NULL, NULL);
74
-   if (!aec_props || !source_props || !sink_props || !capture_props || !playback_props) {
75
+   if (!global_props || !aec_props || !source_props || !sink_props || !capture_props || !playback_props) {
76
        res = -EINVAL;
77
        goto out;
78
    }
79
 
80
+   if ((str = pw_properties_get(props, "aec_method")) == NULL)
81
+       str = "webrtc";
82
+   pw_properties_setf(global_props, "library.name", "aec/libspa-aec-%s", str);
83
+
84
    if ((str = pw_properties_get(props, "source_name")) != NULL) {
85
        pw_properties_set(source_props, PW_KEY_NODE_NAME, str);
86
        pw_properties_set(props, "source_name", NULL);
87
@@ -282,6 +271,7 @@
88
        res = -EINVAL;
89
        goto out;
90
    }
91
+   audioinfo_to_properties(&info, global_props);
92
 
93
    if ((str = pw_properties_get(props, "source_properties")) != NULL) {
94
        module_args_add_props(source_props, str);
95
@@ -314,6 +304,7 @@
96
    }
97
 
98
    d->module = module;
99
+   d->global_props = global_props;
100
    d->props = aec_props;
101
    d->capture_props = capture_props;
102
    d->source_props = source_props;
103
@@ -323,6 +314,7 @@
104
 
105
    return 0;
106
 out:
107
+   pw_properties_free(global_props);
108
    pw_properties_free(aec_props);
109
    pw_properties_free(playback_props);
110
    pw_properties_free(sink_props);
111
pipewire-0.3.71.tar.gz/src/modules/module-protocol-pulse/modules/module-jackdbus-detect.c Added
201
 
1
@@ -0,0 +1,206 @@
2
+/* PipeWire */
3
+/* SPDX-FileCopyrightText: Copyright © 2021 Wim Taymans <wim.taymans@gmail.com> */
4
+/* SPDX-License-Identifier: MIT */
5
+
6
+#include <spa/utils/hook.h>
7
+#include <pipewire/pipewire.h>
8
+
9
+#include "../defs.h"
10
+#include "../module.h"
11
+
12
+#define NAME "jackdbus-detect"
13
+
14
+PW_LOG_TOPIC_STATIC(mod_topic, "mod." NAME);
15
+#define PW_LOG_TOPIC_DEFAULT mod_topic
16
+
17
+
18
+struct module_jackdbus_detect_data {
19
+   struct module *module;
20
+
21
+   struct spa_hook mod_listener;
22
+   struct pw_impl_module *mod;
23
+
24
+   struct pw_properties *props;
25
+   struct pw_properties *sink_props;
26
+   struct pw_properties *source_props;
27
+};
28
+
29
+static void module_destroy(void *data)
30
+{
31
+   struct module_jackdbus_detect_data *d = data;
32
+   spa_hook_remove(&d->mod_listener);
33
+   d->mod = NULL;
34
+   module_schedule_unload(d->module);
35
+}
36
+
37
+static const struct pw_impl_module_events module_events = {
38
+   PW_VERSION_IMPL_MODULE_EVENTS,
39
+   .destroy = module_destroy
40
+};
41
+
42
+static int module_jackdbus_detect_load(struct module *module)
43
+{
44
+   struct module_jackdbus_detect_data *data = module->user_data;
45
+   FILE *f;
46
+   char *args;
47
+   size_t size;
48
+
49
+   pw_properties_setf(data->sink_props, "pulse.module.id",
50
+           "%u", module->index);
51
+   pw_properties_setf(data->source_props, "pulse.module.id",
52
+           "%u", module->index);
53
+
54
+   if ((f = open_memstream(&args, &size)) == NULL)
55
+       return -errno;
56
+
57
+   fprintf(f, "{");
58
+   pw_properties_serialize_dict(f, &data->props->dict, 0);
59
+   fprintf(f, " source.props = {");
60
+   pw_properties_serialize_dict(f, &data->source_props->dict, 0);
61
+   fprintf(f, " } sink.props = {");
62
+   pw_properties_serialize_dict(f, &data->sink_props->dict, 0);
63
+   fprintf(f, " } }");
64
+   fclose(f);
65
+
66
+   data->mod = pw_context_load_module(module->impl->context,
67
+           "libpipewire-module-jackdbus-detect",
68
+           args, NULL);
69
+   free(args);
70
+
71
+   if (data->mod == NULL)
72
+       return -errno;
73
+
74
+   pw_impl_module_add_listener(data->mod,
75
+           &data->mod_listener,
76
+           &module_events, data);
77
+
78
+   return 0;
79
+}
80
+
81
+static int module_jackdbus_detect_unload(struct module *module)
82
+{
83
+   struct module_jackdbus_detect_data *d = module->user_data;
84
+
85
+   if (d->mod) {
86
+       spa_hook_remove(&d->mod_listener);
87
+       pw_impl_module_destroy(d->mod);
88
+       d->mod = NULL;
89
+   }
90
+
91
+   return 0;
92
+}
93
+
94
+static const struct spa_dict_item module_jackdbus_detect_info = {
95
+   { PW_KEY_MODULE_AUTHOR, "Wim Taymans <wim.taymans@gmail.con>" },
96
+   { PW_KEY_MODULE_DESCRIPTION, "Creates a JACK client when jackdbus is started" },
97
+   { PW_KEY_MODULE_USAGE,
98
+       "channels=<number of channels> "
99
+       "sink_name=<name for the sink> "
100
+       "sink_properties=<properties for the sink> "
101
+       "sink_client_name=<jack client name> "
102
+       "sink_channels=<number of channels> "
103
+       "sink_channel_map=<channel map> "
104
+       "source_name=<name for the source> "
105
+       "source_properties=<properties for the source> "
106
+       "source_client_name=<jack client name> "
107
+       "source_channels=<number of channels> "
108
+       "source_channel_map=<channel map> "
109
+       "connect=<connect ports?>" },
110
+   { PW_KEY_MODULE_VERSION, PACKAGE_VERSION },
111
+};
112
+
113
+static int module_jackdbus_detect_prepare(struct module * const module)
114
+{
115
+   struct module_jackdbus_detect_data * const data = module->user_data;
116
+   struct pw_properties * const props = module->props;
117
+   struct pw_properties *jack_props = NULL, *sink_props = NULL, *source_props = NULL;
118
+   struct spa_audio_info_raw info;
119
+   const char *str;
120
+   int res;
121
+
122
+   PW_LOG_TOPIC_INIT(mod_topic);
123
+
124
+   jack_props = pw_properties_new(NULL, NULL);
125
+   sink_props = pw_properties_new(NULL, NULL);
126
+   source_props = pw_properties_new(NULL, NULL);
127
+   if (jack_props == NULL || sink_props == NULL || source_props == NULL) {
128
+       res = -ENOMEM;
129
+       goto out;
130
+   }
131
+
132
+   if ((str = pw_properties_get(props, "channels")) != NULL) {
133
+       pw_properties_set(jack_props, PW_KEY_AUDIO_CHANNELS, str);
134
+       pw_properties_set(props, "channels", NULL);
135
+   }
136
+   if ((str = pw_properties_get(props, "connect")) != NULL) {
137
+       pw_properties_set(jack_props, "jack.connect",
138
+               module_args_parse_bool(str) ? "true" : "false");
139
+   }
140
+
141
+   if ((str = pw_properties_get(props, "sink_name")) != NULL) {
142
+       pw_properties_set(sink_props, PW_KEY_NODE_NAME, str);
143
+       pw_properties_set(props, "sink_name", NULL);
144
+   } else {
145
+       pw_properties_set(sink_props, PW_KEY_NODE_NAME, "jack_out");
146
+   }
147
+   if ((str = pw_properties_get(props, "sink_client_name")) != NULL) {
148
+       pw_properties_set(jack_props, "jack.client-name", str);
149
+       pw_properties_set(props, "sink_client_name", NULL);
150
+   }
151
+
152
+   spa_zero(info);
153
+   if ((res = module_args_to_audioinfo_keys(module->impl, props, NULL, NULL,
154
+           "sink_channels", "sink_channel_map", &info)) < 0) {
155
+       return res;
156
+   } else {
157
+       audioinfo_to_properties(&info, sink_props);
158
+   }
159
+   if ((str = pw_properties_get(props, "sink_properties")) != NULL) {
160
+       module_args_add_props(sink_props, str);
161
+       pw_properties_set(props, "sink_properties", NULL);
162
+   }
163
+
164
+   if ((str = pw_properties_get(props, "source_name")) != NULL) {
165
+       pw_properties_set(source_props, PW_KEY_NODE_NAME, str);
166
+       pw_properties_set(props, "source_name", NULL);
167
+   } else {
168
+       pw_properties_set(source_props, PW_KEY_NODE_NAME, "jack_in");
169
+   }
170
+   if ((str = pw_properties_get(props, "source_client_name")) != NULL) {
171
+       pw_properties_set(jack_props, "jack.client-name", str);
172
+       pw_properties_set(props, "source_client_name", NULL);
173
+   }
174
+   spa_zero(info);
175
+   if ((res = module_args_to_audioinfo_keys(module->impl, props, NULL, NULL,
176
+           "source_channels", "source_channel_map", &info)) < 0) {
177
+       return res;
178
+   } else {
179
+       audioinfo_to_properties(&info, source_props);
180
+   }
181
+   if ((str = pw_properties_get(props, "source_properties")) != NULL) {
182
+       module_args_add_props(source_props, str);
183
+       pw_properties_set(props, "source_properties", NULL);
184
+   }
185
+
186
+   data->module = module;
187
+   data->props = jack_props;
188
+   data->sink_props = sink_props;
189
+   data->source_props = source_props;
190
+
191
+   return 0;
192
+out:
193
+   pw_properties_free(jack_props);
194
+   pw_properties_free(sink_props);
195
+   pw_properties_free(source_props);
196
+   return res;
197
+}
198
+
199
+DEFINE_MODULE_INFO(module_jackdbus_detect) = {
200
+   .name = "module-jackdbus-detect",
201
pipewire-0.3.70.tar.gz/src/modules/module-protocol-pulse/modules/module-ladspa-sink.c -> pipewire-0.3.71.tar.gz/src/modules/module-protocol-pulse/modules/module-ladspa-sink.c Changed
40
 
1
@@ -144,19 +144,6 @@
2
    { PW_KEY_MODULE_VERSION, PACKAGE_VERSION },
3
 };
4
 
5
-static void position_to_props(struct spa_audio_info_raw *info, struct pw_properties *props)
6
-{
7
-   char *s, *p;
8
-   uint32_t i;
9
-
10
-   pw_properties_setf(props, SPA_KEY_AUDIO_CHANNELS, "%u", info->channels);
11
-   p = s = alloca(info->channels * 8);
12
-   for (i = 0; i < info->channels; i++)
13
-       p += spa_scnprintf(p, 8, "%s%s", i == 0 ? "" : ",",
14
-               channel_id2name(info->positioni));
15
-   pw_properties_set(props, SPA_KEY_AUDIO_POSITION, s);
16
-}
17
-
18
 static int module_ladspa_sink_prepare(struct module * const module)
19
 {
20
    struct module_ladspa_sink_data * const d = module->user_data;
21
@@ -203,14 +190,15 @@
22
        pw_properties_set(props, "master", NULL);
23
    }
24
 
25
-   if (module_args_to_audioinfo(module->impl, props, &capture_info) < 0) {
26
+   if (module_args_to_audioinfo_keys(module->impl, props,
27
+           NULL, NULL, "channels", "channel_map", &capture_info) < 0) {
28
        res = -EINVAL;
29
        goto out;
30
    }
31
    playback_info = capture_info;
32
 
33
-   position_to_props(&capture_info, capture_props);
34
-   position_to_props(&playback_info, playback_props);
35
+   audioinfo_to_properties(&capture_info, capture_props);
36
+   audioinfo_to_properties(&playback_info, playback_props);
37
 
38
    if (pw_properties_get(playback_props, PW_KEY_NODE_PASSIVE) == NULL)
39
        pw_properties_set(playback_props, PW_KEY_NODE_PASSIVE, "true");
40
pipewire-0.3.70.tar.gz/src/modules/module-protocol-pulse/modules/module-ladspa-source.c -> pipewire-0.3.71.tar.gz/src/modules/module-protocol-pulse/modules/module-ladspa-source.c Changed
40
 
1
@@ -144,19 +144,6 @@
2
    { PW_KEY_MODULE_VERSION, PACKAGE_VERSION },
3
 };
4
 
5
-static void position_to_props(struct spa_audio_info_raw *info, struct pw_properties *props)
6
-{
7
-   char *s, *p;
8
-   uint32_t i;
9
-
10
-   pw_properties_setf(props, SPA_KEY_AUDIO_CHANNELS, "%u", info->channels);
11
-   p = s = alloca(info->channels * 8);
12
-   for (i = 0; i < info->channels; i++)
13
-       p += spa_scnprintf(p, 8, "%s%s", i == 0 ? "" : ",",
14
-               channel_id2name(info->positioni));
15
-   pw_properties_set(props, SPA_KEY_AUDIO_POSITION, s);
16
-}
17
-
18
 static int module_ladspa_source_prepare(struct module * const module)
19
 {
20
    struct module_ladspa_source_data * const d = module->user_data;
21
@@ -211,14 +198,15 @@
22
        pw_properties_set(props, "master", NULL);
23
    }
24
 
25
-   if (module_args_to_audioinfo(module->impl, props, &playback_info) < 0) {
26
+   if (module_args_to_audioinfo_keys(module->impl, props,
27
+           NULL, NULL, "channels", "channel_map", &playback_info) < 0) {
28
        res = -EINVAL;
29
        goto out;
30
    }
31
    capture_info = playback_info;
32
 
33
-   position_to_props(&capture_info, capture_props);
34
-   position_to_props(&playback_info, playback_props);
35
+   audioinfo_to_properties(&capture_info, capture_props);
36
+   audioinfo_to_properties(&playback_info, playback_props);
37
 
38
    if (pw_properties_get(capture_props, PW_KEY_NODE_PASSIVE) == NULL)
39
        pw_properties_set(capture_props, PW_KEY_NODE_PASSIVE, "true");
40
pipewire-0.3.70.tar.gz/src/modules/module-protocol-pulse/modules/module-loopback.c -> pipewire-0.3.71.tar.gz/src/modules/module-protocol-pulse/modules/module-loopback.c Changed
119
 
1
@@ -22,11 +22,9 @@
2
    struct pw_impl_module *mod;
3
    struct spa_hook mod_listener;
4
 
5
+   struct pw_properties *global_props;
6
    struct pw_properties *capture_props;
7
    struct pw_properties *playback_props;
8
-
9
-   struct spa_audio_info_raw info;
10
-   uint32_t latency_msec;
11
 };
12
 
13
 static void module_destroy(void *data)
14
@@ -47,8 +45,7 @@
15
    struct module_loopback_data *data = module->user_data;
16
    FILE *f;
17
    char *args;
18
-   size_t size, i;
19
-   char val256;
20
+   size_t size;
21
 
22
    pw_properties_setf(data->capture_props, PW_KEY_NODE_GROUP, "loopback-%u", module->index);
23
    pw_properties_setf(data->playback_props, PW_KEY_NODE_GROUP, "loopback-%u", module->index);
24
@@ -59,20 +56,7 @@
25
        return -errno;
26
 
27
    fprintf(f, "{");
28
-   if (data->info.channels != 0) {
29
-       fprintf(f, " audio.channels = %u", data->info.channels);
30
-       if (!(data->info.flags & SPA_AUDIO_FLAG_UNPOSITIONED)) {
31
-           fprintf(f, " audio.position =  ");
32
-           for (i = 0; i < data->info.channels; i++)
33
-               fprintf(f, "%s%s", i == 0 ? "" : ",",
34
-                   channel_id2name(data->info.positioni));
35
-           fprintf(f, " ");
36
-       }
37
-   }
38
-   if (data->latency_msec != 0)
39
-       fprintf(f, " target.delay.sec = %s",
40
-               spa_json_format_float(val, sizeof(val),
41
-                   data->latency_msec / 1000.0f));
42
+   pw_properties_serialize_dict(f, &data->global_props->dict, 0);
43
    fprintf(f, " capture.props = {");
44
    pw_properties_serialize_dict(f, &data->capture_props->dict, 0);
45
    fprintf(f, " } playback.props = {");
46
@@ -107,6 +91,7 @@
47
 
48
    pw_properties_free(d->capture_props);
49
    pw_properties_free(d->playback_props);
50
+   pw_properties_free(d->global_props);
51
 
52
    return 0;
53
 }
54
@@ -132,16 +117,17 @@
55
 {
56
    struct module_loopback_data * const d = module->user_data;
57
    struct pw_properties * const props = module->props;
58
-   struct pw_properties *playback_props = NULL, *capture_props = NULL;
59
+   struct pw_properties *global_props = NULL, *playback_props = NULL, *capture_props = NULL;
60
    const char *str;
61
    struct spa_audio_info_raw info = { 0 };
62
    int res;
63
 
64
    PW_LOG_TOPIC_INIT(mod_topic);
65
 
66
+   global_props = pw_properties_new(NULL, NULL);
67
    capture_props = pw_properties_new(NULL, NULL);
68
    playback_props = pw_properties_new(NULL, NULL);
69
-   if (!capture_props || !playback_props) {
70
+   if (!global_props || !capture_props || !playback_props) {
71
        res = -EINVAL;
72
        goto out;
73
    }
74
@@ -167,10 +153,12 @@
75
        pw_properties_set(props, "sink", NULL);
76
    }
77
 
78
-   if (module_args_to_audioinfo(module->impl, props, &info) < 0) {
79
+   if (module_args_to_audioinfo_keys(module->impl, props,
80
+               NULL, NULL, "channels", "channel_map", &info) < 0) {
81
        res = -EINVAL;
82
        goto out;
83
    }
84
+   audioinfo_to_properties(&info, global_props);
85
 
86
    if ((str = pw_properties_get(props, "source_dont_move")) != NULL) {
87
        pw_properties_set(capture_props, PW_KEY_NODE_DONT_RECONNECT, str);
88
@@ -189,8 +177,13 @@
89
        pw_properties_set(props, "remix", NULL);
90
    }
91
 
92
-   if ((str = pw_properties_get(props, "latency_msec")) != NULL)
93
-       d->latency_msec = atoi(str);
94
+   if ((str = pw_properties_get(props, "latency_msec")) != NULL) {
95
+       uint32_t latency_msec = atoi(str);
96
+       char val256;
97
+       pw_properties_setf(global_props, "target.delay.sec",
98
+               "%s", spa_json_format_float(val, sizeof(val),
99
+                   latency_msec / 1000.0f));
100
+   }
101
 
102
    if ((str = pw_properties_get(props, "sink_input_properties")) != NULL) {
103
        module_args_add_props(playback_props, str);
104
@@ -203,12 +196,13 @@
105
    }
106
 
107
    d->module = module;
108
+   d->global_props = global_props;
109
    d->capture_props = capture_props;
110
    d->playback_props = playback_props;
111
-   d->info = info;
112
 
113
    return 0;
114
 out:
115
+   pw_properties_free(global_props);
116
    pw_properties_free(playback_props);
117
    pw_properties_free(capture_props);
118
 
119
pipewire-0.3.70.tar.gz/src/modules/module-protocol-pulse/modules/module-null-sink.c -> pipewire-0.3.71.tar.gz/src/modules/module-protocol-pulse/modules/module-null-sink.c Changed
48
 
1
@@ -143,7 +143,6 @@
2
    struct pw_properties * const props = module->props;
3
    const char *str;
4
    struct spa_audio_info_raw info = { 0 };
5
-   uint32_t i;
6
 
7
    PW_LOG_TOPIC_INIT(mod_topic);
8
 
9
@@ -160,35 +159,11 @@
10
        pw_properties_set(props, "sink_properties", NULL);
11
    }
12
 
13
-   if (module_args_to_audioinfo(module->impl, props, &info) < 0)
14
+   if (module_args_to_audioinfo_keys(module->impl, props,
15
+           "format", "rate", "channels", "channel_map", &info) < 0)
16
        return -EINVAL;
17
 
18
-   info.format = module->impl->defs.sample_spec.format;
19
-   if ((str = pw_properties_get(props, "format")) != NULL) {
20
-       info.format = format_paname2id(str, strlen(str));
21
-       if (info.format == SPA_AUDIO_FORMAT_UNKNOWN) {
22
-           pw_log_error("invalid format '%s'", str);
23
-           return -EINVAL;
24
-       }
25
-       pw_properties_set(props, "format", NULL);
26
-   }
27
-
28
-   if (info.format)
29
-       pw_properties_setf(props, SPA_KEY_AUDIO_FORMAT, "%s",
30
-                   format_id2name(info.format));
31
-   if (info.rate)
32
-       pw_properties_setf(props, SPA_KEY_AUDIO_RATE, "%u", info.rate);
33
-   if (info.channels) {
34
-       char *s, *p;
35
-
36
-       pw_properties_setf(props, SPA_KEY_AUDIO_CHANNELS, "%u", info.channels);
37
-
38
-       p = s = alloca(info.channels * 8);
39
-       for (i = 0; i < info.channels; i++)
40
-           p += spa_scnprintf(p, 8, "%s%s", i == 0 ? "" : ",",
41
-                   channel_id2name(info.positioni));
42
-       pw_properties_set(props, SPA_KEY_AUDIO_POSITION, s);
43
-   }
44
+   audioinfo_to_properties(&info, props);
45
 
46
    if (pw_properties_get(props, PW_KEY_MEDIA_CLASS) == NULL)
47
        pw_properties_set(props, PW_KEY_MEDIA_CLASS, "Audio/Sink");
48
pipewire-0.3.70.tar.gz/src/modules/module-protocol-pulse/modules/module-pipe-sink.c -> pipewire-0.3.71.tar.gz/src/modules/module-protocol-pulse/modules/module-pipe-sink.c Changed
125
 
1
@@ -25,9 +25,8 @@
2
    struct spa_hook mod_listener;
3
    struct pw_impl_module *mod;
4
 
5
+   struct pw_properties *global_props;
6
    struct pw_properties *capture_props;
7
-   struct spa_audio_info_raw info;
8
-   char *filename;
9
 };
10
 
11
 static void module_destroy(void *data)
12
@@ -49,7 +48,6 @@
13
    FILE *f;
14
    char *args;
15
    size_t size;
16
-   uint32_t i;
17
 
18
    pw_properties_setf(data->capture_props, "pulse.module.id",
19
            "%u", module->index);
20
@@ -58,23 +56,7 @@
21
        return -errno;
22
 
23
    fprintf(f, "{");
24
-   fprintf(f, " \"tunnel.mode\" = \"sink\" ");
25
-   if (data->filename != NULL)
26
-       fprintf(f, " \"pipe.filename\": \"%s\"", data->filename);
27
-   if (data->info.format != 0)
28
-       fprintf(f, " \"audio.format\": \"%s\"", format_id2name(data->info.format));
29
-   if (data->info.rate != 0)
30
-       fprintf(f, " \"audio.rate\": %u,", data->info.rate);
31
-   if (data->info.channels != 0) {
32
-       fprintf(f, " \"audio.channels\": %u,", data->info.channels);
33
-       if (!(data->info.flags & SPA_AUDIO_FLAG_UNPOSITIONED)) {
34
-           fprintf(f, " \"audio.position\":  ");
35
-           for (i = 0; i < data->info.channels; i++)
36
-               fprintf(f, "%s\"%s\"", i == 0 ? "" : ",",
37
-                   channel_id2name(data->info.positioni));
38
-           fprintf(f, " ,");
39
-       }
40
-   }
41
+   pw_properties_serialize_dict(f, &data->global_props->dict, 0);
42
    fprintf(f, " \"stream.props\": {");
43
    pw_properties_serialize_dict(f, &data->capture_props->dict, 0);
44
    fprintf(f, " } }");
45
@@ -105,7 +87,7 @@
46
        d->mod = NULL;
47
    }
48
    pw_properties_free(d->capture_props);
49
-   free(d->filename);
50
+   pw_properties_free(d->global_props);
51
    return 0;
52
 }
53
 
54
@@ -126,35 +108,30 @@
55
 {
56
    struct module_pipesink_data * const d = module->user_data;
57
    struct pw_properties * const props = module->props;
58
-   struct pw_properties *capture_props = NULL;
59
+   struct pw_properties *global_props = NULL, *capture_props = NULL;
60
    struct spa_audio_info_raw info = { 0 };
61
    const char *str;
62
-   char *filename = NULL;
63
    int res = 0;
64
 
65
    PW_LOG_TOPIC_INIT(mod_topic);
66
 
67
+   global_props = pw_properties_new(NULL, NULL);
68
    capture_props = pw_properties_new(NULL, NULL);
69
-   if (!capture_props) {
70
+   if (!global_props || !capture_props) {
71
        res = -EINVAL;
72
        goto out;
73
    }
74
 
75
-   if (module_args_to_audioinfo(module->impl, props, &info) < 0) {
76
+   pw_properties_set(global_props, "tunnel.mode", "sink");
77
+
78
+   info.format = SPA_AUDIO_FORMAT_S16;
79
+   if (module_args_to_audioinfo_keys(module->impl, props,
80
+           "format", "rate", "channels", "channel_map", &info) < 0) {
81
        res = -EINVAL;
82
        goto out;
83
    }
84
+   audioinfo_to_properties(&info, global_props);
85
 
86
-   info.format = SPA_AUDIO_FORMAT_S16;
87
-   if ((str = pw_properties_get(props, "format")) != NULL) {
88
-       info.format = format_paname2id(str, strlen(str));
89
-       if (info.format == SPA_AUDIO_FORMAT_UNKNOWN) {
90
-           pw_log_error("invalid format '%s'", str);
91
-           res = -EINVAL;
92
-           goto out;
93
-       }
94
-       pw_properties_set(props, "format", NULL);
95
-   }
96
    if ((str = pw_properties_get(props, "sink_name")) != NULL) {
97
        pw_properties_set(capture_props, PW_KEY_NODE_NAME, str);
98
        pw_properties_set(props, "sink_name", NULL);
99
@@ -163,7 +140,7 @@
100
        module_args_add_props(capture_props, str);
101
 
102
    if ((str = pw_properties_get(props, "file")) != NULL) {
103
-       filename = strdup(str);
104
+       pw_properties_set(global_props, "pipe.filename", str);
105
        pw_properties_set(props, "file", NULL);
106
    }
107
    if ((str = pw_properties_get(capture_props, PW_KEY_DEVICE_ICON_NAME)) == NULL)
108
@@ -174,14 +151,13 @@
109
                "fifo_output");
110
 
111
    d->module = module;
112
+   d->global_props = global_props;
113
    d->capture_props = capture_props;
114
-   d->info = info;
115
-   d->filename = filename;
116
 
117
    return 0;
118
 out:
119
+   pw_properties_free(global_props);
120
    pw_properties_free(capture_props);
121
-   free(filename);
122
    return res;
123
 }
124
 
125
pipewire-0.3.70.tar.gz/src/modules/module-protocol-pulse/modules/module-pipe-source.c -> pipewire-0.3.71.tar.gz/src/modules/module-protocol-pulse/modules/module-pipe-source.c Changed
124
 
1
@@ -25,9 +25,8 @@
2
    struct spa_hook mod_listener;
3
    struct pw_impl_module *mod;
4
 
5
+   struct pw_properties *global_props;
6
    struct pw_properties *playback_props;
7
-   struct spa_audio_info_raw info;
8
-   char *filename;
9
 };
10
 
11
 static void module_destroy(void *data)
12
@@ -49,7 +48,6 @@
13
    FILE *f;
14
    char *args;
15
    size_t size;
16
-   uint32_t i;
17
 
18
    pw_properties_setf(data->playback_props, "pulse.module.id",
19
            "%u", module->index);
20
@@ -58,23 +56,7 @@
21
        return -errno;
22
 
23
    fprintf(f, "{");
24
-   fprintf(f, " \"tunnel.mode\" = \"source\" ");
25
-   if (data->filename != NULL)
26
-       fprintf(f, " \"pipe.filename\": \"%s\"", data->filename);
27
-   if (data->info.format != 0)
28
-       fprintf(f, " \"audio.format\": \"%s\"", format_id2name(data->info.format));
29
-   if (data->info.rate != 0)
30
-       fprintf(f, " \"audio.rate\": %u,", data->info.rate);
31
-   if (data->info.channels != 0) {
32
-       fprintf(f, " \"audio.channels\": %u,", data->info.channels);
33
-       if (!(data->info.flags & SPA_AUDIO_FLAG_UNPOSITIONED)) {
34
-           fprintf(f, " \"audio.position\":  ");
35
-           for (i = 0; i < data->info.channels; i++)
36
-               fprintf(f, "%s\"%s\"", i == 0 ? "" : ",",
37
-                   channel_id2name(data->info.positioni));
38
-           fprintf(f, " ,");
39
-       }
40
-   }
41
+   pw_properties_serialize_dict(f, &data->global_props->dict, 0);
42
    fprintf(f, " \"stream.props\": {");
43
    pw_properties_serialize_dict(f, &data->playback_props->dict, 0);
44
    fprintf(f, " } }");
45
@@ -105,7 +87,7 @@
46
        d->mod = NULL;
47
    }
48
    pw_properties_free(d->playback_props);
49
-   free(d->filename);
50
+   pw_properties_free(d->global_props);
51
    return 0;
52
 }
53
 
54
@@ -126,35 +108,30 @@
55
 {
56
    struct module_pipesrc_data * const d = module->user_data;
57
    struct pw_properties * const props = module->props;
58
-   struct pw_properties *playback_props = NULL;
59
+   struct pw_properties *global_props = NULL, *playback_props = NULL;
60
    struct spa_audio_info_raw info = { 0 };
61
    const char *str;
62
-   char *filename = NULL;
63
    int res = 0;
64
 
65
    PW_LOG_TOPIC_INIT(mod_topic);
66
 
67
+   global_props = pw_properties_new(NULL, NULL);
68
    playback_props = pw_properties_new(NULL, NULL);
69
-   if (!playback_props) {
70
+   if (!global_props || !playback_props) {
71
        res = -errno;
72
        goto out;
73
    }
74
 
75
-   if (module_args_to_audioinfo(module->impl, props, &info) < 0) {
76
+   pw_properties_set(global_props, "tunnel.mode", "source");
77
+
78
+   info.format = SPA_AUDIO_FORMAT_S16;
79
+   if (module_args_to_audioinfo_keys(module->impl, props,
80
+           "format", "rate", "channels", "channel_map", &info) < 0) {
81
        res = -EINVAL;
82
        goto out;
83
    }
84
+   audioinfo_to_properties(&info, global_props);
85
 
86
-   info.format = SPA_AUDIO_FORMAT_S16;
87
-   if ((str = pw_properties_get(props, "format")) != NULL) {
88
-       info.format = format_paname2id(str, strlen(str));
89
-       if (info.format == SPA_AUDIO_FORMAT_UNKNOWN) {
90
-           pw_log_error("invalid format '%s'", str);
91
-           res = -EINVAL;
92
-           goto out;
93
-       }
94
-       pw_properties_set(props, "format", NULL);
95
-   }
96
    if ((str = pw_properties_get(props, "source_name")) != NULL) {
97
        pw_properties_set(playback_props, PW_KEY_NODE_NAME, str);
98
        pw_properties_set(props, "source_name", NULL);
99
@@ -163,7 +140,7 @@
100
        module_args_add_props(playback_props, str);
101
 
102
    if ((str = pw_properties_get(props, "file")) != NULL) {
103
-       filename = strdup(str);
104
+       pw_properties_set(global_props, "pipe.filename", str);
105
        pw_properties_set(props, "file", NULL);
106
    }
107
    if ((str = pw_properties_get(playback_props, PW_KEY_DEVICE_ICON_NAME)) == NULL)
108
@@ -175,13 +152,12 @@
109
 
110
    d->module = module;
111
    d->playback_props = playback_props;
112
-   d->info = info;
113
-   d->filename = filename;
114
+   d->global_props = global_props;
115
 
116
    return 0;
117
 out:
118
+   pw_properties_free(global_props);
119
    pw_properties_free(playback_props);
120
-   free(filename);
121
    return res;
122
 }
123
 
124
pipewire-0.3.70.tar.gz/src/modules/module-protocol-pulse/modules/module-remap-sink.c -> pipewire-0.3.71.tar.gz/src/modules/module-protocol-pulse/modules/module-remap-sink.c Changed
64
 
1
@@ -109,19 +109,6 @@
2
    { PW_KEY_MODULE_VERSION, PACKAGE_VERSION },
3
 };
4
 
5
-static void position_to_props(struct spa_audio_info_raw *info, struct pw_properties *props)
6
-{
7
-   char *s, *p;
8
-   uint32_t i;
9
-
10
-   pw_properties_setf(props, SPA_KEY_AUDIO_CHANNELS, "%u", info->channels);
11
-   p = s = alloca(info->channels * 8);
12
-   for (i = 0; i < info->channels; i++)
13
-       p += spa_scnprintf(p, 8, "%s%s", i == 0 ? "" : ",",
14
-               channel_id2name(info->positioni));
15
-   pw_properties_set(props, SPA_KEY_AUDIO_POSITION, s);
16
-}
17
-
18
 static int module_remap_sink_prepare(struct module * const module)
19
 {
20
    struct module_remap_sink_data * const d = module->user_data;
21
@@ -180,26 +167,19 @@
22
        pw_properties_set(props, "master", NULL);
23
    }
24
 
25
-   if (module_args_to_audioinfo(module->impl, props, &capture_info) < 0) {
26
+   if (module_args_to_audioinfo_keys(module->impl, props,
27
+           NULL, NULL, "channels", "channel_map", &capture_info) < 0) {
28
        res = -EINVAL;
29
        goto out;
30
    }
31
    playback_info = capture_info;
32
-
33
-   if ((str = pw_properties_get(props, "master_channel_map")) != NULL) {
34
-       struct channel_map map;
35
-
36
-       channel_map_parse(str, &map);
37
-       if (map.channels == 0 || map.channels > SPA_AUDIO_MAX_CHANNELS) {
38
-           pw_log_error("invalid channel_map '%s'", str);
39
-           res = -EINVAL;
40
-           goto out;
41
-       }
42
-       channel_map_to_positions(&map, playback_info.position);
43
-       pw_properties_set(props, "master_channel_map", NULL);
44
+   if (module_args_to_audioinfo_keys(module->impl, props,
45
+           NULL, NULL, NULL, "master_channel_map", &playback_info) < 0) {
46
+       res = -EINVAL;
47
+       goto out;
48
    }
49
-   position_to_props(&capture_info, capture_props);
50
-   position_to_props(&playback_info, playback_props);
51
+   audioinfo_to_properties(&capture_info, capture_props);
52
+   audioinfo_to_properties(&playback_info, playback_props);
53
 
54
    if ((str = pw_properties_get(props, "remix")) != NULL) {
55
        /* Note that the boolean is inverted */
56
@@ -219,7 +199,6 @@
57
 out:
58
    pw_properties_free(playback_props);
59
    pw_properties_free(capture_props);
60
-
61
    return res;
62
 }
63
 
64
pipewire-0.3.70.tar.gz/src/modules/module-protocol-pulse/modules/module-remap-source.c -> pipewire-0.3.71.tar.gz/src/modules/module-protocol-pulse/modules/module-remap-source.c Changed
64
 
1
@@ -109,19 +109,6 @@
2
    { PW_KEY_MODULE_VERSION, PACKAGE_VERSION },
3
 };
4
 
5
-static void position_to_props(struct spa_audio_info_raw *info, struct pw_properties *props)
6
-{
7
-   char *s, *p;
8
-   uint32_t i;
9
-
10
-   pw_properties_setf(props, SPA_KEY_AUDIO_CHANNELS, "%u", info->channels);
11
-   p = s = alloca(info->channels * 8);
12
-   for (i = 0; i < info->channels; i++)
13
-       p += spa_scnprintf(p, 8, "%s%s", i == 0 ? "" : ",",
14
-               channel_id2name(info->positioni));
15
-   pw_properties_set(props, SPA_KEY_AUDIO_POSITION, s);
16
-}
17
-
18
 static int module_remap_source_prepare(struct module * const module)
19
 {
20
    struct module_remap_source_data * const d = module->user_data;
21
@@ -187,26 +174,19 @@
22
        pw_properties_set(props, "master", NULL);
23
    }
24
 
25
-   if (module_args_to_audioinfo(module->impl, props, &playback_info) < 0) {
26
+   if (module_args_to_audioinfo_keys(module->impl, props,
27
+           NULL, NULL, "channels", "channel_map", &playback_info) < 0) {
28
        res = -EINVAL;
29
        goto out;
30
    }
31
    capture_info = playback_info;
32
-
33
-   if ((str = pw_properties_get(props, "master_channel_map")) != NULL) {
34
-       struct channel_map map;
35
-
36
-       channel_map_parse(str, &map);
37
-       if (map.channels == 0 || map.channels > SPA_AUDIO_MAX_CHANNELS) {
38
-           pw_log_error("invalid channel_map '%s'", str);
39
-           res = -EINVAL;
40
-           goto out;
41
-       }
42
-       channel_map_to_positions(&map, capture_info.position);
43
-       pw_properties_set(props, "master_channel_map", NULL);
44
+   if (module_args_to_audioinfo_keys(module->impl, props,
45
+           NULL, NULL, NULL, "master_channel_map", &capture_info) < 0) {
46
+       res = -EINVAL;
47
+       goto out;
48
    }
49
-   position_to_props(&playback_info, playback_props);
50
-   position_to_props(&capture_info, capture_props);
51
+   audioinfo_to_properties(&playback_info, playback_props);
52
+   audioinfo_to_properties(&capture_info, capture_props);
53
 
54
    if ((str = pw_properties_get(props, "remix")) != NULL) {
55
        /* Note that the boolean is inverted */
56
@@ -226,7 +206,6 @@
57
 out:
58
    pw_properties_free(playback_props);
59
    pw_properties_free(capture_props);
60
-
61
    return res;
62
 }
63
 
64
pipewire-0.3.70.tar.gz/src/modules/module-protocol-pulse/modules/module-rtp-send.c -> pipewire-0.3.71.tar.gz/src/modules/module-protocol-pulse/modules/module-rtp-send.c Changed
71
 
1
@@ -25,8 +25,6 @@
2
    struct pw_properties *stream_props;
3
    struct pw_properties *global_props;
4
    struct pw_properties *sap_props;
5
-
6
-   struct spa_audio_info_raw info;
7
 };
8
 
9
 static void module_destroy(void *data)
10
@@ -61,7 +59,6 @@
11
    FILE *f;
12
    char *args;
13
    size_t size;
14
-   uint32_t i;
15
 
16
    pw_properties_setf(data->stream_props, "pulse.module.id",
17
            "%u", module->index);
18
@@ -71,20 +68,6 @@
19
 
20
    fprintf(f, "{");
21
    pw_properties_serialize_dict(f, &data->global_props->dict, 0);
22
-   if (data->info.format != 0)
23
-       fprintf(f, " \"audio.format\": \"%s\"", format_id2name(data->info.format));
24
-   if (data->info.rate != 0)
25
-       fprintf(f, " \"audio.rate\": %u,", data->info.rate);
26
-   if (data->info.channels != 0) {
27
-       fprintf(f, " \"audio.channels\": %u,", data->info.channels);
28
-       if (!(data->info.flags & SPA_AUDIO_FLAG_UNPOSITIONED)) {
29
-           fprintf(f, " \"audio.position\":  ");
30
-           for (i = 0; i < data->info.channels; i++)
31
-               fprintf(f, "%s\"%s\"", i == 0 ? "" : ",",
32
-                   channel_id2name(data->info.positioni));
33
-           fprintf(f, " ,");
34
-       }
35
-   }
36
    fprintf(f, " stream.props = {");
37
    pw_properties_serialize_dict(f, &data->stream_props->dict, 0);
38
    fprintf(f, " } }");
39
@@ -198,19 +181,13 @@
40
            pw_properties_set(stream_props, PW_KEY_TARGET_OBJECT, str);
41
        }
42
    }
43
-   if (module_args_to_audioinfo(module->impl, props, &info) < 0) {
44
+
45
+   if (module_args_to_audioinfo_keys(module->impl, props,
46
+           "format", "rate", "channels", "channel_map", &info) < 0) {
47
        res = -EINVAL;
48
        goto out;
49
    }
50
-   info.format = 0;
51
-   if ((str = pw_properties_get(props, "format")) != NULL) {
52
-       if ((info.format = format_paname2id(str, strlen(str))) ==
53
-               SPA_AUDIO_FORMAT_UNKNOWN) {
54
-           pw_log_error("unknown format %s", str);
55
-           res = -EINVAL;
56
-           goto out;
57
-       }
58
-   }
59
+   audioinfo_to_properties(&info, global_props);
60
 
61
    pw_properties_set(global_props, "sess.media", "audio");
62
    if ((str = pw_properties_get(props, "enable_opus")) != NULL) {
63
@@ -245,7 +222,6 @@
64
    d->stream_props = stream_props;
65
    d->global_props = global_props;
66
    d->sap_props = sap_props;
67
-   d->info = info;
68
 
69
    return 0;
70
 out:
71
pipewire-0.3.70.tar.gz/src/modules/module-protocol-pulse/modules/module-simple-protocol-tcp.c -> pipewire-0.3.71.tar.gz/src/modules/module-protocol-pulse/modules/module-simple-protocol-tcp.c Changed
47
 
1
@@ -44,25 +44,12 @@
2
    struct impl *impl = module->impl;
3
    char *args;
4
    size_t size;
5
-   uint32_t i;
6
    FILE *f;
7
 
8
    if ((f = open_memstream(&args, &size)) == NULL)
9
        return -errno;
10
 
11
    fprintf(f, "{");
12
-   if (data->info.rate != 0)
13
-       fprintf(f, " \"audio.rate\": %u,", data->info.rate);
14
-   if (data->info.channels != 0) {
15
-       fprintf(f, " \"audio.channels\": %u,", data->info.channels);
16
-       if (!(data->info.flags & SPA_AUDIO_FLAG_UNPOSITIONED)) {
17
-           fprintf(f, " \"audio.position\":  ");
18
-           for (i = 0; i < data->info.channels; i++)
19
-               fprintf(f, "%s\"%s\"", i == 0 ? "" : ",",
20
-                   channel_id2name(data->info.positioni));
21
-           fprintf(f, " ,");
22
-       }
23
-   }
24
    pw_properties_serialize_dict(f, &data->module_props->dict, 0);
25
    fprintf(f, "}");
26
    fclose(f);
27
@@ -127,15 +114,13 @@
28
        goto out;
29
    }
30
 
31
-   if ((str = pw_properties_get(props, "format")) != NULL) {
32
-       pw_properties_set(module_props, "audio.format",
33
-               format_id2name(format_paname2id(str, strlen(str))));
34
-       pw_properties_set(props, "format", NULL);
35
-   }
36
-   if (module_args_to_audioinfo(module->impl, props, &info) < 0) {
37
+   if (module_args_to_audioinfo_keys(module->impl, props,
38
+           "format", "rate", "channels", "channel_map", &info) < 0) {
39
        res = -EINVAL;
40
        goto out;
41
    }
42
+   audioinfo_to_properties(&info, module_props);
43
+
44
    if ((str = pw_properties_get(props, "playback")) != NULL) {
45
        pw_properties_set(module_props, "playback", str);
46
        pw_properties_set(props, "playback", NULL);
47
pipewire-0.3.70.tar.gz/src/modules/module-protocol-pulse/modules/module-tunnel-sink.c -> pipewire-0.3.71.tar.gz/src/modules/module-protocol-pulse/modules/module-tunnel-sink.c Changed
121
 
1
@@ -23,8 +23,6 @@
2
    struct pw_impl_module *mod;
3
    struct spa_hook mod_listener;
4
 
5
-   uint32_t latency_msec;
6
-
7
    struct pw_properties *stream_props;
8
 };
9
 
10
@@ -47,9 +45,6 @@
11
    FILE *f;
12
    char *args;
13
    size_t size;
14
-   const char *server;
15
-
16
-   server = pw_properties_get(module->props, "server");
17
 
18
    pw_properties_setf(data->stream_props, "pulse.module.id",
19
            "%u", module->index);
20
@@ -59,10 +54,6 @@
21
 
22
    fprintf(f, "{");
23
    pw_properties_serialize_dict(f, &module->props->dict, 0);
24
-   fprintf(f, " pulse.server.address = \"%s\" ", server);
25
-   fprintf(f, " tunnel.mode = sink ");
26
-   if (data->latency_msec > 0)
27
-       fprintf(f, " pulse.latency = %u ", data->latency_msec);
28
    fprintf(f, " stream.props = {");
29
    pw_properties_serialize_dict(f, &data->stream_props->dict, 0);
30
    fprintf(f, " } }");
31
@@ -115,19 +106,6 @@
32
    { PW_KEY_MODULE_VERSION, PACKAGE_VERSION },
33
 };
34
 
35
-static void audio_info_to_props(struct spa_audio_info_raw *info, struct pw_properties *props)
36
-{
37
-   char *s, *p;
38
-   uint32_t i;
39
-
40
-   pw_properties_setf(props, SPA_KEY_AUDIO_CHANNELS, "%u", info->channels);
41
-   p = s = alloca(info->channels * 8);
42
-   for (i = 0; i < info->channels; i++)
43
-       p += spa_scnprintf(p, 8, "%s%s", i == 0 ? "" : ",",
44
-               channel_id2name(info->positioni));
45
-   pw_properties_set(props, SPA_KEY_AUDIO_POSITION, s);
46
-}
47
-
48
 static int module_tunnel_sink_prepare(struct module * const module)
49
 {
50
    struct module_tunnel_sink_data * const d = module->user_data;
51
@@ -145,6 +123,8 @@
52
        goto out;
53
    }
54
 
55
+   pw_properties_set(props, "tunnel.mode", "sink");
56
+
57
    remote_sink_name = pw_properties_get(props, "sink");
58
    if (remote_sink_name)
59
        pw_properties_set(props, PW_KEY_TARGET_OBJECT, remote_sink_name);
60
@@ -153,11 +133,15 @@
61
        pw_log_error("no server given");
62
        res = -EINVAL;
63
        goto out;
64
+   } else {
65
+       pw_properties_set(props, "pulse.server.address", server);
66
    }
67
 
68
    pw_properties_setf(stream_props, PW_KEY_NODE_DESCRIPTION,
69
-                     _("Tunnel to %s/%s"), server,
70
+                     _("Tunnel to %s%s%s"), server,
71
+            remote_sink_name ? "/" : "",
72
             remote_sink_name ? remote_sink_name : "");
73
+
74
    pw_properties_set(stream_props, PW_KEY_MEDIA_CLASS, "Audio/Sink");
75
 
76
    if ((str = pw_properties_get(props, "sink_name")) != NULL) {
77
@@ -167,36 +151,30 @@
78
        pw_properties_setf(stream_props, PW_KEY_NODE_NAME,
79
                "tunnel-sink.%s", server);
80
    }
81
+   pw_properties_set(props, "server", NULL);
82
 
83
    if ((str = pw_properties_get(props, "sink_properties")) != NULL) {
84
        module_args_add_props(stream_props, str);
85
        pw_properties_set(props, "sink_properties", NULL);
86
    }
87
-   if (module_args_to_audioinfo(module->impl, props, &info) < 0) {
88
+   if (module_args_to_audioinfo_keys(module->impl, props,
89
+           "format", "rate", "channels", "channel_map", &info) < 0) {
90
        res = -EINVAL;
91
        goto out;
92
    }
93
+   audioinfo_to_properties(&info, stream_props);
94
 
95
-   audio_info_to_props(&info, stream_props);
96
-   if ((str = pw_properties_get(props, "format")) != NULL) {
97
-       uint32_t id = format_paname2id(str, strlen(str));
98
-       if (id == SPA_AUDIO_FORMAT_UNKNOWN) {
99
-           res = -EINVAL;
100
-           goto out;
101
-       }
102
-
103
-       pw_properties_set(stream_props, PW_KEY_AUDIO_FORMAT, format_id2name(id));
104
+   if ((str = pw_properties_get(props, "latency_msec")) != NULL) {
105
+       pw_properties_set(props, "pulse.latency", str);
106
+       pw_properties_set(props, "latency_msec", NULL);
107
    }
108
 
109
    d->module = module;
110
    d->stream_props = stream_props;
111
 
112
-   pw_properties_fetch_uint32(props, "latency_msec", &d->latency_msec);
113
-
114
    return 0;
115
 out:
116
    pw_properties_free(stream_props);
117
-
118
    return res;
119
 }
120
 
121
pipewire-0.3.70.tar.gz/src/modules/module-protocol-pulse/modules/module-tunnel-source.c -> pipewire-0.3.71.tar.gz/src/modules/module-protocol-pulse/modules/module-tunnel-source.c Changed
107
 
1
@@ -23,8 +23,6 @@
2
    struct pw_impl_module *mod;
3
    struct spa_hook mod_listener;
4
 
5
-   uint32_t latency_msec;
6
-
7
    struct pw_properties *stream_props;
8
 };
9
 
10
@@ -47,22 +45,15 @@
11
    FILE *f;
12
    char *args;
13
    size_t size;
14
-   const char *server;
15
 
16
    pw_properties_setf(data->stream_props, "pulse.module.id",
17
            "%u", module->index);
18
 
19
-   server = pw_properties_get(module->props, "server");
20
-
21
    if ((f = open_memstream(&args, &size)) == NULL)
22
        return -errno;
23
 
24
    fprintf(f, "{");
25
    pw_properties_serialize_dict(f, &module->props->dict, 0);
26
-   fprintf(f, " pulse.server.address = \"%s\" ", server);
27
-   fprintf(f, " tunnel.mode = source ");
28
-   if (data->latency_msec > 0)
29
-       fprintf(f, " pulse.latency = %u ", data->latency_msec);
30
    fprintf(f, " stream.props = {");
31
    pw_properties_serialize_dict(f, &data->stream_props->dict, 0);
32
    fprintf(f, " } }");
33
@@ -115,19 +106,6 @@
34
    { PW_KEY_MODULE_VERSION, PACKAGE_VERSION },
35
 };
36
 
37
-static void audio_info_to_props(struct spa_audio_info_raw *info, struct pw_properties *props)
38
-{
39
-   char *s, *p;
40
-   uint32_t i;
41
-
42
-   pw_properties_setf(props, SPA_KEY_AUDIO_CHANNELS, "%u", info->channels);
43
-   p = s = alloca(info->channels * 8);
44
-   for (i = 0; i < info->channels; i++)
45
-       p += spa_scnprintf(p, 8, "%s%s", i == 0 ? "" : ",",
46
-               channel_id2name(info->positioni));
47
-   pw_properties_set(props, SPA_KEY_AUDIO_POSITION, s);
48
-}
49
-
50
 static int module_tunnel_source_prepare(struct module * const module)
51
 {
52
    struct module_tunnel_source_data * const d = module->user_data;
53
@@ -145,6 +123,8 @@
54
        goto out;
55
    }
56
 
57
+   pw_properties_set(props, "tunnel.mode", "source");
58
+
59
    remote_source_name = pw_properties_get(props, "source");
60
    if (remote_source_name)
61
        pw_properties_set(props, PW_KEY_TARGET_OBJECT, remote_source_name);
62
@@ -153,10 +133,13 @@
63
        pw_log_error("no server given");
64
        res = -EINVAL;
65
        goto out;
66
+   } else {
67
+       pw_properties_set(props, "pulse.server.address", server);
68
    }
69
 
70
    pw_properties_setf(stream_props, PW_KEY_NODE_DESCRIPTION,
71
-                     _("Tunnel to %s/%s"), server,
72
+                     _("Tunnel to %s%s%s"), server,
73
+            remote_source_name ? "/" : "",
74
             remote_source_name ? remote_source_name : "");
75
    pw_properties_set(stream_props, PW_KEY_MEDIA_CLASS, "Audio/Source");
76
 
77
@@ -171,22 +154,24 @@
78
        module_args_add_props(stream_props, str);
79
        pw_properties_set(props, "source_properties", NULL);
80
    }
81
-   if (module_args_to_audioinfo(module->impl, props, &info) < 0) {
82
+   if (module_args_to_audioinfo_keys(module->impl, props,
83
+           "format", "rate", "channels", "channel_map", &info) < 0) {
84
        res = -EINVAL;
85
        goto out;
86
    }
87
+   audioinfo_to_properties(&info, stream_props);
88
 
89
-   audio_info_to_props(&info, stream_props);
90
+   if ((str = pw_properties_get(props, "latency_msec")) != NULL) {
91
+       pw_properties_set(props, "pulse.latency", str);
92
+       pw_properties_set(props, "latency_msec", NULL);
93
+   }
94
 
95
    d->module = module;
96
    d->stream_props = stream_props;
97
 
98
-   pw_properties_fetch_uint32(props, "latency_msec", &d->latency_msec);
99
-
100
    return 0;
101
 out:
102
    pw_properties_free(stream_props);
103
-
104
    return res;
105
 }
106
 
107
pipewire-0.3.70.tar.gz/src/modules/module-protocol-pulse/operation.h -> pipewire-0.3.71.tar.gz/src/modules/module-protocol-pulse/operation.h Changed
13
 
1
@@ -27,4 +27,11 @@
2
 void operation_free(struct operation *o);
3
 void operation_complete(struct operation *o);
4
 
5
+static inline void operation_free_by_tag(struct client *client, uint32_t tag)
6
+{
7
+   struct operation *o = operation_find(client, tag);
8
+   if (o)
9
+       operation_free(o);
10
+}
11
+
12
 #endif /* PULSER_SERVER_OPERATION_H */
13
pipewire-0.3.70.tar.gz/src/modules/module-protocol-pulse/pending-sample.c -> pipewire-0.3.71.tar.gz/src/modules/module-protocol-pulse/pending-sample.c Changed
137
 
1
@@ -7,24 +7,132 @@
2
 #include <pipewire/work-queue.h>
3
 
4
 #include "client.h"
5
+#include "collect.h"
6
+#include "commands.h"
7
 #include "internal.h"
8
 #include "log.h"
9
+#include "message.h"
10
 #include "operation.h"
11
 #include "pending-sample.h"
12
+#include "reply.h"
13
 #include "sample-play.h"
14
 
15
+static void do_pending_sample_finish(void *obj, void *data, int res, uint32_t id)
16
+{
17
+   struct pending_sample *ps = obj;
18
+   struct client *client = ps->client;
19
+
20
+   pending_sample_free(ps);
21
+   client_unref(client);
22
+}
23
+
24
+static void schedule_maybe_finish(struct pending_sample *ps)
25
+{
26
+   if (!ps->done || !ps->replied)
27
+       return;
28
+
29
+   pw_work_queue_add(ps->client->impl->work_queue, ps, 0,
30
+             do_pending_sample_finish, NULL);
31
+}
32
+
33
+static void sample_play_ready_reply(void *data, struct client *client, uint32_t tag)
34
+{
35
+   struct pending_sample *ps = data;
36
+   uint32_t index = id_to_index(client->manager, ps->play->id);
37
+
38
+   pw_log_info("%s PLAY_SAMPLE tag:%u index:%u",
39
+           client->name, ps->tag, index);
40
+
41
+   if (!ps->replied) {
42
+       struct message *reply = reply_new(client, ps->tag);
43
+       if (client->version >= 13)
44
+           message_put(reply,
45
+               TAG_U32, index,
46
+               TAG_INVALID);
47
+
48
+       client_queue_message(client, reply);
49
+       ps->replied = true;
50
+   }
51
+
52
+   schedule_maybe_finish(ps);
53
+}
54
+
55
+static void on_sample_play_ready(void *data, uint32_t id)
56
+{
57
+   struct pending_sample *ps = data;
58
+   struct client *client = ps->client;
59
+
60
+   if (!ps->replied)
61
+       operation_new_cb(client, ps->tag, sample_play_ready_reply, ps);
62
+}
63
+
64
+static void on_sample_play_done(void *data, int res)
65
+{
66
+   struct pending_sample *ps = data;
67
+   struct client *client = ps->client;
68
+
69
+   if (!ps->replied && res < 0) {
70
+       reply_error(client, COMMAND_PLAY_SAMPLE, ps->tag, res);
71
+       ps->replied = true;
72
+   }
73
+
74
+   pw_log_info("%s PLAY_SAMPLE done tag:%u result:%d", client->name, ps->tag, res);
75
+
76
+   ps->done = true;
77
+   schedule_maybe_finish(ps);
78
+}
79
+
80
+static const struct sample_play_events sample_play_events = {
81
+   VERSION_SAMPLE_PLAY_EVENTS,
82
+   .ready = on_sample_play_ready,
83
+   .done = on_sample_play_done,
84
+};
85
+
86
+static void on_client_disconnect(void *data)
87
+{
88
+   struct pending_sample *ps = data;
89
+
90
+   ps->replied = true;
91
+   operation_free_by_tag(ps->client, ps->tag);
92
+
93
+   schedule_maybe_finish(ps);
94
+}
95
+
96
+static const struct client_events client_events = {
97
+   VERSION_CLIENT_EVENTS,
98
+   .disconnect = on_client_disconnect,
99
+};
100
+
101
+int pending_sample_new(struct client *client, struct sample *sample, struct pw_properties *props, uint32_t tag)
102
+{
103
+   struct pending_sample *ps;
104
+   struct sample_play *p = sample_play_new(client->core, sample, props, sizeof(*ps));
105
+   if (!p)
106
+       return -errno;
107
+
108
+   ps = p->user_data;
109
+   ps->client = client;
110
+   ps->play = p;
111
+   ps->tag = tag;
112
+   sample_play_add_listener(p, &ps->listener, &sample_play_events, ps);
113
+   client_add_listener(client, &ps->client_listener, &client_events, ps);
114
+   spa_list_append(&client->pending_samples, &ps->link);
115
+   client->ref++;
116
+
117
+   return 0;
118
+}
119
+
120
 void pending_sample_free(struct pending_sample *ps)
121
 {
122
    struct client * const client = ps->client;
123
    struct impl * const impl = client->impl;
124
-   struct operation *o;
125
 
126
    spa_list_remove(&ps->link);
127
    spa_hook_remove(&ps->listener);
128
+   spa_hook_remove(&ps->client_listener);
129
    pw_work_queue_cancel(impl->work_queue, ps, SPA_ID_INVALID);
130
 
131
-   if ((o = operation_find(client, ps->tag)) != NULL)
132
-       operation_free(o);
133
+   operation_free_by_tag(client, ps->tag);
134
 
135
    sample_play_destroy(ps->play);
136
 }
137
pipewire-0.3.70.tar.gz/src/modules/module-protocol-pulse/pending-sample.h -> pipewire-0.3.71.tar.gz/src/modules/module-protocol-pulse/pending-sample.h Changed
25
 
1
@@ -11,6 +11,8 @@
2
 #include <spa/utils/hook.h>
3
 
4
 struct client;
5
+struct pw_properties;
6
+struct sample;
7
 struct sample_play;
8
 
9
 struct pending_sample {
10
@@ -18,11 +20,13 @@
11
    struct client *client;
12
    struct sample_play *play;
13
    struct spa_hook listener;
14
+   struct spa_hook client_listener;
15
    uint32_t tag;
16
-   unsigned ready:1;
17
+   unsigned replied:1;
18
    unsigned done:1;
19
 };
20
 
21
+int pending_sample_new(struct client *client, struct sample *sample, struct pw_properties *props, uint32_t tag);
22
 void pending_sample_free(struct pending_sample *ps);
23
 
24
 #endif /* PULSE_SERVER_PENDING_SAMPLE_H */
25
pipewire-0.3.70.tar.gz/src/modules/module-protocol-pulse/pulse-server.c -> pipewire-0.3.71.tar.gz/src/modules/module-protocol-pulse/pulse-server.c Changed
126
 
1
@@ -50,7 +50,6 @@
2
 #include "quirks.h"
3
 #include "reply.h"
4
 #include "sample.h"
5
-#include "sample-play.h"
6
 #include "server.h"
7
 #include "stream.h"
8
 #include "utils.h"
9
@@ -2502,81 +2501,13 @@
10
    return o;
11
 }
12
 
13
-static void sample_play_finish(struct pending_sample *ps)
14
-{
15
-   struct client *client = ps->client;
16
-   pending_sample_free(ps);
17
-   client_unref(client);
18
-}
19
-
20
-static void sample_play_ready_reply(void *data, struct client *client, uint32_t tag)
21
-{
22
-   struct pending_sample *ps = data;
23
-   struct message *reply;
24
-   uint32_t index = id_to_index(client->manager, ps->play->id);
25
-
26
-   pw_log_info("%s PLAY_SAMPLE tag:%u index:%u",
27
-           client->name, ps->tag, index);
28
-
29
-   ps->ready = true;
30
-
31
-   reply = reply_new(client, ps->tag);
32
-   if (client->version >= 13)
33
-       message_put(reply,
34
-           TAG_U32, index,
35
-           TAG_INVALID);
36
-
37
-   client_queue_message(client, reply);
38
-
39
-   if (ps->done)
40
-       sample_play_finish(ps);
41
-}
42
-
43
-static void sample_play_ready(void *data, uint32_t id)
44
-{
45
-   struct pending_sample *ps = data;
46
-   struct client *client = ps->client;
47
-   operation_new_cb(client, ps->tag, sample_play_ready_reply, ps);
48
-}
49
-
50
-static void on_sample_done(void *obj, void *data, int res, uint32_t id)
51
-{
52
-   struct pending_sample *ps = obj;
53
-   ps->done = true;
54
-   if (ps->ready)
55
-       sample_play_finish(ps);
56
-}
57
-
58
-static void sample_play_done(void *data, int res)
59
-{
60
-   struct pending_sample *ps = data;
61
-   struct client *client = ps->client;
62
-   struct impl *impl = client->impl;
63
-
64
-   if (res < 0)
65
-       reply_error(client, COMMAND_PLAY_SAMPLE, ps->tag, res);
66
-   else
67
-       pw_log_info("%s PLAY_SAMPLE done tag:%u", client->name, ps->tag);
68
-
69
-   pw_work_queue_add(impl->work_queue, ps, 0,
70
-               on_sample_done, client);
71
-}
72
-
73
-static const struct sample_play_events sample_play_events = {
74
-   VERSION_SAMPLE_PLAY_EVENTS,
75
-   .ready = sample_play_ready,
76
-   .done = sample_play_done,
77
-};
78
-
79
 static int do_play_sample(struct client *client, uint32_t command, uint32_t tag, struct message *m)
80
 {
81
    struct impl *impl = client->impl;
82
    uint32_t sink_index, volume;
83
    struct sample *sample;
84
-   struct sample_play *play;
85
    const char *sink_name, *name;
86
    struct pw_properties *props = NULL;
87
-   struct pending_sample *ps;
88
    struct pw_manager_object *o;
89
    int res;
90
 
91
@@ -2617,20 +2548,7 @@
92
 
93
    pw_properties_setf(props, PW_KEY_TARGET_OBJECT, "%"PRIu64, o->serial);
94
 
95
-   play = sample_play_new(client->core, sample, props, sizeof(struct pending_sample));
96
-   props = NULL;
97
-   if (play == NULL)
98
-       goto error_errno;
99
-
100
-   ps = play->user_data;
101
-   ps->client = client;
102
-   ps->play = play;
103
-   ps->tag = tag;
104
-   sample_play_add_listener(play, &ps->listener, &sample_play_events, ps);
105
-   spa_list_append(&client->pending_samples, &ps->link);
106
-   client->ref++;
107
-
108
-   return 0;
109
+   return pending_sample_new(client, sample, props, tag);
110
 
111
 error_errno:
112
    res = -errno;
113
@@ -5647,7 +5565,11 @@
114
            &context_events, impl);
115
 
116
 #ifdef HAVE_DBUS
117
-   impl->dbus_name = dbus_request_name(context, "org.pulseaudio.Server");
118
+   str = pw_properties_get(props, "server.dbus-name");
119
+   if (str == NULL)
120
+       str = "org.pulseaudio.Server";
121
+   if (strlen(str) > 0)
122
+       impl->dbus_name = dbus_request_name(context, str);
123
 #endif
124
    cmd_run(impl);
125
 
126
pipewire-0.3.70.tar.gz/src/modules/module-pulse-tunnel.c -> pipewire-0.3.71.tar.gz/src/modules/module-pulse-tunnel.c Changed
61
 
1
@@ -224,7 +224,8 @@
2
    switch (state) {
3
    case PW_STREAM_STATE_ERROR:
4
    case PW_STREAM_STATE_UNCONNECTED:
5
-       pw_impl_module_schedule_destroy(impl->module);
6
+       if (impl->module)
7
+           pw_impl_module_schedule_destroy(impl->module);
8
        break;
9
    case PW_STREAM_STATE_PAUSED:
10
        cork_stream(impl, true);
11
@@ -500,7 +501,8 @@
12
    bool async, uint32_t seq, const void *data, size_t size, void *user_data)
13
 {
14
    struct impl *impl = user_data;
15
-   pw_impl_module_schedule_destroy(impl->module);
16
+   if (impl->module)
17
+       pw_impl_module_schedule_destroy(impl->module);
18
    return 0;
19
 }
20
 
21
@@ -937,8 +939,10 @@
22
    pw_log_error("error id:%u seq:%d res:%d (%s): %s",
23
            id, seq, res, spa_strerror(res), message);
24
 
25
-   if (id == PW_ID_CORE && res == -EPIPE)
26
-       pw_impl_module_schedule_destroy(impl->module);
27
+   if (id == PW_ID_CORE && res == -EPIPE) {
28
+       if (impl->module)
29
+           pw_impl_module_schedule_destroy(impl->module);
30
+   }
31
 }
32
 
33
 static const struct pw_core_events core_events = {
34
@@ -951,7 +955,8 @@
35
    struct impl *impl = d;
36
    spa_hook_remove(&impl->core_listener);
37
    impl->core = NULL;
38
-   pw_impl_module_schedule_destroy(impl->module);
39
+   if (impl->module)
40
+       pw_impl_module_schedule_destroy(impl->module);
41
 }
42
 
43
 static const struct pw_proxy_events core_proxy_events = {
44
@@ -977,6 +982,8 @@
45
    if (impl->core && impl->do_disconnect)
46
        pw_core_disconnect(impl->core);
47
 
48
+   pw_loop_invoke(impl->main_loop, NULL, 0, NULL, 0, false, impl);
49
+
50
    pw_properties_free(impl->stream_props);
51
    pw_properties_free(impl->props);
52
 
53
@@ -988,6 +995,7 @@
54
 {
55
    struct impl *impl = data;
56
    spa_hook_remove(&impl->module_listener);
57
+   impl->module = NULL;
58
    impl_destroy(impl);
59
 }
60
 
61
pipewire-0.3.70.tar.gz/src/modules/module-raop-discover.c -> pipewire-0.3.71.tar.gz/src/modules/module-raop-discover.c Changed
131
 
1
@@ -125,12 +125,10 @@
2
 };
3
 
4
 struct tunnel_info {
5
-   AvahiIfIndex interface;
6
-   AvahiProtocol protocol;
7
    const char *name;
8
    const char *host_name;
9
-   const char *type;
10
-   const char *domain;
11
+   const char *ip;
12
+   const char *port;
13
 };
14
 
15
 #define TUNNEL_INFO(...) ((struct tunnel_info){ __VA_ARGS__ })
16
@@ -152,12 +150,10 @@
17
    if (t == NULL)
18
        return NULL;
19
 
20
-   t->info.interface = info->interface;
21
-   t->info.protocol = info->protocol;
22
    t->info.name = strdup(info->name);
23
    t->info.host_name = strdup(info->host_name);
24
-   t->info.type = strdup(info->type);
25
-   t->info.domain = strdup(info->domain);
26
+   t->info.ip = strdup(info->ip);
27
+   t->info.port = strdup(info->port);
28
    spa_list_append(&impl->tunnel_list, &t->link);
29
 
30
    return t;
31
@@ -167,11 +163,7 @@
32
 {
33
    struct tunnel *t;
34
    spa_list_for_each(t, &impl->tunnel_list, link) {
35
-       if (t->info.interface == info->interface &&
36
-           t->info.protocol == info->protocol &&
37
-           spa_streq(t->info.name, info->name) &&
38
-           spa_streq(t->info.type, info->type) &&
39
-           spa_streq(t->info.domain, info->domain))
40
+       if (spa_streq(t->info.name, info->name))
41
            return t;
42
    }
43
    return NULL;
44
@@ -298,8 +290,8 @@
45
 
46
    free((char *) t->info.name);
47
    free((char *) t->info.host_name);
48
-   free((char *) t->info.type);
49
-   free((char *) t->info.domain);
50
+   free((char *) t->info.ip);
51
+   free((char *) t->info.port);
52
 
53
    free(t);
54
 }
55
@@ -385,23 +377,18 @@
56
 {
57
    struct impl *impl = userdata;
58
    struct tunnel_info tinfo;
59
-   const char *str;
60
+   const char *str, *port_str;
61
    AvahiStringList *l;
62
    struct pw_properties *props = NULL;
63
    char atAVAHI_ADDRESS_STR_MAX;
64
-   int ipv;
65
 
66
    if (event != AVAHI_RESOLVER_FOUND) {
67
        pw_log_error("Resolving of '%s' failed: %s", name,
68
                avahi_strerror(avahi_client_errno(impl->client)));
69
        goto done;
70
    }
71
-   tinfo = TUNNEL_INFO(.interface = interface,
72
-           .protocol = protocol,
73
-           .host_name = host_name,
74
-           .name = name,
75
-           .type = type,
76
-           .domain = domain);
77
+
78
+   avahi_address_snprint(at, sizeof(at), a);
79
 
80
    props = pw_properties_new(NULL, NULL);
81
    if (props == NULL) {
82
@@ -409,11 +396,7 @@
83
        goto done;
84
    }
85
 
86
-   avahi_address_snprint(at, sizeof(at), a);
87
-   ipv = protocol == AVAHI_PROTO_INET ? 4 : 6;
88
-
89
    pw_properties_setf(props, "raop.ip", "%s", at);
90
-   pw_properties_setf(props, "raop.ip.version", "%d", ipv);
91
    pw_properties_setf(props, "raop.port", "%u", port);
92
    pw_properties_setf(props, "raop.name", "%s", name);
93
    pw_properties_setf(props, "raop.hostname", "%s", host_name);
94
@@ -430,6 +413,13 @@
95
        avahi_free(value);
96
    }
97
 
98
+   port_str = pw_properties_get(props, "raop.port");
99
+
100
+   tinfo = TUNNEL_INFO(.name = name,
101
+           .host_name = host_name,
102
+           .ip = at,
103
+           .port = port_str);
104
+
105
    if ((str = pw_properties_get(impl->properties, "stream.rules")) == NULL)
106
        str = DEFAULT_CREATE_RULES;
107
    if (str != NULL) {
108
@@ -462,18 +452,16 @@
109
    if (flags & AVAHI_LOOKUP_RESULT_LOCAL)
110
        return;
111
 
112
-   info = TUNNEL_INFO(.interface = interface,
113
-           .protocol = protocol,
114
-           .name = name,
115
-           .type = type,
116
-           .domain = domain);
117
+   info = TUNNEL_INFO(.name = name);
118
 
119
    t = find_tunnel(impl, &info);
120
 
121
    switch (event) {
122
    case AVAHI_BROWSER_NEW:
123
-       if (t != NULL)
124
+       if (t != NULL) {
125
+           pw_log_debug("found duplicate mdns entry - skipping tunnel creation");
126
            return;
127
+       }
128
        if (!(avahi_service_resolver_new(impl->client,
129
                        interface, protocol,
130
                        name, type, domain,
131
pipewire-0.3.70.tar.gz/src/modules/module-raop-sink.c -> pipewire-0.3.71.tar.gz/src/modules/module-raop-sink.c Changed
120
 
1
@@ -857,6 +857,17 @@
2
    return res;
3
 }
4
 
5
+static int rtsp_send_volume(struct impl *impl)
6
+{
7
+   if (!impl->recording)
8
+       return 0;
9
+
10
+   char header128, volstr64;
11
+   snprintf(header, sizeof(header), "volume: %s\r\n",
12
+           spa_dtoa(volstr, sizeof(volstr), impl->volume));
13
+   return rtsp_send(impl, "SET_PARAMETER", "text/parameters", header, NULL);
14
+}
15
+
16
 static int rtsp_record_reply(void *data, int status, const struct spa_dict *headers)
17
 {
18
    struct impl *impl = data;
19
@@ -892,6 +903,8 @@
20
    impl->sync_period = impl->info.rate / (impl->block_size / impl->frame_size);
21
    impl->recording = true;
22
 
23
+   rtsp_send_volume(impl);
24
+
25
    snprintf(progress, sizeof(progress), "progress: %s/%s/%s\r\n", "0", "0", "0");
26
    return rtsp_send(impl, "SET_PARAMETER", "text/parameters", progress, NULL);
27
 }
28
@@ -1582,7 +1595,6 @@
29
            uint32_t i, n_vols;
30
            float volsSPA_AUDIO_MAX_CHANNELS, volume;
31
            float soft_volsSPA_AUDIO_MAX_CHANNELS;
32
-           char header128, volstr64;
33
 
34
            if ((n_vols = spa_pod_copy_array(&prop->value, SPA_TYPE_Float,
35
                    vols, SPA_AUDIO_MAX_CHANNELS)) > 0) {
36
@@ -1595,10 +1607,9 @@
37
                volume = SPA_CLAMPF(20.0 * log10(volume), VOLUME_MIN, VOLUME_MAX);
38
                impl->volume = volume;
39
 
40
-               snprintf(header, sizeof(header), "volume: %s\r\n",
41
-                       spa_dtoa(volstr, sizeof(volstr), volume));
42
-               rtsp_send(impl, "SET_PARAMETER", "text/parameters", header, NULL);
43
+               rtsp_send_volume(impl);
44
            }
45
+
46
            spa_pod_builder_prop(&b, SPA_PROP_softVolumes, 0);
47
            spa_pod_builder_array(&b, sizeof(float), SPA_TYPE_Float,
48
                    n_vols, soft_vols);
49
@@ -1632,6 +1643,7 @@
50
    case SPA_PARAM_Props:
51
        if (param != NULL)
52
            stream_props_changed(impl, id, param);
53
+       break;
54
    default:
55
        break;
56
    }
57
@@ -1854,7 +1866,7 @@
58
    struct pw_context *context = pw_impl_module_get_context(module);
59
    struct pw_properties *props = NULL;
60
    struct impl *impl;
61
-   const char *str, *name, *hostname, *ipv;
62
+   const char *str, *name, *hostname, *ip, *port;
63
    int res;
64
 
65
    PW_LOG_TOPIC_INIT(mod_topic);
66
@@ -1895,12 +1907,23 @@
67
    impl->context = context;
68
    impl->loop = pw_context_get_main_loop(context);
69
 
70
+   ip = pw_properties_get(props, "raop.ip");
71
+   port = pw_properties_get(props, "raop.port");
72
+   if (ip == NULL || port == NULL) {
73
+       pw_log_error("Missing raop.ip or raop.port");
74
+       res = -EINVAL;
75
+       goto error;
76
+   }
77
+
78
    if (pw_properties_get(props, PW_KEY_NODE_VIRTUAL) == NULL)
79
        pw_properties_set(props, PW_KEY_NODE_VIRTUAL, "true");
80
 
81
    if (pw_properties_get(props, PW_KEY_MEDIA_CLASS) == NULL)
82
        pw_properties_set(props, PW_KEY_MEDIA_CLASS, "Audio/Sink");
83
 
84
+   if (pw_properties_get(props, PW_KEY_DEVICE_ICON_NAME) == NULL)
85
+       pw_properties_set(props, PW_KEY_DEVICE_ICON_NAME, "audio-speakers");
86
+
87
    if ((name = pw_properties_get(props, "raop.name")) == NULL)
88
        name = "RAOP";
89
 
90
@@ -1909,17 +1932,15 @@
91
        if (strlen(str) > 0)
92
            name = str;
93
    }
94
-   if ((ipv = pw_properties_get(props, "raop.ip.version")) == NULL)
95
-       ipv = "4";
96
    if ((hostname = pw_properties_get(props, "raop.hostname")) == NULL)
97
        hostname = name;
98
 
99
+   if (pw_properties_get(props, PW_KEY_NODE_NAME) == NULL)
100
+       pw_properties_setf(props, PW_KEY_NODE_NAME, "raop_sink.%s.%s.%s",
101
+               hostname, ip, port);
102
    if (pw_properties_get(props, PW_KEY_NODE_DESCRIPTION) == NULL)
103
        pw_properties_setf(props, PW_KEY_NODE_DESCRIPTION,
104
-                   "%s (IPv%s)", name, ipv);
105
-   if (pw_properties_get(props, PW_KEY_NODE_NAME) == NULL)
106
-       pw_properties_setf(props, PW_KEY_NODE_NAME, "raop_sink.%s.ipv%s",
107
-               hostname, ipv);
108
+                   "%s", name);
109
    if (pw_properties_get(props, PW_KEY_NODE_LATENCY) == NULL)
110
        pw_properties_set(props, PW_KEY_NODE_LATENCY, "352/44100");
111
 
112
@@ -1930,6 +1951,7 @@
113
    copy_props(impl, props, PW_KEY_AUDIO_RATE);
114
    copy_props(impl, props, PW_KEY_AUDIO_CHANNELS);
115
    copy_props(impl, props, SPA_KEY_AUDIO_POSITION);
116
+   copy_props(impl, props, PW_KEY_DEVICE_ICON_NAME);
117
    copy_props(impl, props, PW_KEY_NODE_NAME);
118
    copy_props(impl, props, PW_KEY_NODE_DESCRIPTION);
119
    copy_props(impl, props, PW_KEY_NODE_GROUP);
120
pipewire-0.3.70.tar.gz/src/modules/module-roc-source.c -> pipewire-0.3.71.tar.gz/src/modules/module-roc-source.c Changed
10
 
1
@@ -291,7 +291,7 @@
2
     * See API reference:
3
     * https://roc-streaming.org/toolkit/docs/api/reference.html
4
     */
5
-   receiver_config.target_latency = data->sess_latency_msec * 1000000;
6
+   receiver_config.target_latency = (unsigned long long)data->sess_latency_msec * 1000000ULL;
7
 
8
    res = roc_receiver_open(data->context, &receiver_config, &data->receiver);
9
    if (res) {
10
pipewire-0.3.70.tar.gz/src/modules/module-rt.c -> pipewire-0.3.71.tar.gz/src/modules/module-rt.c Changed
97
 
1
@@ -111,7 +111,9 @@
2
 #define PW_SCHED_RESET_ON_FORK  0
3
 #endif
4
 
5
-#define IS_VALID_NICE_LEVEL(l) ((l)>=-20 && (l)<=19)
6
+#define MIN_NICE_LEVEL     -20
7
+#define MAX_NICE_LEVEL     19
8
+#define IS_VALID_NICE_LEVEL(l) ((l)>=MIN_NICE_LEVEL && (l)<=MAX_NICE_LEVEL)
9
 
10
 #define DEFAULT_NICE_LEVEL 20
11
 #define DEFAULT_RT_PRIO_MIN    11
12
@@ -251,7 +253,7 @@
13
 bool pw_rtkit_check_xdg_portal(struct pw_rtkit_bus *system_bus)
14
 {
15
    if (!dbus_bus_name_has_owner(system_bus->bus, XDG_PORTAL_SERVICE_NAME, NULL)) {
16
-       pw_log_warn("Can't find %s. Is xdg-desktop-portal running?", XDG_PORTAL_SERVICE_NAME);
17
+       pw_log_info("Can't find %s. Is xdg-desktop-portal running?", XDG_PORTAL_SERVICE_NAME);
18
        return false;
19
    }
20
 
21
@@ -277,7 +279,14 @@
22
    if (spa_streq(name, DBUS_ERROR_ACCESS_DENIED) ||
23
        spa_streq(name, DBUS_ERROR_AUTH_FAILED))
24
        return -EACCES;
25
-
26
+   if (spa_streq(name, DBUS_ERROR_IO_ERROR))
27
+       return -EIO;
28
+   if (spa_streq(name, DBUS_ERROR_NOT_SUPPORTED))
29
+       return -ENOTSUP;
30
+   if (spa_streq(name, DBUS_ERROR_INVALID_ARGS))
31
+       return -EINVAL;
32
+   if (spa_streq(name, DBUS_ERROR_TIMED_OUT))
33
+       return -ETIMEDOUT;
34
    return -EIO;
35
 }
36
 
37
@@ -624,8 +633,16 @@
38
    int res = 0;
39
 
40
 #ifdef HAVE_DBUS
41
-   if (impl->use_rtkit)
42
+   if (impl->use_rtkit) {
43
+       int min_nice = nice_level;
44
+       pw_rtkit_get_min_nice_level(impl, &min_nice);
45
+       if (nice_level < min_nice) {
46
+           pw_log_info("clamped nice level %d to %d",
47
+                   nice_level, min_nice);
48
+           nice_level = min_nice;
49
+       }
50
        res = pw_rtkit_make_high_priority(impl, 0, nice_level);
51
+   }
52
    else
53
        res = sched_set_nice(nice_level);
54
 #else
55
@@ -979,6 +996,12 @@
56
 
57
    bool can_use_rtkit = false, use_rtkit = false;
58
 
59
+   if (!IS_VALID_NICE_LEVEL(impl->nice_level)) {
60
+       pw_log_info("invalid nice level %d (not between %d and %d). "
61
+               "nice level will not be adjusted",
62
+               impl->nice_level, MIN_NICE_LEVEL, MAX_NICE_LEVEL);
63
+   }
64
+
65
 #ifdef HAVE_DBUS
66
    spa_list_init(&impl->threads_list);
67
    pthread_mutex_init(&impl->lock, NULL);
68
@@ -992,7 +1015,8 @@
69
    if (!check_realtime_privileges(impl)) {
70
        if (!can_use_rtkit) {
71
            res = -ENOTSUP;
72
-           pw_log_warn("regular realtime scheduling not available (RTKit fallback disabled)");
73
+           pw_log_warn("regular realtime scheduling not available"
74
+                   " (Portal/RTKit fallback disabled)");
75
            goto error;
76
        }
77
        use_rtkit = true;
78
@@ -1014,7 +1038,7 @@
79
                impl->object_path = XDG_PORTAL_OBJECT_PATH;
80
                impl->interface = XDG_PORTAL_INTERFACE;
81
            } else {
82
-               pw_log_warn("found session bus but no portal");
83
+               pw_log_info("found session bus but no portal, trying RTKit fallback");
84
                pw_rtkit_bus_free(impl->rtkit_bus);
85
                impl->rtkit_bus = NULL;
86
            }
87
@@ -1028,7 +1052,8 @@
88
                impl->interface = RTKIT_INTERFACE;
89
            } else {
90
                res = -errno;
91
-               pw_log_warn("could not get system bus: %m");
92
+               pw_log_warn("Realtime scheduling disabled: unsufficient realtime privileges, "
93
+                   "Portal not found on session bus, and no system bus for RTKit: %m");
94
                goto error;
95
            }
96
        }
97
pipewire-0.3.70.tar.gz/src/modules/module-rtp-sap.c -> pipewire-0.3.71.tar.gz/src/modules/module-rtp-sap.c Changed
28
 
1
@@ -235,7 +235,7 @@
2
    struct spa_source *timer;
3
 
4
    char *ifname;
5
-   bool ttl;
6
+   uint32_t ttl;
7
    bool mcast_loop;
8
 
9
    struct sockaddr_storage src_addr;
10
@@ -266,7 +266,7 @@
11
    { SPA_MEDIA_SUBTYPE_raw, SPA_AUDIO_FORMAT_ALAW, 1, "PCMA", "audio", "ALAW" },
12
    { SPA_MEDIA_SUBTYPE_raw, SPA_AUDIO_FORMAT_ULAW, 1, "PCMU", "audio", "ULAW" },
13
    { SPA_MEDIA_SUBTYPE_raw, SPA_AUDIO_FORMAT_S16_BE, 2, "L16", "audio", "S16BE" },
14
-   { SPA_MEDIA_SUBTYPE_raw, SPA_AUDIO_FORMAT_S24_BE, 3, "L24", "audio", "S16LE" },
15
+   { SPA_MEDIA_SUBTYPE_raw, SPA_AUDIO_FORMAT_S24_BE, 3, "L24", "audio", "S24BE" },
16
    { SPA_MEDIA_SUBTYPE_control, 0, 1, "rtp-midi", "midi", NULL },
17
    { SPA_MEDIA_SUBTYPE_opus, 0, 1, "opus", "opus", NULL },
18
 };
19
@@ -307,7 +307,7 @@
20
        if (sess->announce)
21
            send_sap(impl, sess, 1);
22
        spa_list_remove(&sess->link);
23
-       impl->n_sessions++;
24
+       impl->n_sessions--;
25
    }
26
    if (sess->node && sess->node->session != NULL)
27
        sess->node->session = NULL;
28
pipewire-0.3.70.tar.gz/src/modules/module-rtp-session.c -> pipewire-0.3.71.tar.gz/src/modules/module-rtp-session.c Changed
47
 
1
@@ -564,9 +564,21 @@
2
    .send_feedback = recv_send_feedback,
3
 };
4
 
5
-static void free_session(struct session *sess)
6
+static int
7
+do_unlink_session(struct spa_loop *loop,
8
+       bool async, uint32_t seq, const void *data, size_t size, void *user_data)
9
 {
10
+   struct session *sess = user_data;
11
    spa_list_remove(&sess->link);
12
+   return 0;
13
+}
14
+
15
+static void free_session(struct session *sess)
16
+{
17
+   struct impl *impl = sess->impl;
18
+
19
+   pw_loop_invoke(impl->data_loop, do_unlink_session, 1, NULL, 0, true, sess);
20
+
21
    sess->impl->n_sessions--;
22
 
23
    if (sess->send)
24
@@ -1710,6 +1722,9 @@
25
    impl->loop = pw_context_get_main_loop(context);
26
    impl->data_loop = pw_data_loop_get_loop(pw_context_get_data_loop(context));
27
 
28
+   if (pw_properties_get(props, "sess.media") == NULL)
29
+       pw_properties_set(props, "sess.media", "midi");
30
+
31
    if ((str = pw_properties_get(props, "stream.props")) != NULL)
32
        pw_properties_update_string(stream_props, str, strlen(str));
33
 
34
@@ -1735,10 +1750,8 @@
35
    impl->ttl = pw_properties_get_uint32(props, "net.ttl", DEFAULT_TTL);
36
    impl->mcast_loop = pw_properties_get_bool(props, "net.loop", DEFAULT_LOOP);
37
 
38
-   if ((str = pw_properties_get(stream_props, "sess.media")) == NULL) {
39
-       str = "midi";
40
-       pw_properties_set(stream_props, "sess.media", str);
41
-   }
42
+   str = pw_properties_get(stream_props, "sess.media");
43
+
44
    if (spa_streq(str, "audio")) {
45
        struct spa_dict_item items = {
46
            { "audio.format", DEFAULT_FORMAT },
47
pipewire-0.3.70.tar.gz/src/modules/module-rtp-sink.c -> pipewire-0.3.71.tar.gz/src/modules/module-rtp-sink.c Changed
10
 
1
@@ -163,7 +163,7 @@
2
 
3
    char *ifname;
4
    char *session_name;
5
-   bool ttl;
6
+   uint32_t ttl;
7
    bool mcast_loop;
8
    uint32_t dscp;
9
 
10
pipewire-0.3.70.tar.gz/src/pipewire/context.c -> pipewire-0.3.71.tar.gz/src/pipewire/context.c Changed
154
 
1
@@ -38,6 +38,8 @@
2
    struct spa_plugin_loader plugin_loader;
3
    unsigned int recalc:1;
4
    unsigned int recalc_pending:1;
5
+
6
+   struct pw_data_loop *data_loop_impl;
7
 };
8
 
9
 
10
@@ -83,10 +85,11 @@
11
 
12
 static int context_set_freewheel(struct pw_context *context, bool freewheel)
13
 {
14
+   struct impl *impl = SPA_CONTAINER_OF(context, struct impl, this);
15
    struct spa_thread *thr;
16
    int res = 0;
17
 
18
-   if ((thr = pw_data_loop_get_thread(context->data_loop_impl)) == NULL)
19
+   if ((thr = pw_data_loop_get_thread(impl->data_loop_impl)) == NULL)
20
        return -EIO;
21
 
22
    if (freewheel) {
23
@@ -274,9 +277,9 @@
24
    if ((str = pw_properties_get(pr, "context.data-loop." PW_KEY_LIBRARY_NAME_SYSTEM)))
25
        pw_properties_set(pr, PW_KEY_LIBRARY_NAME_SYSTEM, str);
26
 
27
-   this->data_loop_impl = pw_data_loop_new(&pr->dict);
28
+   impl->data_loop_impl = pw_data_loop_new(&pr->dict);
29
    pw_properties_free(pr);
30
-   if (this->data_loop_impl == NULL)  {
31
+   if (impl->data_loop_impl == NULL)  {
32
        res = -errno;
33
        goto error_free;
34
    }
35
@@ -287,7 +290,7 @@
36
        goto error_free;
37
    }
38
 
39
-   this->data_loop = pw_data_loop_get_loop(this->data_loop_impl);
40
+   this->data_loop = pw_data_loop_get_loop(impl->data_loop_impl);
41
    this->data_system = this->data_loop->system;
42
    this->main_loop = main_loop;
43
 
44
@@ -353,10 +356,10 @@
45
        goto error_free;
46
    pw_log_info("%p: parsed %d context.exec items", this, res);
47
 
48
-   if ((res = pw_data_loop_start(this->data_loop_impl)) < 0)
49
+   if ((res = pw_data_loop_start(impl->data_loop_impl)) < 0)
50
        goto error_free;
51
 
52
-   pw_data_loop_invoke(this->data_loop_impl,
53
+   pw_data_loop_invoke(impl->data_loop_impl,
54
            do_data_loop_setup, 0, NULL, 0, false, this);
55
 
56
    pw_settings_expose(this);
57
@@ -409,8 +412,8 @@
58
    spa_list_consume(resource, &context->registry_resource_list, link)
59
        pw_resource_destroy(resource);
60
 
61
-   if (context->data_loop_impl)
62
-       pw_data_loop_stop(context->data_loop_impl);
63
+   if (impl->data_loop_impl)
64
+       pw_data_loop_stop(impl->data_loop_impl);
65
 
66
    spa_list_consume(module, &context->module_list, link)
67
        pw_impl_module_destroy(module);
68
@@ -427,8 +430,8 @@
69
    pw_log_debug("%p: free", context);
70
    pw_context_emit_free(context);
71
 
72
-   if (context->data_loop_impl)
73
-       pw_data_loop_destroy(context->data_loop_impl);
74
+   if (impl->data_loop_impl)
75
+       pw_data_loop_destroy(impl->data_loop_impl);
76
 
77
    if (context->pool)
78
        pw_mempool_destroy(context->pool);
79
@@ -491,7 +494,8 @@
80
 SPA_EXPORT
81
 struct pw_data_loop *pw_context_get_data_loop(struct pw_context *context)
82
 {
83
-   return context->data_loop_impl;
84
+   struct impl *impl = SPA_CONTAINER_OF(context, struct impl, this);
85
+   return impl->data_loop_impl;
86
 }
87
 
88
 SPA_EXPORT
89
@@ -799,7 +803,7 @@
90
 
91
            pw_log_debug("  peer %p: '%s'", t, t->name);
92
            t->runnable = true;
93
-           if (!t->driver)
94
+           if (!t->driving)
95
                run_nodes(context, t, nodes);
96
        }
97
    }
98
@@ -812,7 +816,7 @@
99
 
100
            pw_log_debug("  peer %p: '%s'", t, t->name);
101
            t->runnable = true;
102
-           if (!t->driver)
103
+           if (!t->driving)
104
                run_nodes(context, t, nodes);
105
        }
106
    }
107
@@ -830,7 +834,7 @@
108
 
109
            pw_log_debug("  group %p: '%s'", t, t->name);
110
            t->runnable = true;
111
-           if (!t->driver)
112
+           if (!t->driving)
113
                run_nodes(context, t, nodes);
114
        }
115
    }
116
@@ -933,7 +937,7 @@
117
        pw_log_debug(" next node %p: '%s' runnable:%u", n, n->name, n->runnable);
118
    }
119
    spa_list_for_each(n, collect, sort_link)
120
-       if (!n->driver && n->runnable)
121
+       if (!n->driving && n->runnable)
122
            run_nodes(context, n, collect);
123
 
124
    return 0;
125
@@ -1264,7 +1268,8 @@
126
        driver = NULL;
127
        spa_list_for_each(t, &collect, sort_link) {
128
            /* is any active and want a driver */
129
-           if (t->want_driver && t->active && t->runnable) {
130
+           if ((t->want_driver && t->active && t->runnable) ||
131
+               t->always_process) {
132
                driver = target;
133
                driver->runnable = true;
134
                break;
135
@@ -1609,6 +1614,7 @@
136
 int pw_context_set_object(struct pw_context *context, const char *type, void *value)
137
 {
138
    struct object_entry *entry;
139
+   struct impl *impl = SPA_CONTAINER_OF(context, struct impl, this);
140
 
141
    entry = find_object(context, type);
142
 
143
@@ -1626,8 +1632,8 @@
144
    }
145
    if (spa_streq(type, SPA_TYPE_INTERFACE_ThreadUtils)) {
146
        context->thread_utils = value;
147
-       if (context->data_loop_impl)
148
-           pw_data_loop_set_thread_utils(context->data_loop_impl,
149
+       if (impl->data_loop_impl)
150
+           pw_data_loop_set_thread_utils(impl->data_loop_impl,
151
                    context->thread_utils);
152
    }
153
    return 0;
154
pipewire-0.3.70.tar.gz/src/pipewire/data-loop.c -> pipewire-0.3.71.tar.gz/src/pipewire/data-loop.c Changed
21
 
1
@@ -51,6 +51,10 @@
2
 {
3
    struct pw_data_loop *this = user_data;
4
    int res;
5
+   struct spa_callbacks *cb = &this->loop->control->iface.cb;
6
+   const struct spa_loop_control_methods *m = cb->funcs;
7
+   void *data = cb->data;
8
+   int (*iterate) (void *object, int timeout) = m->iterate;
9
 
10
    pw_log_debug("%p: enter thread", this);
11
    pw_loop_enter(this->loop);
12
@@ -58,7 +62,7 @@
13
    pthread_cleanup_push(thread_cleanup, this);
14
 
15
    while (SPA_LIKELY(this->running)) {
16
-       if (SPA_UNLIKELY((res = pw_loop_iterate(this->loop, -1)) < 0)) {
17
+       if (SPA_UNLIKELY((res = iterate(data, -1)) < 0)) {
18
            if (res == -EINTR)
19
                continue;
20
            pw_log_error("%p: iterate error %d (%s)",
21
pipewire-0.3.70.tar.gz/src/pipewire/filter.c -> pipewire-0.3.71.tar.gz/src/pipewire/filter.c Changed
201
 
1
@@ -46,8 +46,6 @@
2
 struct queue {
3
    uint32_t idsMAX_BUFFERS;
4
    struct spa_ringbuffer ring;
5
-   uint64_t incount;
6
-   uint64_t outcount;
7
 };
8
 
9
 struct data {
10
@@ -107,6 +105,8 @@
11
    const char *path;
12
 
13
    struct pw_context *context;
14
+   struct pw_loop *main_loop;
15
+   struct pw_loop *data_loop;
16
 
17
    enum pw_filter_flags flags;
18
 
19
@@ -129,13 +129,14 @@
20
 #define NODE_PropInfo      0
21
 #define NODE_Props     1
22
 #define NODE_ProcessLatency    2
23
-#define N_NODE_PARAMS      3
24
+#define NODE_EnumFormat        3
25
+#define NODE_Format        4
26
+#define N_NODE_PARAMS      5
27
    struct spa_param_info paramsN_NODE_PARAMS;
28
 
29
    struct spa_process_latency_info process_latency;
30
 
31
    struct data data;
32
-   uintptr_t seq;
33
    struct pw_time time;
34
    uint64_t base_pos;
35
    uint32_t clock_id;
36
@@ -145,6 +146,7 @@
37
    unsigned int disconnecting:1;
38
    unsigned int disconnect_core:1;
39
    unsigned int draining:1;
40
+   unsigned int drained:1;
41
    unsigned int allow_mlock:1;
42
    unsigned int warn_mlock:1;
43
    unsigned int process_rt:1;
44
@@ -160,6 +162,10 @@
45
        return NODE_Props;
46
    case SPA_PARAM_ProcessLatency:
47
        return NODE_ProcessLatency;
48
+   case SPA_PARAM_EnumFormat:
49
+       return NODE_EnumFormat;
50
+   case SPA_PARAM_Format:
51
+       return NODE_Format;
52
    default:
53
        return -1;
54
    }
55
@@ -319,7 +325,6 @@
56
        return -EINVAL;
57
 
58
    SPA_FLAG_SET(buffer->flags, BUFFER_FLAG_QUEUED);
59
-   queue->incount += buffer->this.size;
60
 
61
    spa_ringbuffer_get_write_index(&queue->ring, &index);
62
    queue->idsindex & MASK_BUFFERS = buffer->id;
63
@@ -342,7 +347,6 @@
64
    spa_ringbuffer_read_update(&queue->ring, index + 1);
65
 
66
    buffer = &port->buffersid;
67
-   queue->outcount += buffer->this.size;
68
    SPA_FLAG_CLEAR(buffer->flags, BUFFER_FLAG_QUEUED);
69
 
70
    return buffer;
71
@@ -351,29 +355,30 @@
72
 static inline void clear_queue(struct port *port, struct queue *queue)
73
 {
74
    spa_ringbuffer_init(&queue->ring);
75
-   queue->incount = queue->outcount;
76
 }
77
 
78
-static bool filter_set_state(struct pw_filter *filter, enum pw_filter_state state, const char *error)
79
+static bool filter_set_state(struct pw_filter *filter, enum pw_filter_state state,
80
+       int res, const char *error)
81
 {
82
    enum pw_filter_state old = filter->state;
83
-   bool res = old != state;
84
+   bool changed = old != state;
85
 
86
-   if (res) {
87
+   if (changed) {
88
        free(filter->error);
89
        filter->error = error ? strdup(error) : NULL;
90
+       filter->error_res = res;
91
 
92
-       pw_log_debug("%p: update state from %s -> %s (%s)", filter,
93
+       pw_log_debug("%p: update state from %s -> %s: (%d) %s", filter,
94
                 pw_filter_state_as_string(old),
95
-                pw_filter_state_as_string(state), filter->error);
96
+                pw_filter_state_as_string(state), res, error);
97
 
98
        if (state == PW_FILTER_STATE_ERROR)
99
-           pw_log_error("%p: error %s", filter, error);
100
+           pw_log_error("%p: error (%d) %s", filter, res, error);
101
 
102
        filter->state = state;
103
        pw_filter_emit_state_changed(filter, old, state, error);
104
    }
105
-   return res;
106
+   return changed;
107
 }
108
 
109
 static int enum_params(struct filter *d, struct spa_list *param_list, int seq,
110
@@ -433,9 +438,6 @@
111
    struct filter *impl = object;
112
    struct pw_filter *filter = &impl->this;
113
 
114
-   if (id != SPA_PARAM_Props)
115
-       return -ENOTSUP;
116
-
117
    pw_filter_emit_param_changed(filter, NULL, id, param);
118
    return 0;
119
 }
120
@@ -467,7 +469,7 @@
121
            impl->position = data;
122
        else
123
            impl->position = NULL;
124
-       pw_loop_invoke(impl->context->data_loop,
125
+       pw_loop_invoke(impl->data_loop,
126
            do_set_position, 1, NULL, 0, true, impl);
127
        break;
128
    }
129
@@ -486,17 +488,17 @@
130
    case SPA_NODE_COMMAND_Suspend:
131
    case SPA_NODE_COMMAND_Flush:
132
    case SPA_NODE_COMMAND_Pause:
133
-       pw_loop_invoke(impl->context->main_loop,
134
+       pw_loop_invoke(impl->main_loop,
135
            NULL, 0, NULL, 0, false, impl);
136
        if (filter->state == PW_FILTER_STATE_STREAMING) {
137
            pw_log_debug("%p: pause", filter);
138
-           filter_set_state(filter, PW_FILTER_STATE_PAUSED, NULL);
139
+           filter_set_state(filter, PW_FILTER_STATE_PAUSED, 0, NULL);
140
        }
141
        break;
142
    case SPA_NODE_COMMAND_Start:
143
        if (filter->state == PW_FILTER_STATE_PAUSED) {
144
            pw_log_debug("%p: start", filter);
145
-           filter_set_state(filter, PW_FILTER_STATE_STREAMING, NULL);
146
+           filter_set_state(filter, PW_FILTER_STATE_STREAMING, 0, NULL);
147
        }
148
        break;
149
    default:
150
@@ -849,7 +851,7 @@
151
        pw_filter_emit_param_changed(filter, port->user_data, id, param);
152
 
153
    if (filter->state == PW_FILTER_STATE_ERROR)
154
-       return -EIO;
155
+       return filter->error_res;
156
 
157
    emit_port_info(impl, port, false);
158
 
159
@@ -951,23 +953,6 @@
160
    return 0;
161
 }
162
 
163
-static inline void copy_position(struct filter *impl)
164
-{
165
-   struct spa_io_position *p = impl->rt.position;
166
-   if (SPA_UNLIKELY(p != NULL)) {
167
-       SEQ_WRITE(impl->seq);
168
-       impl->time.now = p->clock.nsec;
169
-       impl->time.rate = p->clock.rate;
170
-       if (SPA_UNLIKELY(impl->clock_id != p->clock.id)) {
171
-           impl->base_pos = p->clock.position - impl->time.ticks;
172
-           impl->clock_id = p->clock.id;
173
-       }
174
-       impl->time.ticks = p->clock.position - impl->base_pos;
175
-       impl->time.delay = 0;
176
-       SEQ_WRITE(impl->seq);
177
-   }
178
-}
179
-
180
 static int
181
 do_call_process(struct spa_loop *loop,
182
                  bool async, uint32_t seq, const void *data, size_t size, void *user_data)
183
@@ -987,7 +972,7 @@
184
                process, 0, impl->rt.position);
185
    }
186
    else {
187
-       pw_loop_invoke(impl->context->main_loop,
188
+       pw_loop_invoke(impl->main_loop,
189
            do_call_process, 1, NULL, 0, false, impl);
190
    }
191
 }
192
@@ -1000,13 +985,12 @@
193
    struct pw_filter *filter = &impl->this;
194
    pw_log_trace("%p: drained", filter);
195
    pw_filter_emit_drained(filter);
196
-   impl->draining = false;
197
    return 0;
198
 }
199
 
200
 static void call_drained(struct filter *impl)
201
pipewire-0.3.70.tar.gz/src/pipewire/impl-client.c -> pipewire-0.3.71.tar.gz/src/pipewire/impl-client.c Changed
49
 
1
@@ -101,20 +101,45 @@
2
 {
3
    struct pw_resource *r = object;
4
    struct error_data *d = data;
5
-   if (r && r->bound_id == d->id)
6
+
7
+   if (r && r->bound_id == d->id) {
8
+       pw_log_debug("%p: client error for global %u: %d (%s)",
9
+               r, d->id, d->res, d->error);
10
        pw_resource_error(r, d->res, d->error);
11
+   }
12
    return 0;
13
 }
14
 
15
 static int client_error(void *object, uint32_t id, int res, const char *error)
16
 {
17
    struct resource_data *data = object;
18
+   struct pw_resource *resource = data->resource;
19
+   struct pw_impl_client *sender = resource->client;
20
    struct pw_impl_client *client = data->client;
21
    struct error_data d = { id, res, error };
22
+   struct pw_global *global;
23
 
24
-   pw_log_debug("%p: error for global %d", client, id);
25
+   /* Check the global id provided by sender refers to a registered global
26
+    * known to the sender.
27
+    */
28
+   if ((global = pw_context_find_global(resource->context, id)) == NULL)
29
+       goto error_no_id;
30
+   if (sender->recv_generation != 0 && global->generation > sender->recv_generation)
31
+       goto error_stale_id;
32
+
33
+   pw_log_debug("%p: sender %p: error for global %u", client, sender, id);
34
    pw_map_for_each(&client->objects, error_resource, &d);
35
    return 0;
36
+
37
+error_no_id:
38
+   pw_log_debug("%p: sender %p: error for invalid global %u", client, sender, id);
39
+   pw_resource_errorf(resource, -ENOENT, "no global %u", id);
40
+   return -ENOENT;
41
+error_stale_id:
42
+   pw_log_debug("%p: sender %p: error for stale global %u generation:%"PRIu64" recv-generation:%"PRIu64,
43
+           client, sender, id, global->generation, sender->recv_generation);
44
+   pw_resource_errorf(resource, -ESTALE, "no global %u any more", id);
45
+   return -ESTALE;
46
 }
47
 
48
 static bool has_key(const char * const keys, const char *key)
49
pipewire-0.3.70.tar.gz/src/pipewire/impl-link.c -> pipewire-0.3.71.tar.gz/src/pipewire/impl-link.c Changed
182
 
1
@@ -52,6 +52,81 @@
2
 
3
 /** \endcond */
4
 
5
+static struct pw_node_peer *pw_node_peer_ref(struct pw_impl_node *onode, struct pw_impl_node *inode)
6
+{
7
+   struct pw_node_peer *peer;
8
+
9
+   spa_list_for_each(peer, &onode->peer_list, link) {
10
+       if (peer->target.node == inode) {
11
+           pw_log_debug("exiting peer %p from %p to %p", peer, onode, inode);
12
+           peer->ref++;
13
+           return peer;
14
+       }
15
+   }
16
+   peer = calloc(1, sizeof(*peer));
17
+   if (peer == NULL)
18
+       return NULL;
19
+
20
+   peer->ref = 1;
21
+   peer->output = onode;
22
+   peer->active_count = 0;
23
+   peer->target.node = inode;
24
+   peer->target.activation = inode->rt.activation;
25
+   peer->target.system = inode->data_system;
26
+   peer->target.fd = inode->source.fd;
27
+
28
+   spa_list_append(&onode->peer_list, &peer->link);
29
+   pw_log_debug("new peer %p from %p to %p", peer, onode, inode);
30
+   pw_impl_node_emit_peer_added(onode, inode);
31
+
32
+   return peer;
33
+}
34
+
35
+static void pw_node_peer_unref(struct pw_node_peer *peer)
36
+{
37
+   if (--peer->ref > 0)
38
+       return;
39
+   spa_list_remove(&peer->link);
40
+   pw_log_debug("remove peer %p from %p to %p", peer, peer->output, peer->target.node);
41
+   pw_impl_node_emit_peer_removed(peer->output, peer->target.node);
42
+   free(peer);
43
+}
44
+
45
+static void pw_node_peer_activate(struct pw_node_peer *peer)
46
+{
47
+   struct pw_node_activation_state *state;
48
+
49
+   state = &peer->target.activation->state0;
50
+
51
+   if (peer->active_count++ == 0) {
52
+       spa_list_append(&peer->output->rt.target_list, &peer->target.link);
53
+       if (!peer->target.active && peer->output->rt.driver_target.node != NULL) {
54
+           state->required++;
55
+           peer->target.active = true;
56
+       }
57
+   }
58
+   pw_log_trace("%p: node:%p state:%p pending:%d/%d", peer->output,
59
+           peer->target.node, state, state->pending, state->required);
60
+}
61
+
62
+static void pw_node_peer_deactivate(struct pw_node_peer *peer)
63
+{
64
+   struct pw_node_activation_state *state;
65
+   state = &peer->target.activation->state0;
66
+   if (--peer->active_count == 0) {
67
+
68
+       spa_list_remove(&peer->target.link);
69
+
70
+       if (peer->target.active) {
71
+           state->required--;
72
+           peer->target.active = false;
73
+       }
74
+   }
75
+   pw_log_trace("%p: node:%p state:%p pending:%d/%d", peer->output,
76
+           peer->target.node, state, state->pending, state->required);
77
+}
78
+
79
+
80
 static void info_changed(struct pw_impl_link *link)
81
 {
82
    struct pw_resource *resource;
83
@@ -566,28 +641,15 @@
84
         bool async, uint32_t seq, const void *data, size_t size, void *user_data)
85
 {
86
    struct pw_impl_link *this = user_data;
87
-   struct impl *impl = SPA_CONTAINER_OF(this, struct impl, this);
88
 
89
    pw_log_trace("%p: activate", this);
90
 
91
    spa_list_append(&this->output->rt.mix_list, &this->rt.out_mix.rt_link);
92
    spa_list_append(&this->input->rt.mix_list, &this->rt.in_mix.rt_link);
93
 
94
-   if (impl->inode != impl->onode) {
95
-       struct pw_node_activation_state *state;
96
-
97
-       this->rt.target.activation = impl->inode->rt.activation;
98
-       spa_list_append(&impl->onode->rt.target_list, &this->rt.target.link);
99
-
100
-       state = &this->rt.target.activation->state0;
101
-       if (!this->rt.target.active && impl->onode->rt.driver_target.node != NULL) {
102
-           state->required++;
103
-           this->rt.target.active = true;
104
-       }
105
+   if (this->peer)
106
+       pw_node_peer_activate(this->peer);
107
 
108
-       pw_log_trace("%p: node:%p state:%p pending:%d/%d", this, impl->inode,
109
-               state, state->pending, state->required);
110
-   }
111
    return 0;
112
 }
113
 
114
@@ -600,7 +662,7 @@
115
            pw_link_state_as_string(this->info.state));
116
 
117
    if (impl->activated || !this->prepared ||
118
-       !impl->inode->active || !impl->onode->active)
119
+       !impl->inode->runnable || !impl->onode->runnable)
120
        return 0;
121
 
122
    if (!impl->io_set) {
123
@@ -782,26 +844,14 @@
124
           bool async, uint32_t seq, const void *data, size_t size, void *user_data)
125
 {
126
         struct pw_impl_link *this = user_data;
127
-   struct impl *impl = SPA_CONTAINER_OF(this, struct impl, this);
128
 
129
    pw_log_trace("%p: disable %p and %p", this, &this->rt.in_mix, &this->rt.out_mix);
130
 
131
    spa_list_remove(&this->rt.out_mix.rt_link);
132
    spa_list_remove(&this->rt.in_mix.rt_link);
133
 
134
-   if (impl->inode != impl->onode) {
135
-       struct pw_node_activation_state *state;
136
-
137
-       spa_list_remove(&this->rt.target.link);
138
-       state = &this->rt.target.activation->state0;
139
-       if (this->rt.target.active) {
140
-           state->required--;
141
-           this->rt.target.active = false;
142
-       }
143
-
144
-       pw_log_trace("%p: node:%p state:%p pending:%d/%d", this, impl->inode,
145
-               state, state->pending, state->required);
146
-   }
147
+   if (this->peer)
148
+       pw_node_peer_deactivate(this->peer);
149
 
150
    return 0;
151
 }
152
@@ -1252,9 +1302,6 @@
153
        impl->inode = input_node;
154
    }
155
 
156
-   this->rt.target.signal_func = impl->inode->rt.target.signal_func;
157
-   this->rt.target.data = impl->inode->rt.target.data;
158
-
159
    pw_log_debug("%p: constructed out:%p:%d.%d -> in:%p:%d.%d", impl,
160
             output_node, output->port_id, this->rt.out_mix.port.port_id,
161
             input_node, input->port_id, this->rt.in_mix.port.port_id);
162
@@ -1272,7 +1319,8 @@
163
    pw_impl_port_recalc_latency(output);
164
    pw_impl_port_recalc_latency(input);
165
 
166
-   pw_impl_node_emit_peer_added(impl->onode, impl->inode);
167
+   if (impl->onode != impl->inode)
168
+       this->peer = pw_node_peer_ref(impl->onode, impl->inode);
169
 
170
    return this;
171
 
172
@@ -1407,7 +1455,8 @@
173
    if (link->registered)
174
        spa_list_remove(&link->link);
175
 
176
-   pw_impl_node_emit_peer_removed(impl->onode, impl->inode);
177
+   if (link->peer)
178
+       pw_node_peer_unref(link->peer);
179
 
180
    try_unlink_controls(impl, link->output, link->input);
181
 
182
pipewire-0.3.70.tar.gz/src/pipewire/impl-node.c -> pipewire-0.3.71.tar.gz/src/pipewire/impl-node.c Changed
201
 
1
@@ -41,6 +41,8 @@
2
 
3
    unsigned int cache_params:1;
4
    unsigned int pending_play:1;
5
+
6
+   uint64_t prev_signal_time;
7
 };
8
 
9
 #define pw_node_resource(r,m,v,...)    pw_resource_call(r,struct pw_node_events,m,v,__VA_ARGS__)
10
@@ -65,6 +67,19 @@
11
 
12
 /** \endcond */
13
 
14
+/* Called from the node data loop when a node needs to be scheduled by
15
+ * the given driver. 3 things needs to happen:
16
+ *
17
+ * - the node is added to the driver target list and the required state
18
+ *   is incremented. This makes sure the node is woken up when the driver
19
+ *   starts a new cycle.
20
+ * - the node needs to trigger the driver when it completes. This means
21
+ *   the driver is added to the target list.
22
+ * - the node targets (including the driver we added above) have their
23
+ *   required state incremented.
24
+ *
25
+ * This code is called from the data-loop to ensure synchronization
26
+ */
27
 static void add_node(struct pw_impl_node *this, struct pw_impl_node *driver)
28
 {
29
    struct pw_node_activation_state *dstate, *nstate;
30
@@ -76,12 +91,7 @@
31
    pw_log_trace("%p: add to driver %p %p %p", this, driver,
32
            driver->rt.activation, this->rt.activation);
33
 
34
-   /* signal the driver */
35
-   this->rt.driver_target.activation = driver->rt.activation;
36
-   this->rt.driver_target.node = driver;
37
-   this->rt.driver_target.data = driver;
38
-   spa_list_append(&this->rt.target_list, &this->rt.driver_target.link);
39
-
40
+   /* let the driver trigger us as part of the processing cycle */
41
    spa_list_append(&driver->rt.target_list, &this->rt.target.link);
42
    nstate = &this->rt.activation->state0;
43
    if (!this->rt.target.active) {
44
@@ -89,6 +99,15 @@
45
        this->rt.target.active = true;
46
    }
47
 
48
+   /* trigger the driver when we complete */
49
+   this->rt.driver_target.activation = driver->rt.activation;
50
+   this->rt.driver_target.node = driver;
51
+   this->rt.driver_target.system = driver->data_system;
52
+   this->rt.driver_target.fd = driver->source.fd;
53
+   spa_list_append(&this->rt.target_list, &this->rt.driver_target.link);
54
+
55
+   /* now increment the required states of all this node targets, including
56
+    * the driver we added above */
57
    spa_list_for_each(t, &this->rt.target_list, link) {
58
        dstate = &t->activation->state0;
59
        if (!t->active) {
60
@@ -101,6 +120,7 @@
61
    }
62
 }
63
 
64
+/* called from the data loop and undoes the changes done in add_node.  */
65
 static void remove_node(struct pw_impl_node *this)
66
 {
67
    struct pw_node_activation_state *dstate, *nstate;
68
@@ -110,7 +130,7 @@
69
        return;
70
 
71
    pw_log_trace("%p: remove from driver %p %p %p",
72
-           this, this->rt.driver_target.data,
73
+           this, this->rt.driver_target.node,
74
            this->rt.driver_target.activation, this->rt.activation);
75
 
76
    spa_list_remove(&this->rt.target.link);
77
@@ -137,37 +157,41 @@
78
 }
79
 
80
 static int
81
-do_node_add(struct spa_loop *loop, bool async, uint32_t seq, const void *data, size_t size, void *user_data)
82
+do_node_add(struct spa_loop *loop, bool async, uint32_t seq,
83
+       const void *data, size_t size, void *user_data)
84
 {
85
    struct pw_impl_node *this = user_data;
86
    struct pw_impl_node *driver = this->driver_node;
87
 
88
-   this->added = true;
89
-   if (this->source.loop == NULL) {
90
-       struct spa_system *data_system = this->context->data_system;
91
+   if (!this->added) {
92
        uint64_t dummy;
93
        int res;
94
 
95
        /* clear the eventfd in case it was written to while the node was stopped */
96
-       res = spa_system_eventfd_read(data_system, this->source.fd, &dummy);
97
+       res = spa_system_eventfd_read(this->data_system, this->source.fd, &dummy);
98
        if (SPA_UNLIKELY(res != -EAGAIN && res != 0))
99
            pw_log_warn("%p: read failed %m", this);
100
 
101
-       spa_loop_add_source(loop, &this->source);
102
+       this->added = true;
103
+       /* remote nodes have their source added in client-node instead */
104
+       if (!this->remote)
105
+           spa_loop_add_source(loop, &this->source);
106
        add_node(this, driver);
107
    }
108
    return 0;
109
 }
110
 
111
 static int
112
-do_node_remove(struct spa_loop *loop, bool async, uint32_t seq, const void *data, size_t size, void *user_data)
113
+do_node_remove(struct spa_loop *loop, bool async, uint32_t seq,
114
+       const void *data, size_t size, void *user_data)
115
 {
116
    struct pw_impl_node *this = user_data;
117
-   if (this->source.loop != NULL) {
118
-       spa_loop_remove_source(loop, &this->source);
119
+   if (this->added) {
120
+       if (!this->remote)
121
+           spa_loop_remove_source(loop, &this->source);
122
        remove_node(this);
123
+       this->added = false;
124
    }
125
-   this->added = false;
126
    return 0;
127
 }
128
 
129
@@ -572,8 +596,12 @@
130
 {
131
    struct resource_data *data = object;
132
    struct pw_impl_node *node = data->node;
133
+   uint32_t id = SPA_NODE_COMMAND_ID(command);
134
 
135
-   switch (SPA_NODE_COMMAND_ID(command)) {
136
+   pw_log_debug("%p: got command %d (%s)", node, id,
137
+           spa_debug_type_find_name(spa_type_node_command_id, id));
138
+
139
+   switch (id) {
140
    case SPA_NODE_COMMAND_Suspend:
141
        suspend_node(node);
142
        break;
143
@@ -808,7 +836,7 @@
144
    node->target_rate = node->rt.position->clock.target_rate;
145
    node->target_quantum = node->rt.position->clock.target_duration;
146
 
147
-   if (node->source.loop != NULL) {
148
+   if (node->added) {
149
        remove_node(node);
150
        add_node(node, driver);
151
    }
152
@@ -841,18 +869,6 @@
153
 
154
    remove_segment_owner(old, node->info.id);
155
 
156
-   if (old != node && old->driving && driver->info.state < PW_NODE_STATE_RUNNING) {
157
-       pw_log_info("move quantum:%"PRIu64"->%"PRIu64" rate:%d->%d (%s-%d -> %s-%d)",
158
-               driver->target_quantum,
159
-               old->target_quantum,
160
-               driver->target_rate.denom,
161
-               old->target_rate.denom,
162
-               old->name, old->info.id,
163
-               driver->name, driver->info.id);
164
-       driver->target_rate = old->target_rate;
165
-       driver->target_quantum = old->target_quantum;
166
-       driver->target_pending = true;
167
-   }
168
    was_driving = node->driving;
169
    node->driving = node->driver && driver == node;
170
 
171
@@ -924,6 +940,8 @@
172
            else
173
                spa_list_remove(&node->driver_link);
174
        }
175
+       if (driver && node->driver_node == node)
176
+           node->driving = true;
177
        recalc_reason = "driver changed";
178
    }
179
 
180
@@ -1090,20 +1108,26 @@
181
    }
182
 }
183
 
184
-static inline int resume_node(struct pw_impl_node *this, int status)
185
+static inline uint64_t get_time_ns(struct spa_system *system)
186
 {
187
-   struct pw_node_target *t;
188
    struct timespec ts;
189
-   struct pw_node_activation *activation = this->rt.activation;
190
-   struct spa_system *data_system = this->context->data_system;
191
-   uint64_t nsec;
192
+   spa_system_clock_gettime(system, CLOCK_MONOTONIC, &ts);
193
+   return SPA_TIMESPEC_TO_NSEC(&ts);
194
+}
195
 
196
-   spa_system_clock_gettime(data_system, CLOCK_MONOTONIC, &ts);
197
-   nsec = SPA_TIMESPEC_TO_NSEC(&ts);
198
-   activation->status = PW_NODE_ACTIVATION_FINISHED;
199
-   activation->finish_time = nsec;
200
+static inline void node_trigger(struct pw_impl_node *this)
201
pipewire-0.3.70.tar.gz/src/pipewire/impl-port.c -> pipewire-0.3.71.tar.gz/src/pipewire/impl-port.c Changed
42
 
1
@@ -914,6 +914,7 @@
2
        PW_KEY_PORT_CONTROL,
3
        PW_KEY_PORT_ALIAS,
4
        PW_KEY_PORT_EXTRA,
5
+       PW_KEY_PORT_IGNORE_LATENCY,
6
        NULL
7
    };
8
 
9
@@ -992,6 +993,8 @@
10
 
11
    is_monitor = pw_properties_get_bool(port->properties, PW_KEY_PORT_MONITOR, false);
12
 
13
+   port->ignore_latency = pw_properties_get_bool(port->properties, PW_KEY_PORT_IGNORE_LATENCY, false);
14
+
15
    is_control = PW_IMPL_PORT_IS_CONTROL(port);
16
    if (is_control) {
17
        dir = port->direction == PW_DIRECTION_INPUT ?  "control" : "notify";
18
@@ -1440,6 +1443,11 @@
19
    if (port->direction == PW_DIRECTION_OUTPUT) {
20
        spa_list_for_each(l, &port->links, output_link) {
21
            other = l->input;
22
+           if (other->ignore_latency) {
23
+               pw_log_debug("port %d: peer %d: peer latency ignored",
24
+                       port->info.id, other->info.id);
25
+               continue;
26
+           }
27
            spa_latency_info_combine(&latency, &other->latencyother->direction);
28
            pw_log_debug("port %d: peer %d: latency %f-%f %d-%d %"PRIu64"-%"PRIu64,
29
                    port->info.id, other->info.id,
30
@@ -1450,6 +1458,11 @@
31
    } else {
32
        spa_list_for_each(l, &port->links, input_link) {
33
            other = l->output;
34
+           if (other->ignore_latency) {
35
+               pw_log_debug("port %d: peer %d: peer latency ignored",
36
+                       port->info.id, other->info.id);
37
+               continue;
38
+           }
39
            spa_latency_info_combine(&latency, &other->latencyother->direction);
40
            pw_log_debug("port %d: peer %d: latency %f-%f %d-%d %"PRIu64"-%"PRIu64,
41
                    port->info.id, other->info.id,
42
pipewire-0.3.70.tar.gz/src/pipewire/keys.h -> pipewire-0.3.71.tar.gz/src/pipewire/keys.h Changed
21
 
1
@@ -197,6 +197,7 @@
2
 #define PW_KEY_PORT_EXTRA      "port.extra"        /**< api specific extra port info, API name
3
                                  *  should be prefixed. "jack:flags:56" */
4
 #define PW_KEY_PORT_PASSIVE        "port.passive"      /**< the ports wants passive links, since 0.3.67 */
5
+#define PW_KEY_PORT_IGNORE_LATENCY "port.ignore-latency"   /**< latency ignored by peers, since 0.3.71 */
6
 
7
 /** link properties */
8
 #define PW_KEY_LINK_ID         "link.id"       /**< a link id */
9
@@ -274,7 +275,10 @@
10
 #define PW_KEY_STREAM_LATENCY_MAX  "stream.latency.max"    /**< The maximum latency of the stream */
11
 #define PW_KEY_STREAM_MONITOR      "stream.monitor"    /**< Indicates that the stream is monitoring
12
                                  *  and might select a less accurate but faster
13
-                                 *  conversion algorithm. */
14
+                                 *  conversion algorithm. Monitor streams are also
15
+                                 *  ignored when calculating the latency of their peer
16
+                                 *  ports (since 0.3.71).
17
+                                 */
18
 #define PW_KEY_STREAM_DONT_REMIX   "stream.dont-remix" /**< don't remix channels */
19
 #define PW_KEY_STREAM_CAPTURE_SINK "stream.capture.sink"   /**< Try to capture the sink output instead of
20
                                  *  source output */
21
pipewire-0.3.70.tar.gz/src/pipewire/loop.c -> pipewire-0.3.71.tar.gz/src/pipewire/loop.c Changed
14
 
1
@@ -109,6 +109,12 @@
2
                 goto error_unload_loop;
3
         }
4
    this->control = iface;
5
+   if (!spa_interface_callback_check(&this->control->iface,
6
+           struct spa_loop_control_methods, iterate, 0)) {
7
+       res = -EINVAL;
8
+       pw_log_error("%p: loop does not support iterate", this);
9
+       goto error_unload_loop;
10
+   }
11
 
12
         if ((res = spa_handle_get_interface(impl->loop_handle,
13
                        SPA_TYPE_INTERFACE_LoopUtils,
14
pipewire-0.3.70.tar.gz/src/pipewire/loop.h -> pipewire-0.3.71.tar.gz/src/pipewire/loop.h Changed
11
 
1
@@ -45,8 +45,8 @@
2
 #define pw_loop_get_fd(l)      spa_loop_control_get_fd((l)->control)
3
 #define pw_loop_add_hook(l,...)        spa_loop_control_add_hook((l)->control,__VA_ARGS__)
4
 #define pw_loop_enter(l)       spa_loop_control_enter((l)->control)
5
-#define pw_loop_iterate(l,...)     spa_loop_control_iterate((l)->control,__VA_ARGS__)
6
 #define pw_loop_leave(l)       spa_loop_control_leave((l)->control)
7
+#define pw_loop_iterate(l,...)     spa_loop_control_iterate_fast((l)->control,__VA_ARGS__)
8
 
9
 #define pw_loop_add_io(l,...)      spa_loop_utils_add_io((l)->utils,__VA_ARGS__)
10
 #define pw_loop_update_io(l,...)   spa_loop_utils_update_io((l)->utils,__VA_ARGS__)
11
pipewire-0.3.70.tar.gz/src/pipewire/private.h -> pipewire-0.3.71.tar.gz/src/pipewire/private.h Changed
128
 
1
@@ -389,10 +389,10 @@
2
 #define ensure_loop(loop,...) ({                           \
3
    int res = pw_loop_check(loop);                          \
4
    if (res != 1) {                                 \
5
-       pw_log_warn("%s called from wrong context, check thread and locking: %s",       \
6
-               __func__, spa_strerror(res));               \
7
+       pw_log_warn("%s called from wrong context, check thread and locking: %s",   \
8
+               __func__, res < 0 ? spa_strerror(res) : "Not in loop"); \
9
        fprintf(stderr, "*** %s called from wrong context, check thread and locking: %s\n",\
10
-               __func__, spa_strerror(res));               \
11
+               __func__, res < 0 ? spa_strerror(res) : "Not in loop"); \
12
        /* __VA_ARGS__ */                           \
13
    }                                       \
14
 })
15
@@ -475,7 +475,6 @@
16
    struct spa_thread_utils *thread_utils;
17
    struct pw_loop *main_loop;      /**< main loop for control */
18
    struct pw_loop *data_loop;      /**< data loop for data passing */
19
-   struct pw_data_loop *data_loop_impl;
20
    struct spa_system *data_system;     /**< data system for data passing */
21
    struct pw_work_queue *work_queue;   /**< work queue */
22
 
23
@@ -587,8 +586,8 @@
24
    struct spa_list link;
25
    struct pw_impl_node *node;
26
    struct pw_node_activation *activation;
27
-   int (*signal_func) (void *data);
28
-   void *data;
29
+   struct spa_system *system;
30
+   int fd;
31
    unsigned int active:1;
32
 };
33
 
34
@@ -739,6 +738,8 @@
35
 
36
    struct spa_list sort_link;  /**< link used to sort nodes */
37
 
38
+   struct spa_list peer_list;  /* list of peers */
39
+
40
    struct spa_node *node;      /**< SPA node implementation */
41
    struct spa_hook listener;
42
 
43
@@ -750,6 +751,7 @@
44
    struct spa_hook_list listener_list;
45
 
46
    struct pw_loop *data_loop;      /**< the data loop for this node */
47
+   struct spa_system *data_system;
48
 
49
    struct spa_fraction latency;        /**< requested latency */
50
    struct spa_fraction max_latency;    /**< maximum latency */
51
@@ -893,6 +895,7 @@
52
 
53
    struct spa_latency_info latency2;   /**< latencies */
54
    unsigned int have_latency_param:1;
55
+   unsigned int ignore_latency:1;
56
 
57
    void *owner_data;       /**< extra owner data */
58
    void *user_data;                /**< extra user data */
59
@@ -908,6 +911,14 @@
60
    unsigned int valid:1;
61
 };
62
 
63
+struct pw_node_peer {
64
+   int ref;
65
+   int active_count;
66
+   struct spa_list link;           /**< link in peer list */
67
+   struct pw_impl_node *output;        /**< the output node */
68
+   struct pw_node_target target;       /**< target of the input node */
69
+};
70
+
71
 #define pw_impl_link_emit(o,m,v,...) spa_hook_list_call(&o->listener_list, struct pw_impl_link_events, m, v, ##__VA_ARGS__)
72
 #define pw_impl_link_emit_destroy(l)       pw_impl_link_emit(l, destroy, 0)
73
 #define pw_impl_link_emit_free(l)      pw_impl_link_emit(l, free, 0)
74
@@ -939,10 +950,11 @@
75
    struct pw_control_link control;
76
    struct pw_control_link notify;
77
 
78
+   struct pw_node_peer *peer;
79
+
80
    struct {
81
        struct pw_impl_port_mix out_mix;    /**< port added to the output mixer */
82
        struct pw_impl_port_mix in_mix;     /**< port added to the input mixer */
83
-       struct pw_node_target target;       /**< target to trigger the input node */
84
    } rt;
85
 
86
    void *user_data;
87
@@ -1076,12 +1088,14 @@
88
                          *  CONFIGURE state and higher */
89
    enum pw_stream_state state;     /**< stream state */
90
    char *error;                /**< error reason when state is in error */
91
+   int error_res;              /**< error code when in error */
92
 
93
    struct spa_hook_list listener_list;
94
 
95
    struct pw_proxy *proxy;
96
    struct spa_hook proxy_listener;
97
 
98
+   struct pw_impl_node *node;
99
    struct spa_hook node_listener;
100
 
101
    struct spa_list controls;
102
@@ -1112,12 +1126,16 @@
103
                          *  CONFIGURE state and higher */
104
    enum pw_filter_state state;     /**< filter state */
105
    char *error;                /**< error reason when state is in error */
106
+   int error_res;              /**< error code when in error */
107
 
108
    struct spa_hook_list listener_list;
109
 
110
    struct pw_proxy *proxy;
111
    struct spa_hook proxy_listener;
112
 
113
+   struct pw_impl_node *node;
114
+   struct spa_hook node_listener;
115
+
116
    struct spa_list controls;
117
 };
118
 
119
@@ -1263,6 +1281,8 @@
120
 
121
 int pw_impl_node_set_driver(struct pw_impl_node *node, struct pw_impl_node *driver);
122
 
123
+int pw_impl_node_trigger(struct pw_impl_node *node);
124
+
125
 /** Prepare a link
126
   * Starts the negotiation of formats and buffers on \a link */
127
 int pw_impl_link_prepare(struct pw_impl_link *link);
128
pipewire-0.3.70.tar.gz/src/pipewire/properties.c -> pipewire-0.3.71.tar.gz/src/pipewire/properties.c Changed
11
 
1
@@ -788,6 +788,9 @@
2
        if ((len = spa_json_next(&sub, &value)) < 0)
3
            break;
4
 
5
+       if (!spa_json_is_container(value, len))
6
+           len = value ? strlen(value) : 0;
7
+
8
        dump(c, c->indent, &sub, value, len);
9
        count++;
10
    }
11
pipewire-0.3.70.tar.gz/src/pipewire/stream.c -> pipewire-0.3.71.tar.gz/src/pipewire/stream.c Changed
201
 
1
@@ -85,11 +85,12 @@
2
    struct pw_context *context;
3
    struct spa_hook context_listener;
4
 
5
+   struct pw_loop *main_loop;
6
+   struct pw_loop *data_loop;
7
+
8
    enum spa_direction direction;
9
    enum pw_stream_flags flags;
10
 
11
-   struct pw_impl_node *node;
12
-
13
    struct spa_node impl_node;
14
    struct spa_node_methods node_methods;
15
    struct spa_hook_list hooks;
16
@@ -351,26 +352,28 @@
17
    queue->incount = queue->outcount;
18
 }
19
 
20
-static bool stream_set_state(struct pw_stream *stream, enum pw_stream_state state, const char *error)
21
+static bool stream_set_state(struct pw_stream *stream, enum pw_stream_state state,
22
+       int res, const char *error)
23
 {
24
    enum pw_stream_state old = stream->state;
25
-   bool res = old != state;
26
+   bool changed = old != state;
27
 
28
-   if (res) {
29
+   if (changed) {
30
        free(stream->error);
31
        stream->error = error ? strdup(error) : NULL;
32
+       stream->error_res = res;
33
 
34
-       pw_log_debug("%p: update state from %s -> %s (%s)", stream,
35
+       pw_log_debug("%p: update state from %s -> %s (%d) %s", stream,
36
                 pw_stream_state_as_string(old),
37
-                pw_stream_state_as_string(state), stream->error);
38
+                pw_stream_state_as_string(state), res, stream->error);
39
 
40
        if (state == PW_STREAM_STATE_ERROR)
41
-           pw_log_error("%p: error %s", stream, error);
42
+           pw_log_error("%p: error (%d) %s", stream, res, error);
43
 
44
        stream->state = state;
45
        pw_stream_emit_state_changed(stream, old, state, error);
46
    }
47
-   return res;
48
+   return changed;
49
 }
50
 
51
 static struct buffer *get_buffer(struct pw_stream *stream, uint32_t id)
52
@@ -426,7 +429,7 @@
53
    if (impl->process_rt)
54
        spa_callbacks_call(&impl->rt_callbacks, struct pw_stream_events, process, 0);
55
    else
56
-       pw_loop_invoke(impl->context->main_loop,
57
+       pw_loop_invoke(impl->main_loop,
58
            do_call_process, 1, NULL, 0, false, impl);
59
 }
60
 
61
@@ -443,7 +446,7 @@
62
 
63
 static void call_drained(struct stream *impl)
64
 {
65
-   pw_loop_invoke(impl->context->main_loop,
66
+   pw_loop_invoke(impl->main_loop,
67
        do_call_drained, 1, NULL, 0, false, impl);
68
 }
69
 
70
@@ -460,7 +463,7 @@
71
 
72
 static void call_trigger_done(struct stream *impl)
73
 {
74
-   pw_loop_invoke(impl->context->main_loop,
75
+   pw_loop_invoke(impl->main_loop,
76
        do_call_trigger_done, 1, NULL, 0, false, impl);
77
 }
78
 
79
@@ -494,7 +497,7 @@
80
        else
81
            impl->position = NULL;
82
 
83
-       pw_loop_invoke(impl->context->data_loop,
84
+       pw_loop_invoke(impl->data_loop,
85
                do_set_position, 1, NULL, 0, true, impl);
86
        break;
87
    default:
88
@@ -607,12 +610,12 @@
89
    case SPA_NODE_COMMAND_Suspend:
90
    case SPA_NODE_COMMAND_Flush:
91
    case SPA_NODE_COMMAND_Pause:
92
-       pw_loop_invoke(impl->context->main_loop,
93
+       pw_loop_invoke(impl->main_loop,
94
            NULL, 0, NULL, 0, false, impl);
95
        if (stream->state == PW_STREAM_STATE_STREAMING) {
96
 
97
            pw_log_debug("%p: pause", stream);
98
-           stream_set_state(stream, PW_STREAM_STATE_PAUSED, NULL);
99
+           stream_set_state(stream, PW_STREAM_STATE_PAUSED, 0, NULL);
100
        }
101
        break;
102
    case SPA_NODE_COMMAND_Start:
103
@@ -628,7 +631,7 @@
104
                call_process(impl);
105
            }
106
 
107
-           stream_set_state(stream, PW_STREAM_STATE_STREAMING, NULL);
108
+           stream_set_state(stream, PW_STREAM_STATE_STREAMING, 0, NULL);
109
        }
110
        break;
111
    default:
112
@@ -883,7 +886,7 @@
113
    pw_stream_emit_param_changed(stream, id, param);
114
 
115
    if (stream->state == PW_STREAM_STATE_ERROR)
116
-       return -EIO;
117
+       return stream->error_res;
118
 
119
    emit_node_info(impl, false);
120
    emit_port_info(impl, false);
121
@@ -1119,7 +1122,7 @@
122
    pw_log_debug("%p: removed", stream);
123
    spa_hook_remove(&stream->proxy_listener);
124
    stream->node_id = SPA_ID_INVALID;
125
-   stream_set_state(stream, PW_STREAM_STATE_UNCONNECTED, NULL);
126
+   stream_set_state(stream, PW_STREAM_STATE_UNCONNECTED, 0, NULL);
127
 }
128
 
129
 static void proxy_destroy(void *_data)
130
@@ -1145,7 +1148,7 @@
131
    stream->node_id = global_id;
132
    if (props)
133
        pw_properties_update(stream->properties, props);
134
-   stream_set_state(stream, PW_STREAM_STATE_PAUSED, NULL);
135
+   stream_set_state(stream, PW_STREAM_STATE_PAUSED, 0, NULL);
136
 }
137
 
138
 static const struct pw_proxy_events proxy_events = {
139
@@ -1339,15 +1342,13 @@
140
 static void node_event_destroy(void *data)
141
 {
142
    struct pw_stream *stream = data;
143
-   struct stream *impl = SPA_CONTAINER_OF(stream, struct stream, this);
144
    spa_hook_remove(&stream->node_listener);
145
-   impl->node = NULL;
146
+   stream->node = NULL;
147
 }
148
 
149
 static void node_event_info(void *data, const struct pw_node_info *info)
150
 {
151
    struct pw_stream *stream = data;
152
-   struct stream *impl = SPA_CONTAINER_OF(stream, struct stream, this);
153
    uint32_t i;
154
 
155
    if (info->change_mask & PW_NODE_CHANGE_MASK_PARAMS) {
156
@@ -1355,7 +1356,7 @@
157
            switch (info->paramsi.id) {
158
            case SPA_PARAM_PropInfo:
159
            case SPA_PARAM_Props:
160
-               pw_impl_node_for_each_param(impl->node,
161
+               pw_impl_node_for_each_param(stream->node,
162
                        0, info->paramsi.id,
163
                        0, UINT32_MAX,
164
                        NULL,
165
@@ -1383,7 +1384,7 @@
166
            id, seq, res, spa_strerror(res), message);
167
 
168
    if (id == PW_ID_CORE && res == -EPIPE) {
169
-       stream_set_state(stream, PW_STREAM_STATE_UNCONNECTED, message);
170
+       stream_set_state(stream, PW_STREAM_STATE_UNCONNECTED, res, message);
171
    }
172
 }
173
 
174
@@ -1395,7 +1396,7 @@
175
 static void context_drained(void *data, struct pw_impl_node *node)
176
 {
177
    struct stream *impl = data;
178
-   if (impl->node != node)
179
+   if (impl->this.node != node)
180
        return;
181
    if (impl->draining && impl->drained) {
182
        impl->draining = false;
183
@@ -1448,6 +1449,7 @@
184
        res = -errno;
185
        goto error_properties;
186
    }
187
+   impl->main_loop = pw_context_get_main_loop(context);
188
 
189
    this = &impl->this;
190
    pw_log_debug("%p: new \"%s\"", impl, name);
191
@@ -1622,16 +1624,16 @@
192
 
193
    impl->disconnecting = true;
194
 
195
-   if (impl->node)
196
-       pw_impl_node_set_active(impl->node, false);
197
+   if (stream->node)
198
+       pw_impl_node_set_active(stream->node, false);
199
 
200
    if (stream->proxy) {
201
pipewire-0.3.70.tar.gz/src/tools/pw-profiler.c -> pipewire-0.3.71.tar.gz/src/tools/pw-profiler.c Changed
10
 
1
@@ -213,7 +213,7 @@
2
                    d4 > 0 ? d4 : 0,
3
                    d5 > 0 ? d5 : 0,
4
                    d6 > 0 ? d6 : 0,
5
-                   (d5 > 0 && d4 > 0 && d5 > d4) ? d5 - d4 : 0,
6
+                   (d5 > 0 && d4 >= 0 && d5 > d4) ? d5 - d4 : 0,
7
                    (d6 > 0 && d5 > 0 && d6 > d5) ? d6 - d5 : 0,
8
                    point->followeri.status);
9
        }
10
pipewire-0.3.70.tar.gz/test/test-loop.c -> pipewire-0.3.71.tar.gz/test/test-loop.c Changed
20
 
1
@@ -234,6 +234,10 @@
2
    struct spa_hook hook;
3
 };
4
 
5
+static void dmsbd_before(void *data)
6
+{
7
+}
8
+
9
 static void dmsbd_after(void *data)
10
 {
11
    struct dmsbd_data *d = data;
12
@@ -244,6 +248,7 @@
13
 
14
 static const struct spa_loop_control_hooks dmsbd_hooks = {
15
    SPA_VERSION_LOOP_CONTROL_HOOKS,
16
+   .before = dmsbd_before,
17
    .after = dmsbd_after,
18
 };
19
 
20