Changes of Revision 29

pipewire-aptx.changes Changed
x
 
1
@@ -1,4 +1,9 @@
2
 -------------------------------------------------------------------
3
+Thu Jun 29 10:47:58 UTC 2023 - Bjørn Lie <zaitor@opensuse.org>
4
+
5
+- Update to version 0.3.72
6
+
7
+-------------------------------------------------------------------
8
 Sat May 20 12:08:17 UTC 2023 - Bjørn Lie <zaitor@opensuse.org>
9
 
10
 - Update to version 0.3.71
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.71
6
+Version:        0.3.72
7
 Release:        0
8
 Summary:        PipeWire Bluetooth aptX codec plugin
9
 License:        MIT
10
pipewire-0.3.71.tar.gz/.gitlab-ci.yml -> pipewire-0.3.72.tar.gz/.gitlab-ci.yml Changed
27
 
1
@@ -25,7 +25,7 @@
2
 .fedora:
3
   variables:
4
     # Update this tag when you want to trigger a rebuild
5
-    FDO_DISTRIBUTION_TAG: '2023-04-18.0'
6
+    FDO_DISTRIBUTION_TAG: '2023-05-31.0'
7
     FDO_DISTRIBUTION_VERSION: '37'
8
     FDO_DISTRIBUTION_PACKAGES: >-
9
       alsa-lib-devel
10
@@ -46,6 +46,7 @@
11
       jack-audio-connection-kit-devel
12
       libasan
13
       libcanberra-devel
14
+      libffado-devel
15
       libldac-devel
16
       libmysofa-devel
17
       libsndfile-devel
18
@@ -304,6 +305,8 @@
19
         -Droc=disabled
20
         -Dlibcamera=disabled
21
         -Dsession-managers=
22
+        -Dc_args='-UFASTPATH'
23
+        -Dcpp_args='-UFASTPATH'
24
   parallel:
25
     matrix:
26
       - CC: gcc, clang
27
pipewire-0.3.71.tar.gz/NEWS -> pipewire-0.3.72.tar.gz/NEWS Changed
121
 
1
@@ -1,3 +1,109 @@
2
+# PipeWire 0.3.72 (2023-06-26)
3
+
4
+This is a bugfix release that is API and ABI compatible with previous
5
+0.3.x releases.
6
+
7
+## Highlights
8
+  - Fix a critical bug that would refuse to update the samplerate or
9
+    buffersize in JACK clients. (#3226)
10
+  - A new module-netjack2-driver and module-netjack2-manager were added
11
+    that are compatible with NETJACK2. This allows PipeWire to become
12
+    a NETJACK2 manager or a driver between JACK2 or PipeWire servers.
13
+  - Support was added for firewire devices with FFADO. This is untested
14
+    for now and MIDI is not implemented yet.
15
+  - The node scheduling was optimized some more. External drivers are now
16
+    as efficient as in-server ones. This should improve performance of
17
+    various drivers such as bluetooth and JACK based drivers.
18
+  - Many, many bug fixes and a ton of improvements.
19
+
20
+
21
+## PipeWire
22
+  - pw-filter can now be used to write sinks and sources.
23
+  - The node activation for drivers was changed. The driver now does not
24
+    need to go to the server to start the processing cycle. This makes
25
+    out-of-server drivers as efficient as in-server drivers.
26
+  - Don't try to use drivers with 0 priority as fallback drivers. This
27
+    avoids making the screencast driver a driver for audio. (#3219)
28
+  - Improve xrun count reporting in pw-top and the profiler. Now each
29
+    node has their own xrun counter updated when it fails to complete
30
+    processing during the cycle.
31
+  - pw-filter now also has support for TRIGGER.
32
+  - A potential fd leak was found when fds were send to a zombie client.
33
+    (#1840)
34
+  - Fix a bug where monitor or capture streams were logged twice in the
35
+    profiler. (#3278)
36
+  - Remove stream hooks safely. (#3251)
37
+  - A bug in serialization of container properties was fixed. This could
38
+    result in truncated property values. (#3290)
39
+  - The PIPEWIRE_AUTOCONNECT environment variable now always overrides the
40
+    autoconnect settings of streams. (#3299)
41
+  - Node, port and link destroy now avoids some useless work.
42
+  - Port will now try to renegotiate a new format when idle. (#3266)
43
+
44
+## Modules
45
+  - The module-sap now is more compatible with AES67.
46
+  - A new FFADO driver module was added. This is completely untested because
47
+    of lack of hardware. Please test and report issues.
48
+  - A new NETJACK2 driver and a NETJACK2 manager module were added. These
49
+    should be drop in replacements for the JACK2 parts.
50
+  - The RAOP discover module now tries harder to only list devices once.
51
+  - The zeroconf discover module now tries harder to only list devices once.
52
+  - The RAOP sink module now handles latency better and is compatible with
53
+    some more devices. (#3247, #3282)
54
+  - The loopback and filter-chain modules now always dequeue the last input
55
+    buffer to avoid stuttering in some cases. (#3276)
56
+  - The SPA node factory module can now also export nodes. This is used to
57
+    export the PTP clock from the AES67 config file.
58
+  - A bug in module-jack-tunnel was fixed that would cause stuttering and
59
+    corrupted output in some cases. (#3255)
60
+  - The resampler is now disabled in module-loopback and filter-chain when
61
+    the samplerate is set to follow the graph rate. (#2969)
62
+  - The way the mixer peer is sent to clients was improved. It is now also
63
+    possible to let a remote node know about mixer port removes, which
64
+    can avoid memory leaks and some code simplifications.
65
+
66
+## SPA
67
+  - Monitor ports now report latency correctly.
68
+  - The ALSA plugin now uses htimestamp to get a more accurate ringbuffer
69
+    position to estimate the clock skew.
70
+  - The channelmixer now has min/max-volume settings to limit or fix the
71
+    volume.
72
+  - The ALSA plugin can now control the playback and capture rate of USB
73
+    gadgets. This can avoid resampling and instead use the USB feedback
74
+    to control the rate.
75
+  - The ALSA output to multiple devices has been improved, some lockups
76
+    are avoided when the device ringbuffer is full.
77
+  - The compress-offload sink has improved negotiation.
78
+
79
+## pulse-server
80
+  - Only try to use GSettings when the schema exists.
81
+  - @DEFAULT_SOURCE@, @DEFAULT_SINK@ and @DEFAULT_MONITOR@ are now correctly
82
+    handled as targets in playback and capture streams. (#3284)
83
+  - 2 new quirks are added to disable volume updates on sinks/sources.
84
+    (#1517)
85
+  - The virtual-sink and virtual-source modules were added. These are really
86
+    example modules but actually also work and are useful on PulseAudio so
87
+    implement them as well.
88
+  - Fix initial stream volumes. (#3306)
89
+
90
+## Bluetooth
91
+  - Only register A2DP or BAP when we have codecs.
92
+  - Include codec into the media.name
93
+
94
+## JACK
95
+  - Fix a critical bug that would refuse to update the samplerate or
96
+    buffersize. (#3226)
97
+  - Improve updates of samplerate/buffersize, delay the updates until the
98
+    client is activated. (#3297)
99
+  - Use the new mix-info updates to simplify the mixer setup and peer
100
+    detection.
101
+
102
+## GStreamer
103
+  - Fill default strides instead of 0 on pipewire video buffers. (#3236)
104
+
105
+Older versions:
106
+
107
+
108
 # PipeWire 0.3.71 (2023-05-17)
109
 
110
 This is a bugfix release that is API and ABI compatible with previous
111
@@ -140,9 +246,6 @@
112
   - Document the SPA Pod serialization.
113
   - Document the PipeWire native protocol.
114
 
115
-
116
-Older versions:
117
-
118
 # PipeWire 0.3.70 (2023-04-20)
119
 
120
 This is a quick bugfix release that is API and ABI compatible with previous
121
pipewire-0.3.71.tar.gz/doc/pipewire-modules.dox -> pipewire-0.3.72.tar.gz/doc/pipewire-modules.dox Changed
17
 
1
@@ -59,12 +59,15 @@
2
 - \subpage page_module_example_sink
3
 - \subpage page_module_example_source
4
 - \subpage page_module_fallback_sink
5
+- \subpage page_module_ffado_driver
6
 - \subpage page_module_filter_chain
7
 - \subpage page_module_jackdbus_detect
8
 - \subpage page_module_jack_tunnel
9
 - \subpage page_module_link_factory
10
 - \subpage page_module_loopback
11
 - \subpage page_module_metadata
12
+- \subpage page_module_netjack2_driver
13
+- \subpage page_module_netjack2_manager
14
 - \subpage page_module_pipe_tunnel
15
 - \subpage page_module_portal
16
 - \subpage page_module_profiler
17
pipewire-0.3.71.tar.gz/doc/pipewire-protocol.dox -> pipewire-0.3.72.tar.gz/doc/pipewire-protocol.dox Changed
66
 
1
@@ -46,6 +46,10 @@
2
 message than can fit in one message, the message is split into multiple
3
 parts.
4
 
5
+A receiver needs to first read the header to know the size of the
6
+complete message. After collecting the complete message, it can start
7
+processing it.
8
+
9
 The payload is a single POD see \ref page_spa_pod for details.
10
 
11
 After the payload, there is an optional footer POD object.
12
@@ -61,14 +65,28 @@
13
 
14
 
15
 The client opens the socket and allocates a Core proxy with id 0 and a Client
16
-proxy with id 1.
17
+proxy with id 1. The proxy is nothing more than a way to identify the remote
18
+objects with an id. Usually they also contain the type and version of the
19
+remote object and some helpers (interfaces) to dispatch messages.
20
 
21
-The server will allocate a new Core resource with id 0.
22
+The server will allocate a new Core resource with id 0. Resources are the
23
+counterpart of client proxies and also typically contain extra information
24
+and helpers (interfaces) to dispatch messages.
25
 
26
 The client sends the Core::Hello message on the socket to start the
27
 communication.
28
 
29
-The server binds the client resource with id 1 to the client.
30
+The server binds the client resource with id 1 to the client. This means
31
+that the client object on the server will now have a resource with a
32
+client side proxy associated with it.
33
+
34
+When the client sends a message for a certain proxy, the server side
35
+resource with the same id for the client is found and the message is
36
+dispatched to the object associated with the resource.
37
+
38
+Similarly when a server sends a message on a resource, the client will
39
+find the proxy with the same id and dispatch the message to it's
40
+associated object.
41
 
42
 The client then sends client properties to the server.
43
 
44
@@ -1235,7 +1253,8 @@
45
    )
46
 ```
47
 - readfd: the eventfd to start processing
48
-- writefd: the eventfd to signal driver start
49
+- writefd: the eventfd to signal when the driver completes and profiling is
50
+           enabled.
51
 - memfd: the index of the memfd of the activation record
52
 - offset: the offset in memfd of the start of the activation record
53
 - size: the size of the activation record
54
@@ -1243,6 +1262,11 @@
55
 The activation record is currently an internal data structure that is
56
 not yet ABI stable.
57
 
58
+The writefd is meant to wake up the server after the driver completes so
59
+that the profiler can collect the information. The profiler is active
60
+when the pw_node_activation::flags fields has PW_NODE_ACTIVATION_FLAG_PROFILER
61
+set. When the profiler is disabled (or when the node is not driving), this
62
+eventfd should not be signaled.
63
 
64
 ### ClientNode::SetParam (Opcode 1)
65
 
66
pipewire-0.3.71.tar.gz/doc/pipewire-scheduling.dox -> pipewire-0.3.72.tar.gz/doc/pipewire-scheduling.dox Changed
104
 
1
@@ -17,7 +17,7 @@
2
 - an activation record that lives in shared memory with memfd.
3
 
4
 ```
5
-   evenfd  
6
+   eventfd
7
   +-^---------+
8
   |           |
9
  in          out
10
@@ -42,7 +42,7 @@
11
  - Information about repositions (seek) and timebase owners.
12
 
13
 
14
-# links.
15
+# Links
16
 
17
 When two nodes are linked together, the output node becomes a dependency for the input
18
 node. This means the input node can only start processing when the output node is finished.
19
@@ -57,7 +57,7 @@
20
 
21
 
22
 ```
23
-   evenfd                                 eventfd
24
+   eventfd                                eventfd
25
   +-^---------+                          +-^---------+
26
   |           |        link              |           |
27
  in     A    out ---------------------> in     B    out
28
@@ -92,7 +92,7 @@
29
 
30
 
31
 ```
32
-   evenfd                                 eventfd
33
+   eventfd                                eventfd
34
   +-^---------+                          +-^---------+
35
   |           |        link              |           |
36
  in     A    out ---------------------> in     B    out
37
@@ -108,11 +108,11 @@
38
       |    |                       /   /
39
       |    |                      /   /
40
       |    |                     /   /
41
-      |    |                    /   / 
42
+      |    |                    /   /
43
       v    |     /-------------/   /
44
     activation {                  /
45
       status:OK, V---------------/
46
-      pending:0, 
47
+      pending:0,
48
       required:2
49
     }
50
   +-^---------+
51
@@ -133,17 +133,16 @@
52
 The driver will then start processing the graph by emitting the ready signal. PipeWire
53
 will then:
54
 
55
- - Perform some statistics about the previous cycle. Did it complete? compute processing
56
-   times, cpu usage etc.
57
+ - Check the previous cycle. Did it complete? Mark xrun on unfinished nodes.
58
  - Perform reposition requests if any, timebase changes, etc..
59
  - The pending counter of each follower node is set to the required field.
60
- - it then loops over all targets of the driver and atomically decrements the required
61
+ - It then loops over all targets of the driver and atomically decrements the required
62
    field of the activation record. When the required field is 0, the eventfd is signaled
63
    and the node can be scheduled.
64
 
65
-In our example above, Node A and be will have their pending state decremented. Node A
66
+In our example above, Node A and B will have their pending state decremented. Node A
67
 will be 0 and will be triggered first (node B has 2 pending dependencies to start with and
68
-will not be triggered yet). The driver itself also has 2 dependcies left and will not
69
+will not be triggered yet). The driver itself also has 2 dependencies left and will not
70
 be triggered (complete) yet.
71
 
72
 ## Scheduling node A
73
@@ -169,13 +168,15 @@
74
 The graph always completes after the driver is triggered and scheduled. All required
75
 fields from all the nodes in the target list of the driver are now 0.
76
 
77
+The driver calculates some stats about cpu time etc.
78
+
79
 # Remote nodes.
80
 
81
 For remote nodes, the eventfd and the activation is transfered from the server
82
 to the client.
83
 
84
 This means that writing to the remote client eventfd will wake the client directly
85
-without going to the server first. 
86
+without going to the server first.
87
 
88
 All remote clients also get the activation and eventfd of the peer and driver they
89
 are linked to and can directly trigger peers and drivers without going to the
90
@@ -183,9 +184,10 @@
91
 
92
 ## Remote driver nodes.
93
 
94
-Currently the graph start cycle is managed by the server.
95
+Remote drivers start the graph cycle directly without going to the server first.
96
 
97
-Remote driver nodes therefore have an extra eventfd to wake up the server and signal
98
-the graph start.
99
+After they complete (and only when the profiler is active), they will trigger an
100
+extra eventfd to signal the server that the graph completed. This is used by the
101
+server to generate the profiler info.
102
 
103
 */
104
pipewire-0.3.71.tar.gz/doc/spa-pod.dox -> pipewire-0.3.72.tar.gz/doc/spa-pod.dox Changed
37
 
1
@@ -623,7 +623,7 @@
2
     0                   1                   2                   3
3
     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
4
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
5
-   |   4                                                           |
6
+   |   8                                                           |
7
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
8
    |   5                                                           |
9
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
10
@@ -659,7 +659,7 @@
11
     0                   1                   2                   3
12
     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
13
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
14
-   |   4                                                           |
15
+   |   8                                                           |
16
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
17
    |   7                                                           |
18
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
19
@@ -948,7 +948,7 @@
20
     0                   1                   2                   3
21
     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
22
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
23
-   |   size                                                        |
24
+   |   16                                                          |
25
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
26
    |   17                                                          |
27
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
28
@@ -971,7 +971,7 @@
29
     0                   1                   2                   3
30
     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
31
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
32
-   |   size                                                        |
33
+   |   8                                                           |
34
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
35
    |   18                                                          |
36
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
37
pipewire-0.3.71.tar.gz/doc/tutorial1.c -> pipewire-0.3.72.tar.gz/doc/tutorial1.c Changed
14
 
1
@@ -11,9 +11,9 @@
2
    pw_init(&argc, &argv);
3
 
4
    fprintf(stdout, "Compiled with libpipewire %s\n"
5
-                        "Linked with libpipewire %s\n",
6
-                                pw_get_headers_version(),
7
-                                pw_get_library_version());
8
+           "Linked with libpipewire %s\n",
9
+               pw_get_headers_version(),
10
+               pw_get_library_version());
11
    return 0;
12
 }
13
 /* code */
14
pipewire-0.3.71.tar.gz/doc/tutorial2.c -> pipewire-0.3.72.tar.gz/doc/tutorial2.c Changed
63
 
1
@@ -20,37 +20,37 @@
2
 
3
 int main(int argc, char *argv)
4
 {
5
-        struct pw_main_loop *loop;
6
-        struct pw_context *context;
7
-        struct pw_core *core;
8
-        struct pw_registry *registry;
9
-        struct spa_hook registry_listener;
10
+   struct pw_main_loop *loop;
11
+   struct pw_context *context;
12
+   struct pw_core *core;
13
+   struct pw_registry *registry;
14
+   struct spa_hook registry_listener;
15
 
16
-        pw_init(&argc, &argv);
17
+   pw_init(&argc, &argv);
18
 
19
-        loop = pw_main_loop_new(NULL /* properties */);
20
-        context = pw_context_new(pw_main_loop_get_loop(loop),
21
-                        NULL /* properties */,
22
-                        0 /* user_data size */);
23
+   loop = pw_main_loop_new(NULL /* properties */);
24
+   context = pw_context_new(pw_main_loop_get_loop(loop),
25
+           NULL /* properties */,
26
+           0 /* user_data size */);
27
 
28
-        core = pw_context_connect(context,
29
-                        NULL /* properties */,
30
-                        0 /* user_data size */);
31
+   core = pw_context_connect(context,
32
+           NULL /* properties */,
33
+           0 /* user_data size */);
34
 
35
-        registry = pw_core_get_registry(core, PW_VERSION_REGISTRY,
36
-                        0 /* user_data size */);
37
+   registry = pw_core_get_registry(core, PW_VERSION_REGISTRY,
38
+           0 /* user_data size */);
39
 
40
-        spa_zero(registry_listener);
41
-        pw_registry_add_listener(registry, &registry_listener,
42
-                                       &registry_events, NULL);
43
+   spa_zero(registry_listener);
44
+   pw_registry_add_listener(registry, &registry_listener,
45
+                      &registry_events, NULL);
46
 
47
-        pw_main_loop_run(loop);
48
+   pw_main_loop_run(loop);
49
 
50
-        pw_proxy_destroy((struct pw_proxy*)registry);
51
-        pw_core_disconnect(core);
52
-        pw_context_destroy(context);
53
-        pw_main_loop_destroy(loop);
54
+   pw_proxy_destroy((struct pw_proxy*)registry);
55
+   pw_core_disconnect(core);
56
+   pw_context_destroy(context);
57
+   pw_main_loop_destroy(loop);
58
 
59
-        return 0;
60
+   return 0;
61
 }
62
 /* code */
63
pipewire-0.3.71.tar.gz/doc/tutorial4.c -> pipewire-0.3.72.tar.gz/doc/tutorial4.c Changed
26
 
1
@@ -43,15 +43,15 @@
2
    stride = sizeof(int16_t) * DEFAULT_CHANNELS;
3
    n_frames = buf->datas0.maxsize / stride;
4
 
5
-        for (i = 0; i < n_frames; i++) {
6
-                data->accumulator += M_PI_M2 * 440 / DEFAULT_RATE;
7
-                if (data->accumulator >= M_PI_M2)
8
-                        data->accumulator -= M_PI_M2;
9
-
10
-                val = sin(data->accumulator) * DEFAULT_VOLUME * 16767.f;
11
-                for (c = 0; c < DEFAULT_CHANNELS; c++)
12
-                        *dst++ = val;
13
-        }
14
+   for (i = 0; i < n_frames; i++) {
15
+       data->accumulator += M_PI_M2 * 440 / DEFAULT_RATE;
16
+       if (data->accumulator >= M_PI_M2)
17
+           data->accumulator -= M_PI_M2;
18
+
19
+       val = sin(data->accumulator) * DEFAULT_VOLUME * 16767.f;
20
+       for (c = 0; c < DEFAULT_CHANNELS; c++)
21
+           *dst++ = val;
22
+   }
23
 
24
    buf->datas0.chunk->offset = 0;
25
    buf->datas0.chunk->stride = stride;
26
pipewire-0.3.71.tar.gz/meson.build -> pipewire-0.3.72.tar.gz/meson.build Changed
18
 
1
@@ -1,5 +1,5 @@
2
 project('pipewire', 'c' ,
3
-  version : '0.3.71',
4
+  version : '0.3.72',
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
@@ -405,6 +405,9 @@
9
 summary({'lilv (for lv2 plugins)': lilv_lib.found()}, bool_yn: true)
10
 cdata.set('HAVE_LILV', lilv_lib.found())
11
 
12
+libffado_dep = dependency('libffado', required: get_option('libffado'))
13
+summary({'ffado': libffado_dep.found()}, bool_yn: true)
14
+
15
 check_functions = 
16
   'gettid', '#include <unistd.h>', '-D_GNU_SOURCE', ,
17
   'memfd_create', '#include <sys/mman.h>', '-D_GNU_SOURCE', ,
18
pipewire-0.3.71.tar.gz/meson_options.txt -> pipewire-0.3.72.tar.gz/meson_options.txt Changed
9
 
1
@@ -322,3 +322,7 @@
2
        description: 'Enable code that depends on opus',
3
        type: 'feature',
4
        value: 'auto')
5
+option('libffado',
6
+       description: 'Enable code that depends on libffado',
7
+       type: 'feature',
8
+       value: 'auto')
9
pipewire-0.3.71.tar.gz/pipewire-jack/src/pipewire-jack.c -> pipewire-0.3.72.tar.gz/pipewire-jack/src/pipewire-jack.c Changed
201
 
1
@@ -142,7 +142,6 @@
2
            uint32_t dst_serial;
3
            bool src_ours;
4
            bool dst_ours;
5
-           bool is_complete;
6
            struct port *our_input;
7
            struct port *our_output;
8
        } port_link;
9
@@ -518,10 +517,13 @@
10
 
11
 }
12
 
13
-static void init_mix(struct mix *mix, uint32_t mix_id, struct port *port)
14
+static void init_mix(struct mix *mix, uint32_t mix_id, struct port *port, uint32_t peer_id)
15
 {
16
+   pw_log_debug("create %p mix:%d peer:%d", port, mix_id, peer_id);
17
    mix->id = mix_id;
18
+   mix->peer_id = peer_id;
19
    mix->port = port;
20
+   mix->peer_port = NULL;
21
    mix->io = NULL;
22
    mix->n_buffers = 0;
23
    spa_list_init(&mix->queue);
24
@@ -538,6 +540,17 @@
25
    return NULL;
26
 }
27
 
28
+static struct mix *find_port_peer(struct port *port, uint32_t peer_id)
29
+{
30
+   struct mix *mix;
31
+   spa_list_for_each(mix, &port->mix, port_link) {
32
+       pw_log_info("%p %d %d", port, mix->peer_id, peer_id);
33
+       if (mix->peer_id == peer_id)
34
+           return mix;
35
+   }
36
+   return NULL;
37
+}
38
+
39
 static struct mix *find_mix(struct client *c, struct port *port, uint32_t mix_id)
40
 {
41
    struct mix *mix;
42
@@ -549,14 +562,12 @@
43
    return NULL;
44
 }
45
 
46
-static struct mix *ensure_mix(struct client *c, struct port *port, uint32_t mix_id)
47
+static struct mix *create_mix(struct client *c, struct port *port,
48
+       uint32_t mix_id, uint32_t peer_id)
49
 {
50
    struct mix *mix;
51
    uint32_t i;
52
 
53
-   if ((mix = find_mix(c, port, mix_id)) != NULL)
54
-       return mix;
55
-
56
    if (spa_list_is_empty(&c->free_mix)) {
57
        mix = calloc(OBJECT_CHUNK, sizeof(struct mix));
58
        if (mix == NULL)
59
@@ -570,11 +581,19 @@
60
 
61
    spa_list_append(&port->mix, &mix->port_link);
62
 
63
-   init_mix(mix, mix_id, port);
64
+   init_mix(mix, mix_id, port, peer_id);
65
 
66
    return mix;
67
 }
68
 
69
+static struct mix *ensure_mix(struct client *c, struct port *port, uint32_t mix_id)
70
+{
71
+   struct mix *mix;
72
+   if ((mix = find_mix(c, port, mix_id)) != NULL)
73
+       return mix;
74
+   return create_mix(c, port, mix_id, SPA_ID_INVALID);
75
+}
76
+
77
 static int clear_buffers(struct client *c, struct mix *mix)
78
 {
79
    struct port *port = mix->port;
80
@@ -926,7 +945,7 @@
81
        notify = SPA_PTROFF(c->notify_buffer, index & NOTIFY_BUFFER_MASK, struct notify);
82
 
83
        o = notify->object;
84
-       pw_log_debug("%p: dequeue notify index:%08x %p type:%d %p arg1:%d\n", c,
85
+       pw_log_debug("%p: dequeue notify index:%08x %p type:%d %p arg1:%d", c,
86
                index, notify, notify->type, o, notify->arg1);
87
 
88
        switch (notify->type) {
89
@@ -1025,10 +1044,8 @@
90
    int32_t filled;
91
    uint32_t index;
92
    struct notify *notify;
93
-   bool emit = false;;
94
+   bool emit = false;
95
 
96
-   if ((type & NOTIFY_ACTIVE_FLAG) && !c->active)
97
-       return 0;
98
    switch (type) {
99
    case NOTIFY_TYPE_REGISTRATION:
100
        emit = c->registration_callback != NULL && o != NULL;
101
@@ -1060,8 +1077,19 @@
102
    default:
103
        break;
104
    }
105
-   if (!emit) {
106
-       pw_log_debug("%p: skip notify %d", c, type);
107
+   if (!emit || ((type & NOTIFY_ACTIVE_FLAG) && !c->active)) {
108
+       switch (type) {
109
+       case NOTIFY_TYPE_BUFFER_FRAMES:
110
+           if (!emit)
111
+               c->buffer_frames = arg1;
112
+           break;
113
+       case NOTIFY_TYPE_SAMPLE_RATE:
114
+           if (!emit)
115
+               c->sample_rate = arg1;
116
+           break;
117
+       }
118
+       pw_log_debug("%p: skip notify %08x active:%d emit:%d", c, type,
119
+               c->active, emit);
120
        if (o != NULL && arg1 == 0 && o->removing) {
121
            o->removing = false;
122
            free_object(c, o);
123
@@ -1080,7 +1108,7 @@
124
    notify->object = o;
125
    notify->arg1 = arg1;
126
    notify->msg = msg;
127
-   pw_log_debug("%p: queue notify index:%08x %p type:%d %p arg1:%d msg:%s\n", c,
128
+   pw_log_debug("%p: queue notify index:%08x %p type:%d %p arg1:%d msg:%s", c,
129
                index, notify, notify->type, o, notify->arg1, notify->msg);
130
    index += sizeof(struct notify);
131
    spa_ringbuffer_write_update(&c->notify_ring, index);
132
@@ -1597,11 +1625,10 @@
133
    if (SPA_UNLIKELY(sample_rate != c->sample_rate)) {
134
        pw_log_info("%p: sample_rate old:%d new:%d cb:%p", c,
135
                c->sample_rate, sample_rate, c->srate_callback);
136
-       if (c->srate_callback != NULL) {
137
+       if (c->sample_rate != (uint32_t)-1)
138
            queue_notify(c, NOTIFY_TYPE_SAMPLE_RATE, NULL, sample_rate, NULL);
139
-       } else {
140
+       else
141
            c->sample_rate = sample_rate;
142
-       }
143
    }
144
    return c->sample_rate == sample_rate;
145
 }
146
@@ -2679,13 +2706,6 @@
147
    void *ptr;
148
    int res = 0;
149
 
150
-   if (c->node_id == node_id) {
151
-       pw_log_debug("%p: our activation %u: %u %u %u", c, node_id,
152
-               mem_id, offset, size);
153
-       close(signalfd);
154
-       return 0;
155
-   }
156
-
157
    if (mem_id == SPA_ID_INVALID) {
158
        mm = ptr = NULL;
159
        size = 0;
160
@@ -2701,8 +2721,13 @@
161
        ptr = mm->ptr;
162
    }
163
 
164
-   pw_log_debug("%p: set activation %u: %u %u %u %p", c, node_id,
165
-           mem_id, offset, size, ptr);
166
+   if (c->node_id == node_id) {
167
+       pw_log_debug("%p: our activation %u: %u %u %u %p", c, node_id,
168
+               mem_id, offset, size, ptr);
169
+   } else {
170
+       pw_log_debug("%p: set activation %u: %u %u %u %p", c, node_id,
171
+               mem_id, offset, size, ptr);
172
+   }
173
 
174
    if (ptr) {
175
        link = calloc(1, sizeof(struct link));
176
@@ -2751,8 +2776,6 @@
177
    struct client *c = (struct client *) data;
178
    struct port *p = GET_PORT(c, direction, port_id);
179
    struct mix *mix;
180
-   struct object *l;
181
-   uint32_t src, dst;
182
    int res = 0;
183
 
184
    if (p == NULL || !p->valid) {
185
@@ -2760,40 +2783,21 @@
186
        goto exit;
187
    }
188
 
189
-   if ((mix = ensure_mix(c, p, mix_id)) == NULL) {
190
-       res = -ENOMEM;
191
-       goto exit;
192
-   }
193
-   mix->peer_id = peer_id;
194
+   mix = find_mix(c, p, mix_id);
195
 
196
-   if (direction == SPA_DIRECTION_INPUT) {
197
-       src = peer_id;
198
-       dst = p->object->id;
199
+   if (peer_id == SPA_ID_INVALID) {
200
+       if (mix == NULL) {
201
pipewire-0.3.71.tar.gz/po/ca.po -> pipewire-0.3.72.tar.gz/po/ca.po Changed
201
 
1
@@ -5,7 +5,7 @@
2
 # Xavier Conde Rueda <xavi.conde@gmail.com>, 2008.
3
 # Agustí Grau <fletxa@gmail.com>, 2009.
4
 # Judith Pintó Subirada <judithp@gmail.com>
5
-# Jordi Mas i Herǹandez, <jmas@softcatala.org>, 2022
6
+# Jordi Mas i Herǹandez, <jmas@softcatala.org>, 2022-2023
7
 #
8
 # This file is translated according to the glossary and style guide of
9
 # Softcatalà. If you plan to modify this file, please read first the page
10
@@ -27,17 +27,20 @@
11
 msgid ""
12
 msgstr ""
13
 "Project-Id-Version: pipewire\n"
14
-"Report-Msgid-Bugs-To: https://gitlab.freedesktop.org/pipewire/pipewire/issues/new\n"
15
-"POT-Creation-Date: 2021-04-18 16:54+0800\n"
16
-"PO-Revision-Date: 2022-09-01 19:24+0000\n"
17
+"Report-Msgid-Bugs-To: https://gitlab.freedesktop.org/pipewire/pipewire/-/"
18
+"issues\n"
19
+"POT-Creation-Date: 2023-06-06 15:28+0000\n"
20
+"PO-Revision-Date: 2023-06-06 22:39+0200\n"
21
 "Last-Translator: Jordi Mas i Herǹandez, <jmas@softcatala.org>,\n"
22
 "Language-Team: Catalan <fedora@softcatala.net>\n"
23
 "Language: ca\n"
24
 "MIME-Version: 1.0\n"
25
 "Content-Type: text/plain; charset=UTF-8\n"
26
 "Content-Transfer-Encoding: 8bit\n"
27
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
28
+"X-Generator: Poedit 2.4.2\n"
29
 
30
-#: src/daemon/pipewire.c:43
31
+#: src/daemon/pipewire.c:26
32
 #, c-format
33
 msgid ""
34
 "%s options\n"
35
@@ -48,7 +51,8 @@
36
 "%s opcions\n"
37
 "  -h, --help                            Mostra aquesta ajuda\n"
38
 "      --version                         Mostra la versió\n"
39
-"  -c, --config                          Carrega la configuració (predeterminada %s)\n"
40
+"  -c, --config                          Carrega la configuració "
41
+"(predeterminada %s)\n"
42
 
43
 #: src/daemon/pipewire.desktop.in:4
44
 msgid "PipeWire Media System"
45
@@ -58,305 +62,347 @@
46
 msgid "Start the PipeWire Media System"
47
 msgstr "Inicia el sistema multimèdia PipeWire"
48
 
49
-#: src/examples/media-session/alsa-monitor.c:526
50
-#: spa/plugins/alsa/acp/compat.c:187
51
-msgid "Built-in Audio"
52
-msgstr "Àudio intern"
53
+#: src/modules/module-protocol-pulse/modules/module-tunnel-sink.c:141
54
+#: src/modules/module-protocol-pulse/modules/module-tunnel-source.c:141
55
+#, c-format
56
+msgid "Tunnel to %s%s%s"
57
+msgstr "Túnel cap a %s%s%s"
58
 
59
-#: src/examples/media-session/alsa-monitor.c:530
60
-#: spa/plugins/alsa/acp/compat.c:192
61
-msgid "Modem"
62
-msgstr "Mòdem"
63
+#: src/modules/module-fallback-sink.c:31
64
+msgid "Dummy Output"
65
+msgstr "Sortida fictícia"
66
+
67
+#: src/modules/module-pulse-tunnel.c:844
68
+#, c-format
69
+msgid "Tunnel for %s@%s"
70
+msgstr "Túnel per a %s@%s"
71
 
72
-#: src/examples/media-session/alsa-monitor.c:539
73
+#: src/modules/module-zeroconf-discover.c:315
74
 msgid "Unknown device"
75
 msgstr "Dispositiu desconegut"
76
 
77
-#: src/tools/pw-cat.c:991
78
+#: src/modules/module-zeroconf-discover.c:327
79
+#, c-format
80
+msgid "%s on %s@%s"
81
+msgstr "%s en %s@%s"
82
+
83
+#: src/modules/module-zeroconf-discover.c:331
84
+#, c-format
85
+msgid "%s on %s"
86
+msgstr "%s en %s"
87
+
88
+#: src/tools/pw-cat.c:974
89
 #, c-format
90
 msgid ""
91
-"%s options <file>\n"
92
+"%s options <file>|-\n"
93
 "  -h, --help                            Show this help\n"
94
 "      --version                         Show version\n"
95
 "  -v, --verbose                         Enable verbose operations\n"
96
 "\n"
97
 msgstr ""
98
-"%s opcions <fitxer>\n"
99
+"%s opcions <fitxer>|-\n"
100
 "  -h, --help                            Mostra aquesta ajuda\n"
101
 "      --version                         Mostra la versió\n"
102
 "  -v, --verbose                         Habilita les operacions detallades\n"
103
 
104
-#: src/tools/pw-cat.c:998
105
-#, c-format, fuzzy
106
+#: src/tools/pw-cat.c:981
107
+#, c-format
108
 msgid ""
109
 "  -R, --remote                          Remote daemon name\n"
110
 "      --media-type                      Set media type (default %s)\n"
111
 "      --media-category                  Set media category (default %s)\n"
112
 "      --media-role                      Set media role (default %s)\n"
113
-"      --target                          Set node target (default %s)\n"
114
+"      --target                          Set node target serial or name "
115
+"(default %s)\n"
116
 "                                          0 means don't link\n"
117
 "      --latency                         Set node latency (default %s)\n"
118
 "                                          Xunit (unit = s, ms, us, ns)\n"
119
 "                                          or direct samples (256)\n"
120
-"                                          the rate is the one of the source file\n"
121
-"      --list-targets                    List available targets for --target\n"
122
+"                                          the rate is the one of the source "
123
+"file\n"
124
+"  -P  --properties                      Set node properties\n"
125
 "\n"
126
 msgstr ""
127
-"-R, --remote Nom del dimoni remot\n"
128
-"      --media-type Estableix el tipus de mitjà (per defecte %s)\n"
129
-"      --media-category Estableix la categoria dels mitjans (per defecte %s)\n"
130
-"      --media-role Estableix el rol del mitjà (per defecte %s)\n"
131
-"      --target Estableix l'objectiu del node (per defecte %s)\n"
132
+"  -R, --remote                          Nom del dimoni remot\n"
133
+"      --media-type                          Estableix el tipus de mitjà (per "
134
+"defecte %s)\n"
135
+"      --media-category                          Estableix la categoria del "
136
+"mitjà (per defecte %s)\n"
137
+"      --media-role                          Estableix el rol del mitjà (per "
138
+"defecte %s)\n"
139
+"      --target                          Estableix l'objectiu sèrie o el nom "
140
+"del node (per defecte %s)\n"
141
 "                                          0 vol dir que no enllaça\n"
142
-"      --latency Estableix latència del node (per defecte %s)\n"
143
+"      --latency                          Estableix latència del node (per "
144
+"defecte %s)\n"
145
 "                                          Xunit (unitat = s, ms, us, ns)\n"
146
 "                                          o mostres directes (256)\n"
147
 "                                          la taxa és la del fitxer d'origen\n"
148
-"      --list-targets Llista d'objectius disponibles per a --target"
149
+"  -P  --properties                          Estableix les propietats del "
150
+"node\n"
151
+"\n"
152
 
153
-#: src/tools/pw-cat.c:1016
154
-#, c-format, fuzzy
155
+#: src/tools/pw-cat.c:999
156
+#, c-format
157
 msgid ""
158
-"      --rate                            Sample rate (req. for rec) (default %u)\n"
159
-"      --channels                        Number of channels (req. for rec) (default %u)\n"
160
+"      --rate                            Sample rate (req. for rec) (default "
161
+"%u)\n"
162
+"      --channels                        Number of channels (req. for rec) "
163
+"(default %u)\n"
164
 "      --channel-map                     Channel map\n"
165
-"                                            one of: \"stereo\", \"surround-51\",... or\n"
166
-"                                            comma separated list of channel names: eg. \"FL,FR\"\n"
167
-"      --format                          Sample format %s (req. for rec) (default %s)\n"
168
+"                                            one of: \"stereo\", "
169
+"\"surround-51\",... or\n"
170
+"                                            comma separated list of channel "
171
+"names: eg. \"FL,FR\"\n"
172
+"      --format                          Sample format %s (req. for rec) "
173
+"(default %s)\n"
174
 "      --volume                          Stream volume 0-1.0 (default %.3f)\n"
175
-"  -q  --quality                         Resampler quality (0 - 15) (default %d)\n"
176
+"  -q  --quality                         Resampler quality (0 - 15) (default "
177
+"%d)\n"
178
 "\n"
179
 msgstr ""
180
-"--rate Freqüència de mostreig (req. per rec) (predeterminat %u)\n"
181
-"      --channels Nombre de canals (req. per rec) (predeterminat %u)\n"
182
-"      --channel-map Mapa de canals\n"
183
-"                                            un dels següents: \"estèreo\", \"surround-51\",... o\n"
184
-"                                            Llista separada per comes dels noms dels canals: per exemple. \"FL,FR\"\n"
185
-"      --format Format de mostra %s (req. per a rec) (predeterminat %s)\n"
186
-"      --volume Volum de flux 0-1.0 (predeterminat %.3f)\n"
187
-"  -q --qualitat Remostrador de qualitat (0 - 15) (per defecte %d)"
188
-
189
-#: src/tools/pw-cat.c:1033
190
-#, fuzzy
191
+"      --rate                            Freqüència de mostreig (req. per a "
192
+"rec) (predeterminat %u)\n"
193
+"      --channels                            Nombre de canals (req. per a rec) "
194
+"(predeterminat %u)\n"
195
+"      --channel-map                            Mapa de canals\n"
196
+"                                            un dels següents: \"stereo\", "
197
+"\"surround-51\",... o\n"
198
+"                                            llista separada per comes dels "
199
+"noms dels canals: per exemple. «FL,FR»\n"
200
+"      --format                          Format de mostra %s (req. per a rec) "
201
pipewire-0.3.71.tar.gz/po/pl.po -> pipewire-0.3.72.tar.gz/po/pl.po Changed
201
 
1
@@ -8,8 +8,8 @@
2
 "Project-Id-Version: pipewire\n"
3
 "Report-Msgid-Bugs-To: https://gitlab.freedesktop.org/pipewire/pipewire/-/"
4
 "issues\n"
5
-"POT-Creation-Date: 2023-03-04 12:58+0000\n"
6
-"PO-Revision-Date: 2023-03-04 14:10+0100\n"
7
+"POT-Creation-Date: 2023-05-28 10:45+0000\n"
8
+"PO-Revision-Date: 2023-05-28 12:48+0200\n"
9
 "Last-Translator: Piotr Drąg <piotrdrag@gmail.com>\n"
10
 "Language-Team: Polish <community-poland@mozilla.org>\n"
11
 "Language: pl\n"
12
@@ -41,17 +41,17 @@
13
 msgid "Start the PipeWire Media System"
14
 msgstr "Uruchomienie systemu multimediów PipeWire"
15
 
16
-#: src/modules/module-protocol-pulse/modules/module-tunnel-sink.c:159
17
-#: src/modules/module-protocol-pulse/modules/module-tunnel-source.c:159
18
+#: src/modules/module-protocol-pulse/modules/module-tunnel-sink.c:141
19
+#: src/modules/module-protocol-pulse/modules/module-tunnel-source.c:141
20
 #, c-format
21
-msgid "Tunnel to %s/%s"
22
-msgstr "Tunel do %s/%s"
23
+msgid "Tunnel to %s%s%s"
24
+msgstr "Tunel do %s%s%s"
25
 
26
 #: src/modules/module-fallback-sink.c:31
27
 msgid "Dummy Output"
28
 msgstr "Głuche wyjście"
29
 
30
-#: src/modules/module-pulse-tunnel.c:675
31
+#: src/modules/module-pulse-tunnel.c:844
32
 #, c-format
33
 msgid "Tunnel for %s@%s"
34
 msgstr "Tunel dla %s@%s"
35
@@ -175,7 +175,7 @@
36
 "  -o, --encoded                         Tryb zakodowany\n"
37
 "\n"
38
 
39
-#: src/tools/pw-cli.c:2216
40
+#: src/tools/pw-cli.c:2220
41
 #, c-format
42
 msgid ""
43
 "%s options command\n"
44
@@ -200,7 +200,7 @@
45
 msgstr "Dźwięk w zastosowaniach profesjonalnych"
46
 
47
 #: spa/plugins/alsa/acp/acp.c:427 spa/plugins/alsa/acp/alsa-mixer.c:4648
48
-#: spa/plugins/bluez5/bluez5-device.c:1283
49
+#: spa/plugins/bluez5/bluez5-device.c:1586
50
 msgid "Off"
51
 msgstr "Wyłączone"
52
 
53
@@ -227,7 +227,7 @@
54
 
55
 #: spa/plugins/alsa/acp/alsa-mixer.c:2657
56
 #: spa/plugins/alsa/acp/alsa-mixer.c:2741
57
-#: spa/plugins/bluez5/bluez5-device.c:1519
58
+#: spa/plugins/bluez5/bluez5-device.c:1831
59
 msgid "Microphone"
60
 msgstr "Mikrofon"
61
 
62
@@ -293,7 +293,7 @@
63
 msgstr "Brak podbicia basów"
64
 
65
 #: spa/plugins/alsa/acp/alsa-mixer.c:2672
66
-#: spa/plugins/bluez5/bluez5-device.c:1525
67
+#: spa/plugins/bluez5/bluez5-device.c:1837
68
 msgid "Speaker"
69
 msgstr "Głośnik"
70
 
71
@@ -408,7 +408,7 @@
72
 
73
 #: spa/plugins/alsa/acp/alsa-mixer.c:4484
74
 #: spa/plugins/alsa/acp/alsa-mixer.c:4642
75
-#: spa/plugins/bluez5/bluez5-device.c:1507
76
+#: spa/plugins/bluez5/bluez5-device.c:1819
77
 msgid "Headset"
78
 msgstr "Słuchawki z mikrofonem"
79
 
80
@@ -522,12 +522,12 @@
81
 msgid "Mono Chat + 7.1 Surround"
82
 msgstr "Rozmowa mono + przestrzenne 7.1"
83
 
84
-#: spa/plugins/alsa/acp/alsa-mixer.c:4754
85
+#: spa/plugins/alsa/acp/alsa-mixer.c:4748
86
 #, c-format
87
 msgid "%s Output"
88
 msgstr "Wyjście %s"
89
 
90
-#: spa/plugins/alsa/acp/alsa-mixer.c:4761
91
+#: spa/plugins/alsa/acp/alsa-mixer.c:4756
92
 #, c-format
93
 msgid "%s Input"
94
 msgstr "Wejście %s"
95
@@ -632,104 +632,104 @@
96
 msgid "Modem"
97
 msgstr "Modem"
98
 
99
-#: spa/plugins/bluez5/bluez5-device.c:1294
100
+#: spa/plugins/bluez5/bluez5-device.c:1597
101
 msgid "Audio Gateway (A2DP Source & HSP/HFP AG)"
102
 msgstr "Bramka dźwięku (źródło A2DP i AG HSP/HFP)"
103
 
104
-#: spa/plugins/bluez5/bluez5-device.c:1319
105
+#: spa/plugins/bluez5/bluez5-device.c:1622
106
 #, c-format
107
 msgid "High Fidelity Playback (A2DP Sink, codec %s)"
108
 msgstr "Odtwarzanie o wysokiej dokładności (odpływ A2DP, kodek %s)"
109
 
110
-#: spa/plugins/bluez5/bluez5-device.c:1322
111
+#: spa/plugins/bluez5/bluez5-device.c:1625
112
 #, c-format
113
 msgid "High Fidelity Duplex (A2DP Source/Sink, codec %s)"
114
 msgstr "Dupleks o wysokiej dokładności (źródło/odpływ A2DP, kodek %s)"
115
 
116
-#: spa/plugins/bluez5/bluez5-device.c:1330
117
+#: spa/plugins/bluez5/bluez5-device.c:1633
118
 msgid "High Fidelity Playback (A2DP Sink)"
119
 msgstr "Odtwarzanie o wysokiej dokładności (odpływ A2DP)"
120
 
121
-#: spa/plugins/bluez5/bluez5-device.c:1332
122
+#: spa/plugins/bluez5/bluez5-device.c:1635
123
 msgid "High Fidelity Duplex (A2DP Source/Sink)"
124
 msgstr "Dupleks o wysokiej dokładności (źródło/odpływ A2DP)"
125
 
126
-#: spa/plugins/bluez5/bluez5-device.c:1374
127
+#: spa/plugins/bluez5/bluez5-device.c:1677
128
 #, c-format
129
 msgid "High Fidelity Playback (BAP Sink, codec %s)"
130
 msgstr "Odtwarzanie o wysokiej dokładności (odpływ BAP, kodek %s)"
131
 
132
-#: spa/plugins/bluez5/bluez5-device.c:1378
133
+#: spa/plugins/bluez5/bluez5-device.c:1681
134
 #, c-format
135
 msgid "High Fidelity Input (BAP Source, codec %s)"
136
 msgstr "Wejście o wysokiej dokładności (źródło BAP, kodek %s)"
137
 
138
-#: spa/plugins/bluez5/bluez5-device.c:1382
139
+#: spa/plugins/bluez5/bluez5-device.c:1685
140
 #, c-format
141
 msgid "High Fidelity Duplex (BAP Source/Sink, codec %s)"
142
 msgstr "Dupleks o wysokiej dokładności (źródło/odpływ BAP, kodek %s)"
143
 
144
-#: spa/plugins/bluez5/bluez5-device.c:1390=
145
+#: spa/plugins/bluez5/bluez5-device.c:1693
146
 msgid "High Fidelity Playback (BAP Sink)"
147
 msgstr "Odtwarzanie o wysokiej dokładności (odpływ BAP)"
148
 
149
-#: spa/plugins/bluez5/bluez5-device.c:1393
150
+#: spa/plugins/bluez5/bluez5-device.c:1696
151
 msgid "High Fidelity Input (BAP Source)"
152
 msgstr "Wejście o wysokiej dokładności (źródło BAP)"
153
 
154
-#: spa/plugins/bluez5/bluez5-device.c:1396
155
+#: spa/plugins/bluez5/bluez5-device.c:1699
156
 msgid "High Fidelity Duplex (BAP Source/Sink)"
157
 msgstr "Dupleks o wysokiej dokładności (źródło/odpływ BAP)"
158
 
159
-#: spa/plugins/bluez5/bluez5-device.c:1424
160
+#: spa/plugins/bluez5/bluez5-device.c:1735
161
 #, c-format
162
 msgid "Headset Head Unit (HSP/HFP, codec %s)"
163
 msgstr "Jednostka główna słuchawek z mikrofonem (HSP/HFP, kodek %s)"
164
 
165
-#: spa/plugins/bluez5/bluez5-device.c:1429
166
+#: spa/plugins/bluez5/bluez5-device.c:1740
167
 msgid "Headset Head Unit (HSP/HFP)"
168
 msgstr "Jednostka główna słuchawek z mikrofonem (HSP/HFP)"
169
 
170
-#: spa/plugins/bluez5/bluez5-device.c:1508
171
-#: spa/plugins/bluez5/bluez5-device.c:1513
172
-#: spa/plugins/bluez5/bluez5-device.c:1520
173
-#: spa/plugins/bluez5/bluez5-device.c:1526
174
-#: spa/plugins/bluez5/bluez5-device.c:1532
175
-#: spa/plugins/bluez5/bluez5-device.c:1538
176
-#: spa/plugins/bluez5/bluez5-device.c:1544
177
-#: spa/plugins/bluez5/bluez5-device.c:1550
178
-#: spa/plugins/bluez5/bluez5-device.c:1556
179
+#: spa/plugins/bluez5/bluez5-device.c:1820
180
+#: spa/plugins/bluez5/bluez5-device.c:1825
181
+#: spa/plugins/bluez5/bluez5-device.c:1832
182
+#: spa/plugins/bluez5/bluez5-device.c:1838
183
+#: spa/plugins/bluez5/bluez5-device.c:1844
184
+#: spa/plugins/bluez5/bluez5-device.c:1850
185
+#: spa/plugins/bluez5/bluez5-device.c:1856
186
+#: spa/plugins/bluez5/bluez5-device.c:1862
187
+#: spa/plugins/bluez5/bluez5-device.c:1868
188
 msgid "Handsfree"
189
 msgstr "Zestaw głośnomówiący"
190
 
191
-#: spa/plugins/bluez5/bluez5-device.c:1514
192
+#: spa/plugins/bluez5/bluez5-device.c:1826
193
 msgid "Handsfree (HFP)"
194
 msgstr "Zestaw głośnomówiący (HFP)"
195
 
196
-#: spa/plugins/bluez5/bluez5-device.c:1531
197
+#: spa/plugins/bluez5/bluez5-device.c:1843
198
 msgid "Headphone"
199
 msgstr "Słuchawki"
200
 
201
pipewire-0.3.71.tar.gz/po/tr.po -> pipewire-0.3.72.tar.gz/po/tr.po Changed
58
 
1
@@ -1,6 +1,7 @@
2
 # Turkish translation for PipeWire.
3
-# Copyright (C) 2014 PipeWire's COPYRIGHT HOLDER
4
+# Copyright (C) 2014-2023 PipeWire's COPYRIGHT HOLDER
5
 # This file is distributed under the same license as the PipeWire package.
6
+#
7
 # Necdet Yücel <necdetyucel@gmail.com>, 2014.
8
 # Kaan Özdinçer <kaanozdincer@gmail.com>, 2014.
9
 # Muhammet Kara <muhammetk@gmail.com>, 2015, 2016, 2017.
10
@@ -12,15 +13,15 @@
11
 "Report-Msgid-Bugs-To: https://gitlab.freedesktop.org/pipewire/pipewire/"
12
 "issues/new\n"
13
 "POT-Creation-Date: 2022-06-30 12:50+0200\n"
14
-"PO-Revision-Date: 2022-10-23 10:40+0300\n"
15
-"Last-Translator: Oğuz Ersen <oguz@ersen.moe>\n"
16
-"Language-Team: Turkish <tr>\n"
17
+"PO-Revision-Date: 2023-05-26 23:39+0300\n"
18
+"Last-Translator: Sabri Ünal <libreajans@gmail.com>\n"
19
+"Language-Team: Türkçe <takim@gnome.org.tr>\n"
20
 "Language: tr\n"
21
 "MIME-Version: 1.0\n"
22
 "Content-Type: text/plain; charset=UTF-8\n"
23
 "Content-Transfer-Encoding: 8bit\n"
24
 "Plural-Forms: nplurals=1; plural=0;\n"
25
-"X-Generator: Weblate 4.4.2\n"
26
+"X-Generator: Poedit 3.2.2\n"
27
 
28
 #: src/daemon/pipewire.c:46
29
 #, c-format
30
@@ -96,7 +97,7 @@
31
 "  -P  --properties                      Set node properties\n"
32
 "\n"
33
 msgstr ""
34
-"  -R, --remote                          Uzak arka plan programı adı\n"
35
+"  -R, --remote                          Uzak art alan hizmeti adı\n"
36
 "      --media-type                      Ortam türünü ayarla (öntanımlı %s)\n"
37
 "      --media-category                  Ortam kategorisini ayarla (öntanımlı "
38
 "%s)\n"
39
@@ -175,7 +176,7 @@
40
 "%s seçenekler komut\n"
41
 "  -h, --help                            Bu yardımı göster\n"
42
 "      --version                         Sürümü göster\n"
43
-"  -d, --daemon                          Arka plan programı olarak başlat "
44
+"  -d, --daemon                          Art alan hizmeti olarak başlat "
45
 "(Öntanımlı olarak yanlış)\n"
46
 "  -r, --remote                          Uzak arka plan programı adı\n"
47
 "\n"
48
@@ -683,9 +684,3 @@
49
 #: spa/plugins/bluez5/bluez5-device.c:1498
50
 msgid "Bluetooth (HFP)"
51
 msgstr "Bluetooth (HFP)"
52
-
53
-#~ msgid "PipeWire Media System"
54
-#~ msgstr "PipeWire Ortam Sistemi"
55
-
56
-#~ msgid "Start the PipeWire Media System"
57
-#~ msgstr "PipeWire Ortam Sistemini Başlat"
58
pipewire-0.3.71.tar.gz/spa/plugins/alsa/alsa-compress-offload-sink.c -> pipewire-0.3.72.tar.gz/spa/plugins/alsa/alsa-compress-offload-sink.c Changed
79
 
1
@@ -510,6 +510,8 @@
2
    this->device_context = NULL;
3
    this->device_started = false;
4
    this->device_is_paused = false;
5
+
6
+   this->have_format = false;
7
 }
8
 
9
 static int device_start(struct impl *this)
10
@@ -919,11 +921,6 @@
11
    if (this->started)
12
        return 0;
13
 
14
-   if (!this->have_format)
15
-       return -EIO;
16
-   if (this->n_buffers == 0)
17
-       return -EIO;
18
-
19
    this->following = is_following(this);
20
    spa_log_debug(this->log, "%p: starting output; starting as follower: %d",
21
                  this, this->following);
22
@@ -1357,8 +1354,14 @@
23
        break;
24
 
25
    case SPA_NODE_COMMAND_Start:
26
+       if (!this->have_format)
27
+           return -EIO;
28
+       if (this->n_buffers == 0)
29
+           return -EIO;
30
+
31
        if (SPA_UNLIKELY((res = do_start(this)) < 0))
32
            return res;
33
+
34
        break;
35
 
36
    case SPA_NODE_COMMAND_Suspend:
37
@@ -1403,6 +1406,12 @@
38
                  "device opened: %d have configured format: %d device started: %d",
39
                  this, device_opened, this->have_format, device_started);
40
 
41
+   if (!this->started && this->have_format) {
42
+       spa_log_debug(this->log, "%p: closing device to reset configured format", this);
43
+       device_close(this);
44
+       device_opened = false;
45
+   }
46
+
47
    if (!device_opened) {
48
        if ((res = device_open(this)) < 0)
49
            return res;
50
@@ -1487,11 +1496,18 @@
51
        return port_enum_formats(this, seq, start, num, filter, &b);
52
 
53
    case SPA_PARAM_Format:
54
-       if (!this->have_format)
55
+       if (!this->have_format) {
56
+           spa_log_debug(this->log, "%p: attempted to enumerate current "
57
+                         "format, but no current audio info set", this);
58
            return -EIO;
59
+       }
60
+
61
        if (result.index > 0)
62
            return 0;
63
 
64
+       spa_log_debug(this->log, "%p: current audio info is set; "
65
+                     "enumerating currently set format", this);
66
+
67
        param = spa_format_audio_build(&b, id, &this->current_audio_info);
68
        break;
69
 
70
@@ -1557,8 +1573,6 @@
71
        spa_log_debug(this->log, "%p: clearing format and closing device", this);
72
        device_close(this);
73
        clear_buffers(this);
74
-
75
-       this->have_format = false;
76
    } else {
77
        struct spa_audio_info info = { 0 };
78
        uint32_t rate;
79
pipewire-0.3.71.tar.gz/spa/plugins/alsa/alsa-pcm-sink.c -> pipewire-0.3.72.tar.gz/spa/plugins/alsa/alsa-pcm-sink.c Changed
21
 
1
@@ -771,6 +771,7 @@
2
        break;
3
    case SPA_IO_RateMatch:
4
        this->rate_match = data;
5
+       spa_alsa_update_rate_match(this);
6
        break;
7
    default:
8
        return -ENOENT;
9
@@ -819,6 +820,11 @@
10
 
11
        io->status = SPA_STATUS_OK;
12
    }
13
+   else if (!spa_list_is_empty(&this->ready)) {
14
+       spa_alsa_write(this);
15
+
16
+       io->status = SPA_STATUS_OK;
17
+   }
18
    return SPA_STATUS_HAVE_DATA;
19
 }
20
 
21
pipewire-0.3.71.tar.gz/spa/plugins/alsa/alsa-pcm-source.c -> pipewire-0.3.72.tar.gz/spa/plugins/alsa/alsa-pcm-source.c Changed
9
 
1
@@ -709,6 +709,7 @@
2
        break;
3
    case SPA_IO_RateMatch:
4
        this->rate_match = data;
5
+       spa_alsa_update_rate_match(this);
6
        break;
7
    default:
8
        return -ENOENT;
9
pipewire-0.3.71.tar.gz/spa/plugins/alsa/alsa-pcm.c -> pipewire-0.3.72.tar.gz/spa/plugins/alsa/alsa-pcm.c Changed
201
 
1
@@ -477,6 +477,11 @@
2
    .write = log_write,
3
 };
4
 
5
+static void silence_error_handler(const char *file, int line,
6
+       const char *function, int err, const char *fmt, ...)
7
+{
8
+}
9
+
10
 int spa_alsa_init(struct state *state, const struct spa_dict *info)
11
 {
12
    uint32_t i;
13
@@ -546,6 +551,57 @@
14
    return err;
15
 }
16
 
17
+static int probe_pitch_ctl(struct state *state, const char* device_name)
18
+{
19
+   snd_ctl_elem_id_t *id;
20
+   /* TODO: Add configuration params for the control name and units */
21
+   const char *elem_name =
22
+       state->stream == SND_PCM_STREAM_CAPTURE ?
23
+       "Capture Pitch 1000000" :
24
+       "Playback Pitch 1000000";
25
+   int err;
26
+
27
+   snd_lib_error_set_handler(silence_error_handler);
28
+
29
+   err = snd_ctl_open(&state->ctl, device_name, SND_CTL_NONBLOCK);
30
+   if (err < 0) {
31
+       spa_log_info(state->log, "%s could not find ctl device: %s",
32
+               state->props.device, snd_strerror(err));
33
+       state->ctl = NULL;
34
+       goto error;
35
+   }
36
+
37
+   snd_ctl_elem_id_alloca(&id);
38
+   snd_ctl_elem_id_set_name(id, elem_name);
39
+   snd_ctl_elem_id_set_interface(id, SND_CTL_ELEM_IFACE_PCM);
40
+
41
+   snd_ctl_elem_value_malloc(&state->pitch_elem);
42
+   snd_ctl_elem_value_set_id(state->pitch_elem, id);
43
+
44
+   err = snd_ctl_elem_read(state->ctl, state->pitch_elem);
45
+   if (err < 0) {
46
+       spa_log_debug(state->log, "%s: did not find ctl %s: %s",
47
+               state->props.device, elem_name, snd_strerror(err));
48
+
49
+       snd_ctl_elem_value_free(state->pitch_elem);
50
+       state->pitch_elem = NULL;
51
+
52
+       snd_ctl_close(state->ctl);
53
+       state->ctl = NULL;
54
+       goto error;
55
+   }
56
+
57
+   snd_ctl_elem_value_set_integer(state->pitch_elem, 0, 1000000);
58
+   CHECK(snd_ctl_elem_write(state->ctl, state->pitch_elem), "snd_ctl_elem_write");
59
+   state->last_rate = 1.0;
60
+
61
+   spa_log_info(state->log, "%s: found ctl %s", state->props.device, elem_name);
62
+   err = 0;
63
+error:
64
+   snd_lib_error_set_handler(NULL);
65
+   return err;
66
+}
67
+
68
 int spa_alsa_open(struct state *state, const char *params)
69
 {
70
    int err;
71
@@ -588,6 +644,8 @@
72
    state->sample_count = 0;
73
    state->sample_time = 0;
74
 
75
+   probe_pitch_ctl(state, device_name);
76
+
77
    return 0;
78
 
79
 error_exit_close:
80
@@ -622,6 +680,14 @@
81
    state->have_format = false;
82
    state->opened = false;
83
 
84
+   if (state->pitch_elem) {
85
+       snd_ctl_elem_value_free(state->pitch_elem);
86
+       state->pitch_elem = NULL;
87
+
88
+       snd_ctl_close(state->ctl);
89
+       state->ctl = NULL;
90
+   }
91
+
92
    return err;
93
 }
94
 
95
@@ -1665,6 +1731,41 @@
96
    return match ? 0 : 1;
97
 }
98
 
99
+int spa_alsa_update_rate_match(struct state *state)
100
+{
101
+   uint64_t pitch, last_pitch;
102
+   int err;
103
+
104
+   if (!state->pitch_elem)
105
+       return -ENOENT;
106
+
107
+   /* The rate/pitch defines the rate of input to output (if there were a
108
+    * resampler, it's the ratio of input samples to output samples). This
109
+    * means that to adjust the playback rate, we need to apply the inverse
110
+    * of the given rate. */
111
+   if (state->stream == SND_PCM_STREAM_CAPTURE) {
112
+       pitch = 1000000 * state->rate_match->rate;
113
+       last_pitch = 1000000 * state->last_rate;
114
+   } else {
115
+       pitch = 1000000 / state->rate_match->rate;
116
+       last_pitch = 1000000 / state->last_rate;
117
+   }
118
+
119
+   /* The pitch adjustment is limited to 1 ppm */
120
+   if (pitch == last_pitch)
121
+       return 0;
122
+
123
+   snd_ctl_elem_value_set_integer(state->pitch_elem, 0, pitch);
124
+   CHECK(snd_ctl_elem_write(state->ctl, state->pitch_elem), "snd_ctl_elem_write");
125
+
126
+   spa_log_trace_fp(state->log, "%s %u set rate to %g",
127
+           state->props.device, state->stream, state->rate_match->rate);
128
+
129
+   state->last_rate = state->rate_match->rate;
130
+
131
+   return 0;
132
+}
133
+
134
 static int set_swparams(struct state *state)
135
 {
136
    snd_pcm_t *hndl = state->hndl;
137
@@ -1856,7 +1957,8 @@
138
    return do_start(state);
139
 }
140
 
141
-static int get_avail(struct state *state, uint64_t current_time)
142
+#if 0
143
+static int get_avail(struct state *state, uint64_t current_time, snd_pcm_uframes_t *delay)
144
 {
145
    int res, missed;
146
    snd_pcm_sframes_t avail;
147
@@ -1874,19 +1976,21 @@
148
    } else {
149
        state->alsa_recovering = false;
150
    }
151
+   *delay = avail;
152
    return avail;
153
 }
154
 
155
-#if 0
156
-static int get_avail_htimestamp(struct state *state, uint64_t current_time)
157
+#else
158
+static int get_avail(struct state *state, uint64_t current_time, snd_pcm_uframes_t *delay)
159
 {
160
    int res, missed;
161
    snd_pcm_uframes_t avail;
162
    snd_htimestamp_t tstamp;
163
    uint64_t then;
164
 
165
+   avail = snd_pcm_avail(state->hndl);
166
    if ((res = snd_pcm_htimestamp(state->hndl, &avail, &tstamp)) < 0) {
167
-       if ((res = alsa_recover(state, avail)) < 0)
168
+       if ((res = alsa_recover(state, res)) < 0)
169
            return res;
170
        if ((res = snd_pcm_htimestamp(state->hndl, &avail, &tstamp)) < 0) {
171
            if ((missed = ratelimit_test(&state->rate_limit, current_time)) >= 0) {
172
@@ -1898,28 +2002,34 @@
173
    } else {
174
        state->alsa_recovering = false;
175
    }
176
+   *delay = avail;
177
 
178
    if ((then = SPA_TIMESPEC_TO_NSEC(&tstamp)) != 0) {
179
+       int64_t diff;
180
+
181
        if (then < current_time)
182
-           avail += (current_time - then) * state->rate / SPA_NSEC_PER_SEC;
183
+           diff = ((int64_t)(current_time - then)) * state->rate / SPA_NSEC_PER_SEC;
184
        else
185
-           avail -= (then - current_time) * state->rate / SPA_NSEC_PER_SEC;
186
+           diff = -((int64_t)(then - current_time)) * state->rate / SPA_NSEC_PER_SEC;
187
+
188
+       spa_log_trace_fp(state->log, "%"PRIu64" %"PRIu64" %"PRIi64, current_time, then, diff);
189
+
190
+       *delay += diff;
191
    }
192
    return SPA_MIN(avail, state->buffer_frames);
193
 }
194
 #endif
195
 
196
-static int get_status(struct state *state, uint64_t current_time,
197
+static int get_status(struct state *state, uint64_t current_time, snd_pcm_uframes_t *avail,
198
        snd_pcm_uframes_t *delay, snd_pcm_uframes_t *target)
199
 {
200
-   int avail;
201
pipewire-0.3.71.tar.gz/spa/plugins/alsa/alsa-pcm.h -> pipewire-0.3.72.tar.gz/spa/plugins/alsa/alsa-pcm.h Changed
21
 
1
@@ -218,6 +218,11 @@
2
 
3
    struct spa_latency_info latency2;
4
    struct spa_process_latency_info process_latency;
5
+
6
+   /* Rate match via an ALSA ctl */
7
+   snd_ctl_t *ctl;
8
+   snd_ctl_elem_value_t *pitch_elem;
9
+   double last_rate;
10
 };
11
 
12
 struct spa_pod *spa_alsa_enum_propinfo(struct state *state,
13
@@ -230,6 +235,7 @@
14
             const struct spa_pod *filter);
15
 
16
 int spa_alsa_set_format(struct state *state, struct spa_audio_info *info, uint32_t flags);
17
+int spa_alsa_update_rate_match(struct state *state);
18
 
19
 int spa_alsa_init(struct state *state, const struct spa_dict *info);
20
 int spa_alsa_clear(struct state *state);
21
pipewire-0.3.71.tar.gz/spa/plugins/audioconvert/audioadapter.c -> pipewire-0.3.72.tar.gz/spa/plugins/audioconvert/audioadapter.c Changed
39
 
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 warned:1;
6
    unsigned int ready:1;
7
    unsigned int driver:1;
8
    unsigned int async:1;
9
@@ -844,15 +845,18 @@
10
        if ((res = negotiate_buffers(this)) < 0)
11
            return res;
12
        this->ready = true;
13
+       this->warned = false;
14
        break;
15
    case SPA_NODE_COMMAND_Suspend:
16
        this->started = false;
17
        this->ready = false;
18
+       this->warned = 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
+       this->warned = false;
25
        spa_log_debug(this->log, "%p: pausing", this);
26
        break;
27
    case SPA_NODE_COMMAND_Flush:
28
@@ -1470,7 +1474,9 @@
29
    int status = 0, fstatus, retry = 8;
30
 
31
    if (!this->started) {
32
-       spa_log_warn(this->log, "%p: scheduling stopped node", this);
33
+       if (!this->warned)
34
+           spa_log_warn(this->log, "%p: scheduling stopped node", this);
35
+       this->warned = true;
36
        return -EIO;
37
    }
38
 
39
pipewire-0.3.71.tar.gz/spa/plugins/audioconvert/audioconvert.c -> pipewire-0.3.72.tar.gz/spa/plugins/audioconvert/audioconvert.c Changed
201
 
1
@@ -44,8 +44,10 @@
2
 #define MAX_DATAS  SPA_AUDIO_MAX_CHANNELS
3
 #define MAX_PORTS  (SPA_AUDIO_MAX_CHANNELS+1)
4
 
5
-#define DEFAULT_MUTE   false
6
-#define DEFAULT_VOLUME VOLUME_NORM
7
+#define DEFAULT_MUTE       false
8
+#define DEFAULT_VOLUME     VOLUME_NORM
9
+#define DEFAULT_MIN_VOLUME 0.0
10
+#define DEFAULT_MAX_VOLUME 10.0
11
 
12
 struct volumes {
13
    bool mute;
14
@@ -72,6 +74,8 @@
15
 
16
 struct props {
17
    float volume;
18
+   float min_volume;
19
+   float max_volume;
20
    float prev_volume;
21
    uint32_t n_channels;
22
    uint32_t channel_mapSPA_AUDIO_MAX_CHANNELS;
23
@@ -91,6 +95,8 @@
24
 {
25
    uint32_t i;
26
    props->volume = DEFAULT_VOLUME;
27
+   props->min_volume = DEFAULT_MIN_VOLUME;
28
+   props->max_volume = DEFAULT_MAX_VOLUME;
29
    props->n_channels = 0;
30
    for (i = 0; i < SPA_AUDIO_MAX_CHANNELS; i++)
31
        props->channel_mapi = SPA_AUDIO_CHANNEL_UNKNOWN;
32
@@ -446,7 +452,8 @@
33
                SPA_TYPE_OBJECT_PropInfo, id,
34
                SPA_PROP_INFO_id,   SPA_POD_Id(SPA_PROP_volume),
35
                SPA_PROP_INFO_description, SPA_POD_String("Volume"),
36
-               SPA_PROP_INFO_type, SPA_POD_CHOICE_RANGE_Float(p->volume, 0.0, 10.0));
37
+               SPA_PROP_INFO_type, SPA_POD_CHOICE_RANGE_Float(p->volume,
38
+                   DEFAULT_MIN_VOLUME, DEFAULT_MAX_VOLUME));
39
            break;
40
        case 1:
41
            param = spa_pod_builder_add_object(&b,
42
@@ -460,7 +467,8 @@
43
                SPA_TYPE_OBJECT_PropInfo, id,
44
                SPA_PROP_INFO_id,   SPA_POD_Id(SPA_PROP_channelVolumes),
45
                SPA_PROP_INFO_description, SPA_POD_String("Channel Volumes"),
46
-               SPA_PROP_INFO_type, SPA_POD_CHOICE_RANGE_Float(p->volume, 0.0, 10.0),
47
+               SPA_PROP_INFO_type, SPA_POD_CHOICE_RANGE_Float(p->volume,
48
+                   DEFAULT_MIN_VOLUME, DEFAULT_MAX_VOLUME),
49
                SPA_PROP_INFO_container, SPA_POD_Id(SPA_TYPE_Array));
50
            break;
51
        case 3:
52
@@ -483,7 +491,8 @@
53
                SPA_TYPE_OBJECT_PropInfo, id,
54
                SPA_PROP_INFO_id,   SPA_POD_Id(SPA_PROP_monitorVolumes),
55
                SPA_PROP_INFO_description, SPA_POD_String("Monitor Volumes"),
56
-               SPA_PROP_INFO_type, SPA_POD_CHOICE_RANGE_Float(p->volume, 0.0, 10.0),
57
+               SPA_PROP_INFO_type, SPA_POD_CHOICE_RANGE_Float(p->volume,
58
+                   DEFAULT_MIN_VOLUME, DEFAULT_MAX_VOLUME),
59
                SPA_PROP_INFO_container, SPA_POD_Id(SPA_TYPE_Array));
60
            break;
61
        case 6:
62
@@ -498,7 +507,8 @@
63
                SPA_TYPE_OBJECT_PropInfo, id,
64
                SPA_PROP_INFO_id,   SPA_POD_Id(SPA_PROP_softVolumes),
65
                SPA_PROP_INFO_description, SPA_POD_String("Soft Volumes"),
66
-               SPA_PROP_INFO_type, SPA_POD_CHOICE_RANGE_Float(p->volume, 0.0, 10.0),
67
+               SPA_PROP_INFO_type, SPA_POD_CHOICE_RANGE_Float(p->volume,
68
+                   DEFAULT_MIN_VOLUME, DEFAULT_MAX_VOLUME),
69
                SPA_PROP_INFO_container, SPA_POD_Id(SPA_TYPE_Array));
70
            break;
71
        case 8:
72
@@ -521,13 +531,31 @@
73
        case 10:
74
            param = spa_pod_builder_add_object(&b,
75
                SPA_TYPE_OBJECT_PropInfo, id,
76
+               SPA_PROP_INFO_name, SPA_POD_String("channelmix.min-volume"),
77
+               SPA_PROP_INFO_description, SPA_POD_String("Minimum volume level"),
78
+               SPA_PROP_INFO_type, SPA_POD_CHOICE_RANGE_Float(p->min_volume,
79
+                   DEFAULT_MIN_VOLUME, DEFAULT_MAX_VOLUME),
80
+               SPA_PROP_INFO_params, SPA_POD_Bool(true));
81
+           break;
82
+       case 11:
83
+           param = spa_pod_builder_add_object(&b,
84
+               SPA_TYPE_OBJECT_PropInfo, id,
85
+               SPA_PROP_INFO_name, SPA_POD_String("channelmix.max-volume"),
86
+               SPA_PROP_INFO_description, SPA_POD_String("Maximum volume level"),
87
+               SPA_PROP_INFO_type, SPA_POD_CHOICE_RANGE_Float(p->max_volume,
88
+                   DEFAULT_MIN_VOLUME, DEFAULT_MAX_VOLUME),
89
+               SPA_PROP_INFO_params, SPA_POD_Bool(true));
90
+           break;
91
+       case 12:
92
+           param = spa_pod_builder_add_object(&b,
93
+               SPA_TYPE_OBJECT_PropInfo, id,
94
                SPA_PROP_INFO_name, SPA_POD_String("channelmix.normalize"),
95
                SPA_PROP_INFO_description, SPA_POD_String("Normalize Volumes"),
96
                SPA_PROP_INFO_type, SPA_POD_CHOICE_Bool(
97
                    SPA_FLAG_IS_SET(this->mix.options, CHANNELMIX_OPTION_NORMALIZE)),
98
                SPA_PROP_INFO_params, SPA_POD_Bool(true));
99
            break;
100
-       case 11:
101
+       case 13:
102
            param = spa_pod_builder_add_object(&b,
103
                SPA_TYPE_OBJECT_PropInfo, id,
104
                SPA_PROP_INFO_name, SPA_POD_String("channelmix.mix-lfe"),
105
@@ -536,7 +564,7 @@
106
                    SPA_FLAG_IS_SET(this->mix.options, CHANNELMIX_OPTION_MIX_LFE)),
107
                SPA_PROP_INFO_params, SPA_POD_Bool(true));
108
            break;
109
-       case 12:
110
+       case 14:
111
            param = spa_pod_builder_add_object(&b,
112
                SPA_TYPE_OBJECT_PropInfo, id,
113
                SPA_PROP_INFO_name, SPA_POD_String("channelmix.upmix"),
114
@@ -545,7 +573,7 @@
115
                    SPA_FLAG_IS_SET(this->mix.options, CHANNELMIX_OPTION_UPMIX)),
116
                SPA_PROP_INFO_params, SPA_POD_Bool(true));
117
            break;
118
-       case 13:
119
+       case 15:
120
            param = spa_pod_builder_add_object(&b,
121
                SPA_TYPE_OBJECT_PropInfo, id,
122
                SPA_PROP_INFO_name, SPA_POD_String("channelmix.lfe-cutoff"),
123
@@ -554,7 +582,7 @@
124
                    this->mix.lfe_cutoff, 0.0, 1000.0),
125
                SPA_PROP_INFO_params, SPA_POD_Bool(true));
126
            break;
127
-       case 14:
128
+       case 16:
129
            param = spa_pod_builder_add_object(&b,
130
                SPA_TYPE_OBJECT_PropInfo, id,
131
                SPA_PROP_INFO_name, SPA_POD_String("channelmix.fc-cutoff"),
132
@@ -563,7 +591,7 @@
133
                    this->mix.fc_cutoff, 0.0, 48000.0),
134
                SPA_PROP_INFO_params, SPA_POD_Bool(true));
135
            break;
136
-       case 15:
137
+       case 17:
138
            param = spa_pod_builder_add_object(&b,
139
                SPA_TYPE_OBJECT_PropInfo, id,
140
                SPA_PROP_INFO_name, SPA_POD_String("channelmix.rear-delay"),
141
@@ -572,7 +600,7 @@
142
                    this->mix.rear_delay, 0.0, 1000.0),
143
                SPA_PROP_INFO_params, SPA_POD_Bool(true));
144
            break;
145
-       case 16:
146
+       case 18:
147
            param = spa_pod_builder_add_object(&b,
148
                SPA_TYPE_OBJECT_PropInfo, id,
149
                SPA_PROP_INFO_name, SPA_POD_String("channelmix.stereo-widen"),
150
@@ -581,7 +609,7 @@
151
                    this->mix.widen, 0.0, 1.0),
152
                SPA_PROP_INFO_params, SPA_POD_Bool(true));
153
            break;
154
-       case 17:
155
+       case 19:
156
            param = spa_pod_builder_add_object(&b,
157
                SPA_TYPE_OBJECT_PropInfo, id,
158
                SPA_PROP_INFO_name, SPA_POD_String("channelmix.hilbert-taps"),
159
@@ -590,7 +618,7 @@
160
                    this->mix.hilbert_taps, 0, MAX_TAPS),
161
                SPA_PROP_INFO_params, SPA_POD_Bool(true));
162
            break;
163
-       case 18:
164
+       case 20:
165
            spa_pod_builder_push_object(&b, &f0, SPA_TYPE_OBJECT_PropInfo, id);
166
            spa_pod_builder_add(&b,
167
                SPA_PROP_INFO_name, SPA_POD_String("channelmix.upmix-method"),
168
@@ -609,14 +637,14 @@
169
            spa_pod_builder_pop(&b, &f1);
170
            param = spa_pod_builder_pop(&b, &f0);
171
            break;
172
-       case 19:
173
+       case 21:
174
            param = spa_pod_builder_add_object(&b,
175
                SPA_TYPE_OBJECT_PropInfo, id,
176
                SPA_PROP_INFO_id, SPA_POD_Id(SPA_PROP_rate),
177
                SPA_PROP_INFO_description, SPA_POD_String("Rate scaler"),
178
                SPA_PROP_INFO_type, SPA_POD_CHOICE_RANGE_Double(p->rate, 0.0, 10.0));
179
            break;
180
-       case 20:
181
+       case 22:
182
            param = spa_pod_builder_add_object(&b,
183
                SPA_TYPE_OBJECT_PropInfo, id,
184
                SPA_PROP_INFO_id, SPA_POD_Id(SPA_PROP_quality),
185
@@ -625,7 +653,7 @@
186
                SPA_PROP_INFO_type, SPA_POD_CHOICE_RANGE_Int(p->resample_quality, 0, 14),
187
                SPA_PROP_INFO_params, SPA_POD_Bool(true));
188
            break;
189
-       case 21:
190
+       case 23:
191
            param = spa_pod_builder_add_object(&b,
192
                SPA_TYPE_OBJECT_PropInfo, id,
193
                SPA_PROP_INFO_name, SPA_POD_String("resample.disable"),
194
@@ -633,7 +661,7 @@
195
                SPA_PROP_INFO_type, SPA_POD_CHOICE_Bool(p->resample_disabled),
196
                SPA_PROP_INFO_params, SPA_POD_Bool(true));
197
            break;
198
-       case 22:
199
+       case 24:
200
            param = spa_pod_builder_add_object(&b,
201
pipewire-0.3.71.tar.gz/spa/plugins/audioconvert/resample-native.c -> pipewire-0.3.72.tar.gz/spa/plugins/audioconvert/resample-native.c Changed
23
 
1
@@ -169,8 +169,8 @@
2
        r->func_name = data->info->inter_name;
3
    }
4
 
5
-   spa_log_trace_fp(r->log, "native %p: rate:%f in:%d out:%d phase:%d inc:%d frac:%d", r,
6
-           rate, data->in_rate, data->out_rate, data->phase, data->inc, data->frac);
7
+   spa_log_trace_fp(r->log, "native %p: rate:%f in:%d out:%d gcd:%d phase:%d inc:%d frac:%d", r,
8
+           rate, r->i_rate, r->o_rate, gcd, data->phase, data->inc, data->frac);
9
 
10
 }
11
 
12
@@ -367,8 +367,8 @@
13
        return -ENOTSUP;
14
    }
15
 
16
-   spa_log_debug(r->log, "native %p: q:%d in:%d out:%d n_taps:%d n_phases:%d features:%08x:%08x",
17
-           r, r->quality, in_rate, out_rate, n_taps, n_phases,
18
+   spa_log_debug(r->log, "native %p: q:%d in:%d out:%d gcd:%d n_taps:%d n_phases:%d features:%08x:%08x",
19
+           r, r->quality, r->i_rate, r->o_rate, gcd, n_taps, n_phases,
20
            r->cpu_flags, d->info->cpu_flags);
21
 
22
    r->cpu_flags = d->info->cpu_flags;
23
pipewire-0.3.71.tar.gz/spa/plugins/bluez5/bluez-hardware.conf -> pipewire-0.3.72.tar.gz/spa/plugins/bluez5/bluez-hardware.conf Changed
9
 
1
@@ -30,6 +30,7 @@
2
     { name = "Air 1 Plus", no-features =  hw-volume-mic  },
3
     { name = "AirPods", no-features =  msbc-alt1, msbc-alt1-rtl  },
4
     { name = "AirPods Pro", no-features =  msbc-alt1, msbc-alt1-rtl  },
5
+    { name = "Audio Pro_A26", address = "~^7c:96:d2:", no-features =  hw-volume },  # doesn't remember volume, #pipewire-3225
6
     { name = "AXLOIE Goin", no-features =  msbc-alt1, msbc-alt1-rtl  },
7
     { name = "BAA 100", no-features =  hw-volume  },  # Buxton BAA 100, doesn't remember volume, #pipewire-1449
8
     { name = "D50s", address = "~^00:13:ef:", no-features =  hw-volume  },  # volume has no effect, #pipewire-1562
9
pipewire-0.3.71.tar.gz/spa/plugins/bluez5/bluez5-dbus.c -> pipewire-0.3.72.tar.gz/spa/plugins/bluez5/bluez5-dbus.c Changed
159
 
1
@@ -203,6 +203,7 @@
2
 static int spa_bt_transport_start_volume_timer(struct spa_bt_transport *transport);
3
 static int spa_bt_transport_stop_release_timer(struct spa_bt_transport *transport);
4
 static int spa_bt_transport_start_release_timer(struct spa_bt_transport *transport);
5
+static void spa_bt_transport_commit_release_timer(struct spa_bt_transport *transport);
6
 
7
 static int device_start_timer(struct spa_bt_device *device);
8
 static int device_stop_timer(struct spa_bt_device *device);
9
@@ -2589,6 +2590,13 @@
10
        if (state >= SPA_BT_TRANSPORT_STATE_PENDING && old < SPA_BT_TRANSPORT_STATE_PENDING)
11
            transport_sync_volume(transport);
12
 
13
+       if (state < SPA_BT_TRANSPORT_STATE_ACTIVE) {
14
+           /* If transport becomes inactive, do any pending releases
15
+            * immediately, since the fd is not usable any more.
16
+            */
17
+           spa_bt_transport_commit_release_timer(transport);
18
+       }
19
+
20
        if (state == SPA_BT_TRANSPORT_STATE_ERROR) {
21
            uint64_t now = get_time_now(monitor);
22
 
23
@@ -2719,6 +2727,27 @@
24
    return res;
25
 }
26
 
27
+static void spa_bt_transport_do_release(struct spa_bt_transport *transport)
28
+{
29
+   struct spa_bt_monitor *monitor = transport->monitor;
30
+
31
+   spa_assert(transport->acquire_refcount >= 1);
32
+   spa_assert(transport->acquired);
33
+
34
+   if (transport->acquire_refcount == 1) {
35
+       if (!transport->keepalive) {
36
+           spa_bt_transport_impl(transport, release, 0);
37
+           transport->acquired = false;
38
+       } else {
39
+           spa_log_debug(monitor->log, "transport %p: keepalive %s on release",
40
+                   transport, transport->path);
41
+       }
42
+   } else {
43
+       spa_log_debug(monitor->log, "transport %p: delayed decref %s", transport, transport->path);
44
+   }
45
+   transport->acquire_refcount -= 1;
46
+}
47
+
48
 int spa_bt_transport_release(struct spa_bt_transport *transport)
49
 {
50
    struct spa_bt_monitor *monitor = transport->monitor;
51
@@ -2736,8 +2765,15 @@
52
    spa_assert(transport->acquire_refcount == 1);
53
    spa_assert(transport->acquired);
54
 
55
-   /* Postpone transport releases, since we might need it again soon */
56
-   return spa_bt_transport_start_release_timer(transport);
57
+   /* Postpone active transport releases, since we might need it again soon.
58
+    * If not active, release now since it has to be reacquired before using again.
59
+    */
60
+   if (transport->state == SPA_BT_TRANSPORT_STATE_ACTIVE) {
61
+       return spa_bt_transport_start_release_timer(transport);
62
+   } else {
63
+       spa_bt_transport_do_release(transport);
64
+       return 0;
65
+   }
66
 }
67
 
68
 static int spa_bt_transport_release_now(struct spa_bt_transport *transport)
69
@@ -2808,25 +2844,9 @@
70
 static void spa_bt_transport_release_timer_event(struct spa_source *source)
71
 {
72
    struct spa_bt_transport *transport = source->data;
73
-   struct spa_bt_monitor *monitor = transport->monitor;
74
-
75
-   spa_assert(transport->acquire_refcount >= 1);
76
-   spa_assert(transport->acquired);
77
 
78
    spa_bt_transport_stop_release_timer(transport);
79
-
80
-   if (transport->acquire_refcount == 1) {
81
-       if (!transport->keepalive) {
82
-           spa_bt_transport_impl(transport, release, 0);
83
-           transport->acquired = false;
84
-       } else {
85
-           spa_log_debug(monitor->log, "transport %p: keepalive %s on release",
86
-                   transport, transport->path);
87
-       }
88
-   } else {
89
-       spa_log_debug(monitor->log, "transport %p: delayed decref %s", transport, transport->path);
90
-   }
91
-   transport->acquire_refcount -= 1;
92
+   spa_bt_transport_do_release(transport);
93
 }
94
 
95
 static int spa_bt_transport_start_release_timer(struct spa_bt_transport *transport)
96
@@ -2842,6 +2862,17 @@
97
    return stop_timeout_timer(transport->monitor, &transport->release_timer);
98
 }
99
 
100
+static void spa_bt_transport_commit_release_timer(struct spa_bt_transport *transport)
101
+{
102
+   struct spa_bt_monitor *monitor = transport->monitor;
103
+
104
+   /* Do release now if it is pending */
105
+   if (transport->release_timer.data) {
106
+       spa_log_debug(monitor->log, "transport %p: commit pending release", transport);
107
+       spa_bt_transport_release_timer_event(&transport->release_timer);
108
+   }
109
+}
110
+
111
 static void spa_bt_transport_volume_changed(struct spa_bt_transport *transport)
112
 {
113
    struct spa_bt_monitor *monitor = transport->monitor;
114
@@ -4906,10 +4937,28 @@
115
    dbus_connection_unregister_object_path(monitor->conn, A2DP_OBJECT_MANAGER_PATH);
116
 }
117
 
118
+static bool have_codec_endpoints(struct spa_bt_monitor *monitor, bool bap)
119
+{
120
+   const struct media_codec * const * const media_codecs = monitor->media_codecs;
121
+   int i;
122
+
123
+   for (i = 0; media_codecsi; i++) {
124
+       const struct media_codec *codec = media_codecsi;
125
+
126
+       if (codec->bap != bap)
127
+           continue;
128
+       if (endpoint_should_be_registered(monitor, codec, SPA_BT_MEDIA_SINK) ||
129
+               endpoint_should_be_registered(monitor, codec, SPA_BT_MEDIA_SOURCE))
130
+           return true;
131
+   }
132
+   return false;
133
+}
134
+
135
 static int adapter_register_application(struct spa_bt_adapter *a, bool bap)
136
 {
137
    const char *object_manager_path = bap ? BAP_OBJECT_MANAGER_PATH : A2DP_OBJECT_MANAGER_PATH;
138
    struct spa_bt_monitor *monitor = a->monitor;
139
+   const char *ep_type_name = (bap ? "LE Audio" : "A2DP");
140
    DBusMessage *m;
141
    DBusMessageIter i, d;
142
    DBusPendingCall *call;
143
@@ -4925,8 +4974,14 @@
144
        return -ENOTSUP;
145
    }
146
 
147
+   if (!have_codec_endpoints(monitor, bap)) {
148
+       spa_log_warn(monitor->log, "No available %s codecs to register on adapter %s",
149
+               ep_type_name, a->path);
150
+       return -ENOENT;
151
+   }
152
+
153
    spa_log_debug(monitor->log, "Registering bluez5 %s media application on adapter %s",
154
-           (bap ? "LE Audio" : "A2DP"), a->path);
155
+           ep_type_name, a->path);
156
 
157
    m = dbus_message_new_method_call(BLUEZ_SERVICE,
158
                                     a->path,
159
pipewire-0.3.71.tar.gz/spa/plugins/bluez5/media-source.c -> pipewire-0.3.72.tar.gz/spa/plugins/bluez5/media-source.c Changed
27
 
1
@@ -845,14 +845,23 @@
2
 {
3
    uint64_t old = full ? this->info.change_mask : 0;
4
    char latency64;
5
+   char media_name256;
6
+
7
+   spa_scnprintf(
8
+       media_name,
9
+       sizeof(media_name),
10
+       "%s (codec %s)",
11
+       ((this->transport && this->transport->device->name) ?
12
+           this->transport->device->name : this->codec->bap ? "BAP" : "A2DP"),
13
+       this->codec->description
14
+   );
15
 
16
    struct spa_dict_item node_info_items = {
17
        { SPA_KEY_DEVICE_API, "bluez5" },
18
        { SPA_KEY_MEDIA_CLASS, this->is_internal ? "Audio/Source/Internal" :
19
          this->is_input ? "Audio/Source" : "Stream/Output/Audio" },
20
        { SPA_KEY_NODE_LATENCY, this->is_input ? "" : latency },
21
-       { "media.name", ((this->transport && this->transport->device->name) ?
22
-                   this->transport->device->name : this->codec->bap ? "BAP" : "A2DP") },
23
+       { "media.name", media_name },
24
        { SPA_KEY_NODE_DRIVER, this->is_input ? "true" : "false" },
25
    };
26
 
27
pipewire-0.3.71.tar.gz/spa/plugins/control/mixer.c -> pipewire-0.3.72.tar.gz/spa/plugins/control/mixer.c Changed
20
 
1
@@ -656,10 +656,16 @@
2
        d = inport->buffersinio->buffer_id.buffer->datas;
3
 
4
        if ((pod = spa_pod_from_data(d->data, d->maxsize,
5
-               d->chunk->offset, d->chunk->size)) == NULL)
6
+               d->chunk->offset, d->chunk->size)) == NULL) {
7
+           spa_log_trace_fp(this->log, NAME " %p: skip input idx:%d max:%u "
8
+                   "offset:%u size:%u", this, i,
9
+                   d->maxsize, d->chunk->offset, d->chunk->size);
10
            continue;
11
-       if (!spa_pod_is_sequence(pod))
12
+       }
13
+       if (!spa_pod_is_sequence(pod)) {
14
+           spa_log_trace_fp(this->log, NAME " %p: skip input idx:%d", this, i);
15
            continue;
16
+       }
17
 
18
        seqn_seq = pod;
19
        ctrln_seq = spa_pod_control_first(&seqn_seq->body);
20
pipewire-0.3.71.tar.gz/spa/plugins/support/node-driver.c -> pipewire-0.3.72.tar.gz/spa/plugins/support/node-driver.c Changed
23
 
1
@@ -539,10 +539,10 @@
2
        const char *s = info->itemsi.value;
3
        if (spa_streq(k, "node.freewheel")) {
4
            this->props.freewheel = spa_atob(s);
5
-       } else if (spa_streq(k, "clock.name")) {
6
+       } else if (spa_streq(k, "clock.name") && this->clock_fd < 0) {
7
            spa_scnprintf(this->props.clock_name,
8
                sizeof(this->props.clock_name), "%s", s);
9
-       } else if (spa_streq(k, "clock.id")) {
10
+       } else if (spa_streq(k, "clock.id") && this->clock_fd < 0) {
11
            this->props.clock_id = clock_name_to_id(s);
12
            if (this->props.clock_id == -1) {
13
                spa_log_warn(this->log, "unknown clock id '%s'", s);
14
@@ -551,7 +551,7 @@
15
        } else if (spa_streq(k, "clock.device")) {
16
            this->clock_fd = open(s, O_RDWR);
17
            if (this->clock_fd == -1) {
18
-               spa_log_warn(this->log, "failed to open clock device '%s'", s);
19
+               spa_log_info(this->log, "failed to open clock device '%s'", s);
20
            } else {
21
                this->props.clock_id = FD_TO_CLOCKID(this->clock_fd);
22
            }
23
pipewire-0.3.71.tar.gz/src/daemon/pipewire-aes67.conf.in -> pipewire-0.3.72.tar.gz/src/daemon/pipewire-aes67.conf.in Changed
107
 
1
@@ -18,10 +18,28 @@
2
     #default.clock.quantum-limit = 8192
3
 }
4
 
5
-#context.spa-libs = {
6
-#    audio.convert.* = audioconvert/libspa-audioconvert
7
-#    support.*       = support/libspa-support
8
-#}
9
+context.spa-libs = {
10
+    support.*       = support/libspa-support
11
+}
12
+
13
+context.objects = 
14
+    # An example clock reading from /dev/ptp0. Another option is to sync the
15
+    # ptp clock to CLOCK_TAI and then set clock.id = tai.
16
+    # If both device and ID are given and available, device takes precedence
17
+    { factory = spa-node-factory
18
+        args = {
19
+            factory.name    = support.node.driver
20
+            node.name       = PTP0-Driver
21
+            node.group      = pipewire.ptp0
22
+            # This driver should only be used for network nodes marked with group
23
+            priority.driver = 0
24
+            clock.name      = "clock.system.ptp0"
25
+            clock.device    = "/dev/ptp0"
26
+            clock.id        = tai
27
+            object.export   = true
28
+        }
29
+    }
30
+
31
 
32
 context.modules = 
33
     { name = libpipewire-module-rt
34
@@ -35,12 +53,15 @@
35
     }
36
     { name = libpipewire-module-protocol-native }
37
     { name = libpipewire-module-client-node }
38
+    { name = libpipewire-module-spa-node-factory }
39
     { name = libpipewire-module-adapter }
40
     { name = libpipewire-module-rtp-sap
41
         args = {
42
             local.ifname = eth0
43
             sap.ip = 239.255.255.255
44
             sap.port = 9875
45
+            net.ttl = 32
46
+            net.loop = true
47
 
48
             stream.rules = 
49
                 {
50
@@ -55,10 +76,54 @@
51
                             media.class = "Audio/Source"
52
                             device.api = aes67
53
                             sess.latency.msec = 10
54
+                            node.group = pipewire.ptp0
55
+                        }
56
+                    }
57
+                },
58
+                {
59
+                    matches = 
60
+                        {
61
+                            sess.sap.announce = true
62
                         }
63
+                    
64
+                    actions = {
65
+                        announce-stream = {}
66
                     }
67
-                }
68
+               }
69
             
70
         }
71
-    }
72
+    },
73
+    { name = libpipewire-module-rtp-sink
74
+        args = {
75
+            local.ifname = eth0
76
+            destination.ip = 239.69.150.243
77
+            destination.port = 5004
78
+            net.mtu = 1280
79
+            net.ttl = 32
80
+            net.loop = true
81
+            sess.min-ptime = 1
82
+            sess.max-ptime = 1
83
+            sess.name = "PipeWire RTP stream"
84
+            sess.media = "audio"
85
+            sess.ts-refclk = "ptp=traceable"
86
+            sess.ts-offset = 0
87
+            sess.ptime = 1
88
+            sess.latency.msec = 1
89
+            sess.announce = true
90
+            audio.format = "S24BE"
91
+            audio.rate = 48000
92
+            audio.channels = 2
93
+            audio.position =  FL FR 
94
+
95
+            stream.props = {
96
+                node.name = "rtp-sink"
97
+                media.class = "Audio/Sink"
98
+                node.virtual = false
99
+                device.api = aes67
100
+                sess.sap.announce = true
101
+                node.always-process = true
102
+                node.group = pipewire.ptp0
103
+            }
104
+        }
105
+    },
106
 
107
pipewire-0.3.71.tar.gz/src/daemon/pipewire-pulse.conf.in -> pipewire-0.3.72.tar.gz/src/daemon/pipewire-pulse.conf.in Changed
19
 
1
@@ -129,6 +129,8 @@
2
             # Possible quirks:"
3
             #    force-s16-info                 forces sink and source info as S16 format
4
             #    remove-capture-dont-move       removes the capture DONT_MOVE flag
5
+            #    block-source-volume            blocks updates to source volume
6
+            #    block-sink-volume              blocks updates to sink volume
7
             #quirks =  
8
         }
9
     }
10
@@ -158,4 +160,8 @@
11
             }
12
         }
13
     }
14
+    #{
15
+    #    matches =  { application.process.binary = "Discord" } 
16
+    #    actions = { quirks =  block-source-volume  }
17
+    #}
18
 
19
pipewire-0.3.71.tar.gz/src/daemon/pipewire.conf.in -> pipewire-0.3.72.tar.gz/src/daemon/pipewire.conf.in Changed
21
 
1
@@ -221,19 +221,6 @@
2
             node.freewheel  = true
3
         }
4
     }
5
-    # An example clock reading from /dev/ptp0. Another option is to sync the
6
-    # ptp clock to CLOCK_TAI and then set clock.id = tai.
7
-    #{ factory = spa-node-factory
8
-    #    args = {
9
-    #        factory.name    = support.node.driver
10
-    #        node.name       = PTP0-Driver
11
-    #        node.group      = pipewire.ptp0
12
-    #        priority.driver = 30000
13
-    #        clock.name      = "clock.system.ptp0"
14
-    #        #clock.id       = tai
15
-    #        clock.device    = "/dev/ptp0"
16
-    #    }
17
-    #}
18
 
19
     # This creates a new Source node. It will have input ports
20
     # that you can link, to provide audio for this source.
21
pipewire-0.3.72.tar.gz/src/examples/internal.c Added
128
 
1
@@ -0,0 +1,126 @@
2
+/* PipeWire */
3
+/* SPDX-FileCopyrightText: Copyright © 2023 Wim Taymans */
4
+/* SPDX-License-Identifier: MIT */
5
+
6
+/*
7
+ title
8
+ In process pipewire graph
9
+ title
10
+ */
11
+
12
+#include <stdio.h>
13
+#include <errno.h>
14
+#include <signal.h>
15
+#include <math.h>
16
+
17
+#include <spa/utils/names.h>
18
+
19
+#include <pipewire/pipewire.h>
20
+#include <pipewire/impl.h>
21
+
22
+struct data {
23
+   struct pw_main_loop *loop;
24
+
25
+   struct pw_context *context;
26
+   struct pw_core *core;
27
+
28
+   struct pw_proxy *source;
29
+   struct pw_proxy *sink;
30
+   struct pw_proxy *link;
31
+
32
+   int res;
33
+};
34
+
35
+static void do_quit(void *userdata, int signal_number)
36
+{
37
+   struct data *data = userdata;
38
+   pw_main_loop_quit(data->loop);
39
+}
40
+
41
+int main(int argc, char *argv)
42
+{
43
+   struct data data = { 0, };
44
+   struct pw_properties *props;
45
+   const char *dev = "hw:0";
46
+
47
+   pw_init(&argc, &argv);
48
+
49
+   data.loop = pw_main_loop_new(NULL);
50
+
51
+   if (argc > 1)
52
+       dev = argv1;
53
+
54
+   pw_loop_add_signal(pw_main_loop_get_loop(data.loop), SIGINT, do_quit, &data);
55
+   pw_loop_add_signal(pw_main_loop_get_loop(data.loop), SIGTERM, do_quit, &data);
56
+
57
+   data.context = pw_context_new(pw_main_loop_get_loop(data.loop),
58
+           pw_properties_new(
59
+               PW_KEY_CONFIG_NAME, "client-rt.conf",
60
+               NULL), 0);
61
+
62
+   pw_context_load_module(data.context, "libpipewire-module-spa-node-factory", NULL, NULL);
63
+   pw_context_load_module(data.context, "libpipewire-module-link-factory", NULL, NULL);
64
+
65
+   data.core = pw_context_connect_self(data.context, NULL, 0);
66
+   if (data.core == NULL) {
67
+       fprintf(stderr, "can't connect: %m\n");
68
+       data.res = -errno;
69
+       goto cleanup;
70
+   }
71
+
72
+   props = pw_properties_new(
73
+                        SPA_KEY_LIBRARY_NAME, "audiotestsrc/libspa-audiotestsrc",
74
+                        SPA_KEY_FACTORY_NAME, "audiotestsrc",
75
+                        PW_KEY_NODE_NAME, "test_source",
76
+           "Spa:Pod:Object:Param:Props:live", "false",
77
+                        NULL);
78
+   data.source = pw_core_create_object(data.core,
79
+           "spa-node-factory",
80
+           PW_TYPE_INTERFACE_Node,
81
+           PW_VERSION_NODE,
82
+           &props->dict, 0);
83
+   pw_properties_free(props);
84
+
85
+   props = pw_properties_new(
86
+                        SPA_KEY_LIBRARY_NAME, "alsa/libspa-alsa",
87
+                        SPA_KEY_FACTORY_NAME, SPA_NAME_API_ALSA_PCM_SINK,
88
+                        PW_KEY_NODE_NAME, "alsa_sink",
89
+           "api.alsa.path", dev,
90
+           "priority.driver", "1000",
91
+                        NULL);
92
+   data.sink = pw_core_create_object(data.core,
93
+           "spa-node-factory",
94
+           PW_TYPE_INTERFACE_Node,
95
+           PW_VERSION_NODE,
96
+           &props->dict, 0);
97
+
98
+   while (true) {
99
+       if (pw_proxy_get_bound_id(data.source) != SPA_ID_INVALID &&
100
+           pw_proxy_get_bound_id(data.sink) != SPA_ID_INVALID)
101
+           break;
102
+
103
+       pw_loop_iterate(pw_main_loop_get_loop(data.loop), -1);
104
+        }
105
+
106
+   pw_properties_clear(props);
107
+   pw_properties_setf(props,
108
+                        PW_KEY_LINK_OUTPUT_NODE, "%d", pw_proxy_get_bound_id(data.source));
109
+        pw_properties_setf(props,
110
+                        PW_KEY_LINK_INPUT_NODE, "%d", pw_proxy_get_bound_id(data.sink));
111
+
112
+   data.link = pw_core_create_object(data.core,
113
+           "link-factory",
114
+           PW_TYPE_INTERFACE_Link,
115
+           PW_VERSION_LINK,
116
+           &props->dict, 0);
117
+        pw_properties_free(props);
118
+
119
+   pw_main_loop_run(data.loop);
120
+
121
+cleanup:
122
+   pw_context_destroy(data.context);
123
+   pw_main_loop_destroy(data.loop);
124
+   pw_deinit();
125
+
126
+   return data.res;
127
+}
128
pipewire-0.3.71.tar.gz/src/examples/meson.build -> pipewire-0.3.72.tar.gz/src/examples/meson.build Changed
9
 
1
@@ -13,6 +13,7 @@
2
   'video-src-reneg',
3
   'video-src-fixate',
4
   'video-play-fixate',
5
+  'internal',
6
   'export-sink',
7
   'export-source',
8
   'export-spa',
9
pipewire-0.3.71.tar.gz/src/gst/gstpipewiresink.c -> pipewire-0.3.72.tar.gz/src/gst/gstpipewiresink.c Changed
10
 
1
@@ -482,7 +482,7 @@
2
     GstMemory *mem = gst_buffer_peek_memory (buffer, i);
3
     d->chunk->offset = mem->offset;
4
     d->chunk->size = mem->size;
5
-    d->chunk->stride = 0;
6
+    d->chunk->stride = pwsink->pool->video_info.stridei;
7
   }
8
 
9
   GstVideoMeta *meta = gst_buffer_get_video_meta (buffer);
10
pipewire-0.3.71.tar.gz/src/modules/meson.build -> pipewire-0.3.72.tar.gz/src/modules/meson.build Changed
72
 
1
@@ -14,12 +14,15 @@
2
   'module-example-sink.c',
3
   'module-example-source.c',
4
   'module-fallback-sink.c',
5
+  'module-ffado-driver.c',
6
   'module-filter-chain.c',
7
   'module-jack-tunnel.c',
8
   'module-jackdbus-detect.c',
9
   'module-link-factory.c',
10
   'module-loopback.c',
11
   'module-metadata.c',
12
+  'module-netjack2-driver.c',
13
+  'module-netjack2-manager.c',
14
   'module-pipe-tunnel.c',
15
   'module-portal.c',
16
   'module-profiler.c',
17
@@ -185,7 +188,45 @@
18
 
19
 summary({'jack-tunnel': build_module_jack_tunnel}, bool_yn: true, section: 'Optional Modules')
20
 
21
+build_module_ffado_driver = libffado_dep.found()
22
+if build_module_ffado_driver
23
+  pipewire_module_jack_tunnel = shared_library('pipewire-module-ffado-driver',
24
+     'module-ffado-driver.c' ,
25
+    include_directories : configinc,
26
+    install : true,
27
+    install_dir : modules_install_dir,
28
+    install_rpath: modules_install_dir,
29
+    dependencies : mathlib, dl_lib, pipewire_dep, libffado_dep,
30
+  )
31
+endif
32
+
33
+summary({'ffado-driver': build_module_ffado_driver}, bool_yn: true, section: 'Optional Modules')
34
+
35
+opus_custom_h = cc.has_header('opus/opus_custom.h', dependencies: opus_dep)
36
+if opus_custom_h
37
+  opus_custom_dep = declare_dependency(compile_args: '-DHAVE_OPUS_CUSTOM', dependencies: opus_dep)
38
+else
39
+  opus_custom_dep = dependency('', required: false)
40
+endif
41
+summary({'Opus with custom modes for NetJack2': opus_custom_dep}, bool_yn: true, section: 'Streaming between daemons')
42
+
43
+pipewire_module_netjack2_driver = shared_library('pipewire-module-netjack2-driver',
44
+   'module-netjack2-driver.c' ,
45
+  include_directories : configinc,
46
+  install : true,
47
+  install_dir : modules_install_dir,
48
+  install_rpath: modules_install_dir,
49
+  dependencies : spa_dep, mathlib, dl_lib, pipewire_dep, opus_custom_dep,
50
+)
51
 
52
+pipewire_module_netjack2_manager = shared_library('pipewire-module-netjack2-manager',
53
+   'module-netjack2-manager.c' ,
54
+  include_directories : configinc,
55
+  install : true,
56
+  install_dir : modules_install_dir,
57
+  install_rpath: modules_install_dir,
58
+  dependencies : spa_dep, mathlib, dl_lib, pipewire_dep, opus_custom_dep,
59
+)
60
 
61
 pipewire_module_profiler = shared_library('pipewire-module-profiler',
62
    'module-profiler.c',
63
@@ -322,6 +363,8 @@
64
   'module-protocol-pulse/modules/module-switch-on-connect.c',
65
   'module-protocol-pulse/modules/module-tunnel-sink.c',
66
   'module-protocol-pulse/modules/module-tunnel-source.c',
67
+  'module-protocol-pulse/modules/module-virtual-sink.c',
68
+  'module-protocol-pulse/modules/module-virtual-source.c',
69
   'module-protocol-pulse/modules/module-x11-bell.c',
70
   'module-protocol-pulse/modules/module-zeroconf-discover.c',
71
 
72
pipewire-0.3.71.tar.gz/src/modules/module-client-node/client-node.c -> pipewire-0.3.72.tar.gz/src/modules/module-client-node/client-node.c Changed
201
 
1
@@ -50,7 +50,7 @@
2
 
3
 struct mix {
4
    unsigned int valid:1;
5
-   uint32_t id;
6
+   uint32_t mix_id;
7
    struct port *port;
8
    uint32_t peer_id;
9
    uint32_t n_buffers;
10
@@ -209,10 +209,10 @@
11
    return mix;
12
 }
13
 
14
-static void mix_init(struct mix *mix, struct port *p, uint32_t id)
15
+static void mix_init(struct mix *mix, struct port *p, uint32_t mix_id)
16
 {
17
    mix->valid = true;
18
-   mix->id = id;
19
+   mix->mix_id = mix_id;
20
    mix->port = p;
21
    mix->n_buffers = 0;
22
 }
23
@@ -281,7 +281,7 @@
24
    if (!mix->valid)
25
        return;
26
    do_port_use_buffers(impl, port->direction, port->id,
27
-           mix->id, 0, NULL, 0);
28
+           mix->mix_id, 0, NULL, 0);
29
    mix->valid = false;
30
 }
31
 
32
@@ -890,8 +890,8 @@
33
     * directly */
34
    spa_log_warn(impl->log, "exported node activation");
35
    spa_system_clock_gettime(impl->data_system, CLOCK_MONOTONIC, &ts);
36
-   n->rt.activation->status = PW_NODE_ACTIVATION_TRIGGERED;
37
-   n->rt.activation->signal_time = SPA_TIMESPEC_TO_NSEC(&ts);
38
+   n->rt.target.activation->status = PW_NODE_ACTIVATION_TRIGGERED;
39
+   n->rt.target.activation->signal_time = SPA_TIMESPEC_TO_NSEC(&ts);
40
 
41
    if (SPA_UNLIKELY(spa_system_eventfd_write(n->rt.target.system, n->rt.target.fd, 1) < 0))
42
        pw_log_warn("%p: write failed %m", impl);
43
@@ -1080,8 +1080,6 @@
44
    if (SPA_LIKELY(source->rmask & SPA_IO_IN)) {
45
        uint64_t cmd;
46
        struct pw_impl_node *node = impl->this.node;
47
-       struct pw_node_activation *a = node->rt.activation;
48
-       int status;
49
 
50
        if (SPA_UNLIKELY(spa_system_eventfd_read(impl->data_system,
51
                    impl->data_source.fd, &cmd) < 0))
52
@@ -1090,9 +1088,15 @@
53
            pw_log_info("(%s-%u) client missed %"PRIu64" wakeups",
54
                node->name, node->info.id, cmd - 1);
55
 
56
-       status = a->state0.status;
57
-       spa_log_trace_fp(impl->log, "%p: got ready %d", impl, status);
58
-       spa_node_call_ready(&impl->callbacks, status);
59
+       if (impl->resource && impl->resource->version < 5) {
60
+           struct pw_node_activation *a = node->rt.target.activation;
61
+           int status = a->state0.status;
62
+           spa_log_trace_fp(impl->log, "%p: got ready %d", impl, status);
63
+           spa_node_call_ready(&impl->callbacks, status);
64
+       } else {
65
+           spa_log_trace_fp(impl->log, "%p: got complete", impl);
66
+           pw_context_driver_emit_complete(node->context, node);
67
+       }
68
    }
69
 }
70
 
71
@@ -1197,6 +1201,57 @@
72
    spa_node_emit_result(&impl->hooks, seq, 0, 0, NULL);
73
 }
74
 
75
+static void node_peer_added(void *data, struct pw_impl_node *peer)
76
+{
77
+   struct impl *impl = data;
78
+   struct pw_memblock *m;
79
+
80
+   m = pw_mempool_import_block(impl->client->pool, peer->activation);
81
+   if (m == NULL) {
82
+       pw_log_warn("%p: can't ensure mem: %m", impl);
83
+       return;
84
+   }
85
+
86
+   pw_log_debug("%p: peer %p/%p id:%u added mem_id:%u", impl, peer,
87
+           impl->this.node, peer->info.id, m->id);
88
+
89
+   if (impl->resource == NULL)
90
+       return;
91
+
92
+   pw_client_node_resource_set_activation(impl->resource,
93
+                     peer->info.id,
94
+                     peer->source.fd,
95
+                     m->id,
96
+                     0,
97
+                     sizeof(struct pw_node_activation));
98
+}
99
+
100
+static void node_peer_removed(void *data, struct pw_impl_node *peer)
101
+{
102
+   struct impl *impl = data;
103
+   struct pw_memblock *m;
104
+
105
+   m = pw_mempool_find_fd(impl->client->pool, peer->activation->fd);
106
+   if (m == NULL) {
107
+       pw_log_warn("%p: unknown peer %p fd:%d", impl, peer,
108
+           peer->source.fd);
109
+       return;
110
+   }
111
+
112
+   pw_log_debug("%p: peer %p/%p id:%u removed mem_id:%u", impl, peer,
113
+           impl->this.node, peer->info.id, m->id);
114
+
115
+   if (impl->resource != NULL) {
116
+       pw_client_node_resource_set_activation(impl->resource,
117
+                     peer->info.id,
118
+                     -1,
119
+                     SPA_ID_INVALID,
120
+                     0,
121
+                     0);
122
+   }
123
+   pw_memblock_unref(m);
124
+}
125
+
126
 void pw_impl_client_node_registered(struct pw_impl_client_node *this, struct pw_global *global)
127
 {
128
    struct impl *impl = SPA_CONTAINER_OF(this, struct impl, this);
129
@@ -1225,6 +1280,8 @@
130
                      0,
131
                      sizeof(struct pw_node_activation));
132
 
133
+   node_peer_added(impl, node);
134
+
135
    if (impl->bind_node_id) {
136
        pw_global_bind(global, client, PW_PERM_ALL,
137
                impl->bind_node_version, impl->bind_node_id);
138
@@ -1298,7 +1355,7 @@
139
        pw_resource_destroy(impl->resource);
140
 
141
    if (impl->activation)
142
-       pw_memblock_unref(impl->activation);
143
+       pw_memblock_free(impl->activation);
144
 
145
    pw_array_for_each(area, &impl->io_areas) {
146
        if (*area)
147
@@ -1351,6 +1408,11 @@
148
 
149
    m->peer_id = mix->peer_id;
150
 
151
+   if (impl->resource && impl->resource->version >= 4)
152
+       pw_client_node_resource_port_set_mix_info(impl->resource,
153
+                    mix->port.direction, mix->p->port_id,
154
+                    mix->port.port_id, mix->peer_id, NULL);
155
+
156
    pw_log_debug("%p: init mix id:%d io:%p base:%p", impl,
157
            mix->id, mix->io, area->map->ptr);
158
 
159
@@ -1373,6 +1435,11 @@
160
    if ((m = find_mix(port, mix->port.port_id)) == NULL || !m->valid)
161
        return -EINVAL;
162
 
163
+   if (impl->resource && impl->resource->version >= 4)
164
+       pw_client_node_resource_port_set_mix_info(impl->resource,
165
+                    mix->port.direction, mix->p->port_id,
166
+                    mix->port.port_id, SPA_ID_INVALID, NULL);
167
+
168
    pw_map_remove(&impl->io_map, mix->id);
169
    m->valid = false;
170
 
171
@@ -1458,13 +1525,7 @@
172
            mix->io = data;
173
        else
174
            mix->io = NULL;
175
-
176
-       if (mix->io != NULL && impl->resource && impl->resource->version >= 4)
177
-           pw_client_node_resource_port_set_mix_info(impl->resource,
178
-                        direction, port->port_id,
179
-                        mix->port.port_id, mix->peer_id, NULL);
180
    }
181
-
182
    return do_port_set_io(impl,
183
                  direction, port->port_id, mix->port.port_id,
184
                  id, data, size);
185
@@ -1544,62 +1605,6 @@
186
    clear_port(impl, p);
187
 }
188
 
189
-static void node_peer_added(void *data, struct pw_impl_node *peer)
190
-{
191
-   struct impl *impl = data;
192
-   struct pw_memblock *m;
193
-
194
-   if (peer == impl->this.node)
195
-       return;
196
-
197
-   m = pw_mempool_import_block(impl->client->pool, peer->activation);
198
-   if (m == NULL) {
199
-       pw_log_debug("%p: can't ensure mem: %m", impl);
200
-       return;
201
pipewire-0.3.71.tar.gz/src/modules/module-client-node/remote-node.c -> pipewire-0.3.72.tar.gz/src/modules/module-client-node/remote-node.c Changed
201
 
1
@@ -40,13 +40,15 @@
2
    struct spa_list link;
3
    struct pw_impl_port *port;
4
    uint32_t mix_id;
5
+   uint32_t peer_id;
6
    struct pw_impl_port_mix mix;
7
    struct pw_array buffers;
8
-   bool active;
9
 };
10
 
11
 struct node_data {
12
    struct pw_context *context;
13
+   struct spa_hook context_listener;
14
+
15
    struct pw_loop *data_loop;
16
    struct spa_system *data_system;
17
 
18
@@ -91,7 +93,7 @@
19
    struct link *l;
20
 
21
    spa_list_for_each(l, links, link) {
22
-       if (l->node_id == node_id)
23
+       if (l->target.id == node_id)
24
            return l;
25
    }
26
    return NULL;
27
@@ -138,66 +140,24 @@
28
    }
29
 
30
    pw_memmap_free(data->activation);
31
-   data->node->rt.activation = data->node->activation->map->ptr;
32
+   data->node->rt.target.activation = data->node->activation->map->ptr;
33
 
34
    spa_system_close(data->data_system, data->rtwritefd);
35
    data->have_transport = false;
36
 }
37
 
38
-static void mix_init(struct mix *mix, struct pw_impl_port *port, uint32_t mix_id)
39
+static void mix_init(struct mix *mix, struct pw_impl_port *port,
40
+       uint32_t mix_id, uint32_t peer_id)
41
 {
42
    pw_log_debug("port %p: mix init %d.%d", port, port->port_id, mix_id);
43
    mix->port = port;
44
    mix->mix_id = mix_id;
45
+   mix->peer_id = peer_id;
46
    pw_impl_port_init_mix(port, &mix->mix);
47
-   mix->active = false;
48
    pw_array_init(&mix->buffers, 32);
49
    pw_array_ensure_size(&mix->buffers, sizeof(struct buffer) * 64);
50
 }
51
 
52
-static int
53
-do_deactivate_mix(struct spa_loop *loop,
54
-                bool async, uint32_t seq, const void *data, size_t size, void *user_data)
55
-{
56
-   struct mix *mix = user_data;
57
-   spa_list_remove(&mix->mix.rt_link);
58
-        return 0;
59
-}
60
-
61
-static int
62
-deactivate_mix(struct node_data *data, struct mix *mix)
63
-{
64
-   if (mix->active) {
65
-       pw_log_debug("node %p: mix %p deactivate", data, mix);
66
-       pw_loop_invoke(data->data_loop,
67
-                       do_deactivate_mix, SPA_ID_INVALID, NULL, 0, true, mix);
68
-       mix->active = false;
69
-   }
70
-   return 0;
71
-}
72
-
73
-static int
74
-do_activate_mix(struct spa_loop *loop,
75
-                bool async, uint32_t seq, const void *data, size_t size, void *user_data)
76
-{
77
-   struct mix *mix = user_data;
78
-
79
-   spa_list_append(&mix->port->rt.mix_list, &mix->mix.rt_link);
80
-        return 0;
81
-}
82
-
83
-static int
84
-activate_mix(struct node_data *data, struct mix *mix)
85
-{
86
-   if (!mix->active) {
87
-       pw_log_debug("node %p: mix %p activate", data, mix);
88
-       pw_loop_invoke(data->data_loop,
89
-                       do_activate_mix, SPA_ID_INVALID, NULL, 0, false, mix);
90
-       mix->active = true;
91
-   }
92
-   return 0;
93
-}
94
-
95
 static struct mix *find_mix(struct node_data *data,
96
        enum spa_direction direction, uint32_t port_id, uint32_t mix_id)
97
 {
98
@@ -214,15 +174,13 @@
99
    return NULL;
100
 }
101
 
102
-static struct mix *ensure_mix(struct node_data *data,
103
-       enum spa_direction direction, uint32_t port_id, uint32_t mix_id)
104
+static struct mix *create_mix(struct node_data *data,
105
+       enum spa_direction direction, uint32_t port_id,
106
+       uint32_t mix_id, uint32_t peer_id)
107
 {
108
    struct mix *mix;
109
    struct pw_impl_port *port;
110
 
111
-   if ((mix = find_mix(data, direction, port_id, mix_id)))
112
-       return mix;
113
-
114
    port = pw_impl_node_find_port(data->node, direction, port_id);
115
    if (port == NULL)
116
        return NULL;
117
@@ -234,13 +192,21 @@
118
        mix = spa_list_first(&data->free_mix, struct mix, link);
119
        spa_list_remove(&mix->link);
120
    }
121
-
122
-   mix_init(mix, port, mix_id);
123
+   mix_init(mix, port, mix_id, peer_id);
124
    spa_list_append(&data->mixdirection, &mix->link);
125
 
126
    return mix;
127
 }
128
 
129
+static struct mix *ensure_mix(struct node_data *data,
130
+       enum spa_direction direction, uint32_t port_id,
131
+       uint32_t mix_id)
132
+{
133
+   struct mix *mix;
134
+   if ((mix = find_mix(data, direction, port_id, mix_id)))
135
+       return mix;
136
+   return create_mix(data, direction, port_id, mix_id, SPA_ID_INVALID);
137
+}
138
 
139
 static int client_node_transport(void *_data,
140
            int readfd, int writefd, uint32_t mem_id, uint32_t offset, uint32_t size)
141
@@ -258,7 +224,10 @@
142
        return -errno;
143
    }
144
 
145
-   node->rt.activation = data->activation->ptr;
146
+   node->rt.target.activation = data->activation->ptr;
147
+   node->rt.position = &node->rt.target.activation->position;
148
+   node->info.id = node->rt.target.activation->position.clock.id;
149
+   node->rt.target.id = node->info.id;
150
 
151
    pw_log_debug("remote-node %p: fds:%d %d node:%u activation:%p",
152
        proxy, readfd, writefd, data->remote_id, data->activation->ptr);
153
@@ -824,11 +793,6 @@
154
    pw_log_debug("port %p: set io:%s new:%p old:%p", mix->port,
155
            spa_debug_type_find_name(spa_type_io, id), ptr, mix->mix.io);
156
 
157
-   if (id == SPA_IO_Buffers) {
158
-       if (ptr == NULL && mix->mix.io)
159
-           deactivate_mix(data, mix);
160
-   }
161
-
162
    if ((res = spa_node_port_set_io(mix->port->mix,
163
                 direction, mix->mix.port.port_id, id, ptr, size)) < 0) {
164
        if (res == -ENOTSUP)
165
@@ -836,11 +800,6 @@
166
        else
167
            goto exit_free;
168
    }
169
-   if (id == SPA_IO_Buffers) {
170
-       mix->mix.io = ptr;
171
-       if (ptr)
172
-           activate_mix(data, mix);
173
-   }
174
 exit_free:
175
    pw_memmap_free(old);
176
 exit:
177
@@ -878,13 +837,6 @@
178
    struct link *link;
179
    int res = 0;
180
 
181
-   if (data->remote_id == node_id) {
182
-       pw_log_debug("node %p: our activation %u: %u %u %u", node, node_id,
183
-               memid, offset, size);
184
-       spa_system_close(data->data_system, signalfd);
185
-       return 0;
186
-   }
187
-
188
    if (memid == SPA_ID_INVALID) {
189
        mm = ptr = NULL;
190
        size = 0;
191
@@ -897,7 +849,13 @@
192
        }
193
        ptr = mm->ptr;
194
    }
195
-   pw_log_debug("node %p: set activation %d %p %u %u", node, node_id, ptr, offset, size);
196
+   if (data->remote_id == node_id) {
197
+       pw_log_debug("node %p: our activation %u: %u %p %u %u", node, node_id,
198
+               memid, ptr, offset, size);
199
+   } else {
200
+       pw_log_debug("node %p: set activation %u: %u %p %u %u", node, node_id,
201
pipewire-0.3.71.tar.gz/src/modules/module-combine-stream.c -> pipewire-0.3.72.tar.gz/src/modules/module-combine-stream.c Changed
68
 
1
@@ -965,8 +965,17 @@
2
    struct stream *s;
3
    bool delay_changed = false;
4
 
5
-   if ((in = pw_stream_dequeue_buffer(impl->combine)) == NULL) {
6
-       pw_log_debug("out of buffers: %m");
7
+   in = NULL;
8
+   while (true) {
9
+       struct pw_buffer *t;
10
+       if ((t = pw_stream_dequeue_buffer(impl->combine)) == NULL)
11
+           break;
12
+       if (in)
13
+           pw_stream_queue_buffer(impl->combine, in);
14
+       in = t;
15
+   }
16
+   if (in == NULL) {
17
+       pw_log_debug("%p: out of input buffers: %m", impl);
18
        return;
19
    }
20
 
21
@@ -980,7 +989,7 @@
22
            delay_changed = true;
23
 
24
        if ((out = pw_stream_dequeue_buffer(s->stream)) == NULL) {
25
-           pw_log_warn("out of playback buffers: %m");
26
+           pw_log_warn("%p: out of playback buffers: %m", s);
27
            goto do_trigger;
28
        }
29
 
30
@@ -1033,7 +1042,7 @@
31
    bool delay_changed = false;
32
 
33
    if ((out = pw_stream_dequeue_buffer(impl->combine)) == NULL) {
34
-       pw_log_debug("out of buffers: %m");
35
+       pw_log_debug("%p: out of output buffers: %m", impl);
36
        return;
37
    }
38
 
39
@@ -1046,8 +1055,17 @@
40
        if (check_stream_delay(s))
41
            delay_changed = true;
42
 
43
-       if ((in = pw_stream_dequeue_buffer(s->stream)) == NULL) {
44
-           pw_log_warn("%p: out of capture buffers: %m", s);
45
+       in = NULL;
46
+       while (true) {
47
+           struct pw_buffer *t;
48
+           if ((t = pw_stream_dequeue_buffer(s->stream)) == NULL)
49
+               break;
50
+           if (in)
51
+               pw_stream_queue_buffer(s->stream, in);
52
+           in = t;
53
+       }
54
+       if (in == NULL) {
55
+           pw_log_debug("%p: out of input buffers: %m", s);
56
            continue;
57
        }
58
        s->ready = false;
59
@@ -1357,6 +1375,8 @@
60
        pw_properties_set(props, PW_KEY_NODE_VIRTUAL, "true");
61
    if (pw_properties_get(props, "resample.prefill") == NULL)
62
        pw_properties_set(props, "resample.prefill", "true");
63
+   if (pw_properties_get(props, "resample.disable") == NULL)
64
+       pw_properties_set(props, "resample.disable", "true");
65
 
66
    if (pw_properties_get(props, PW_KEY_MEDIA_CLASS) == NULL) {
67
        if (impl->mode == MODE_SINK)
68
pipewire-0.3.72.tar.gz/src/modules/module-ffado-driver.c Added
201
 
1
@@ -0,0 +1,1148 @@
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
+#include <pipewire/thread.h>
33
+
34
+#include <libffado/ffado.h>
35
+
36
+/** \page page_module_ffado_driver PipeWire Module: FFADO firewire audio driver
37
+ *
38
+ * The ffado-driver module provides a source or sink using the libffado library for
39
+ * reading and writing to firewire audio devices.
40
+ *
41
+ * ## Module Options
42
+ *
43
+ * - `driver.mode`: the driver mode, sink|source|duplex, default duplex
44
+ * - `ffado.devices`: array of devices to open, default hw:0
45
+ * - `ffado.period-size`: period size,default 1024
46
+ * - `ffado.period-num`: period number,default 3
47
+ * - `ffado.sample-rate`: sample-rate, default 48000
48
+ * - `ffado.slave-mode`: slave mode
49
+ * - `ffado.snoop-mode`: snoop mode
50
+ * - `ffado.verbose`: ffado verbose level
51
+ * - `latency.internal.input`: extra input latency in frames
52
+ * - `latency.internal.output`: extra output latency in frames
53
+ * - `source.props`: Extra properties for the source filter.
54
+ * - `sink.props`: Extra properties for the sink filter.
55
+ *
56
+ * ## General options
57
+ *
58
+ * Options with well-known behavior.
59
+ *
60
+ * - \ref PW_KEY_REMOTE_NAME
61
+ * - \ref SPA_KEY_AUDIO_POSITION
62
+ * - \ref PW_KEY_NODE_NAME
63
+ * - \ref PW_KEY_NODE_DESCRIPTION
64
+ * - \ref PW_KEY_NODE_GROUP
65
+ * - \ref PW_KEY_NODE_VIRTUAL
66
+ * - \ref PW_KEY_MEDIA_CLASS
67
+ * - \ref PW_KEY_TARGET_OBJECT to specify the remote node.name or serial.id to link to
68
+ *
69
+ * ## Example configuration of a duplex sink/source
70
+ *
71
+ *\code{.unparsed}
72
+ * context.modules = 
73
+ * {   name = libpipewire-module-ffado-driver
74
+ *     args = {
75
+ *         #driver.mode       = duplex
76
+ *         #ffado.devices     =  hw:0 
77
+ *         #ffado.period-size = 1024
78
+ *         #ffado.period-num  = 3
79
+ *         #ffado.sample-rate = 48000
80
+ *         #ffado.slave-mode  = false
81
+ *         #ffado.snoop-mode  = false
82
+ *         #ffado.verbose     = 0
83
+ *         #latency.internal.input  = 0
84
+ *         #latency.internal.output = 0
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 "ffado-driver"
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_DEVICES        " hw:0 "
106
+#define DEFAULT_PERIOD_SIZE    1024
107
+#define DEFAULT_PERIOD_NUM 3
108
+#define DEFAULT_SAMPLE_RATE    48000
109
+#define DEFAULT_SLAVE_MODE false
110
+#define DEFAULT_SNOOP_MODE false
111
+#define DEFAULT_VERBOSE        0
112
+
113
+#define DEFAULT_POSITION   " FL FR "
114
+#define DEFAULT_MIDI_PORTS 1
115
+
116
+#define MODULE_USAGE   "( remote.name=<remote> ) "             \
117
+           "( driver.mode=<sink|source|duplex> ) "         \
118
+           "( ffado.devices=<devices array size, default hw:0> ) " \
119
+           "( ffado.period-size=<period size, default 1024> ) "    \
120
+           "( ffado.period-num=<period num, default 3> ) "     \
121
+           "( ffado.sample-rate=<sampe rate, default 48000> ) "    \
122
+           "( ffado.slave-mode=<slave mode, default false> ) " \
123
+           "( ffado.snoop-mode=<snoop mode, default false> ) " \
124
+           "( ffado.verbose=<verbose level, default 0> ) "     \
125
+           "( audio.position=<channel map> ) "         \
126
+           "( source.props=<properties> ) "            \
127
+           "( sink.props=<properties> ) "
128
+
129
+
130
+static const struct spa_dict_item module_props = {
131
+   { PW_KEY_MODULE_AUTHOR, "Wim Taymans <wim.taymans@gmail.com>" },
132
+   { PW_KEY_MODULE_DESCRIPTION, "Create an FFADO based driver" },
133
+   { PW_KEY_MODULE_USAGE, MODULE_USAGE },
134
+   { PW_KEY_MODULE_VERSION, PACKAGE_VERSION },
135
+};
136
+
137
+struct port {
138
+   enum spa_direction direction;
139
+   struct spa_latency_info latency2;
140
+   bool latency_changed2;
141
+   unsigned int is_midi:1;
142
+   void *buffer;
143
+};
144
+
145
+struct volume {
146
+   bool mute;
147
+   uint32_t n_volumes;
148
+   float volumesSPA_AUDIO_MAX_CHANNELS;
149
+};
150
+
151
+struct stream {
152
+   struct impl *impl;
153
+
154
+   enum spa_direction direction;
155
+   struct pw_properties *props;
156
+   struct pw_filter *filter;
157
+   struct spa_hook listener;
158
+   struct spa_audio_info_raw info;
159
+   uint32_t n_ports;
160
+   struct port *portsMAX_PORTS;
161
+   struct volume volume;
162
+
163
+   unsigned int running:1;
164
+};
165
+
166
+struct impl {
167
+   struct pw_context *context;
168
+   struct pw_loop *main_loop;
169
+   struct spa_system *system;
170
+   struct spa_thread_utils *utils;
171
+
172
+   ffado_device_info_t device_info;
173
+   ffado_options_t device_options;
174
+   ffado_device_t *dev;
175
+
176
+#define MODE_SINK  (1<<0)
177
+#define MODE_SOURCE    (1<<1)
178
+#define MODE_DUPLEX    (MODE_SINK|MODE_SOURCE)
179
+   uint32_t mode;
180
+   struct pw_properties *props;
181
+
182
+   struct pw_impl_module *module;
183
+
184
+   struct spa_hook module_listener;
185
+
186
+   struct pw_core *core;
187
+   struct spa_hook core_proxy_listener;
188
+   struct spa_hook core_listener;
189
+
190
+   struct spa_io_position *position;
191
+
192
+   uint32_t latency2;
193
+
194
+   struct stream source;
195
+   struct stream sink;
196
+
197
+   char *devicesFFADO_MAX_SPECSTRINGS;
198
+   uint32_t n_devices;
199
+   int32_t sample_rate;
200
+   int32_t period_size;
201
pipewire-0.3.71.tar.gz/src/modules/module-filter-chain.c -> pipewire-0.3.72.tar.gz/src/modules/module-filter-chain.c Changed
43
 
1
@@ -166,6 +166,9 @@
2
  * are read-only except for the bq_raw biquad, which can configure default values
3
  * depending on the graph rate and change those at runtime.
4
  *
5
+ * We refer to https://arachnoid.com/BiQuadDesigner/index.html for an explanation of
6
+ * the controls.
7
+ *
8
  * The following labels can be used:
9
  *
10
  * - `bq_lowpass` a lowpass filter.
11
@@ -685,7 +688,16 @@
12
    struct graph_port *port;
13
    struct spa_data *bd;
14
 
15
-   if ((in = pw_stream_dequeue_buffer(impl->capture)) == NULL)
16
+   in = NULL;
17
+   while (true) {
18
+       struct pw_buffer *t;
19
+       if ((t = pw_stream_dequeue_buffer(impl->capture)) == NULL)
20
+           break;
21
+       if (in)
22
+           pw_stream_queue_buffer(impl->capture, in);
23
+       in = t;
24
+   }
25
+   if (in == NULL)
26
        pw_log_debug("%p: out of capture buffers: %m", impl);
27
 
28
    if ((out = pw_stream_dequeue_buffer(impl->playback)) == NULL)
29
@@ -2454,7 +2466,12 @@
30
    parse_audio_info(impl->capture_props, &impl->capture_info);
31
    parse_audio_info(impl->playback_props, &impl->playback_info);
32
 
33
-   if (impl->capture_info.rate && !impl->playback_info.rate)
34
+   if (!impl->capture_info.rate && !impl->playback_info.rate) {
35
+       if (pw_properties_get(impl->playback_props, "resample.disable") == NULL)
36
+           pw_properties_set(impl->playback_props, "resample.disable", "true");
37
+       if (pw_properties_get(impl->capture_props, "resample.disable") == NULL)
38
+           pw_properties_set(impl->capture_props, "resample.disable", "true");
39
+   } else if (impl->capture_info.rate && !impl->playback_info.rate)
40
        impl->playback_info.rate = impl->capture_info.rate;
41
    else if (impl->playback_info.rate && !impl->capture_info.rate)
42
        impl->capture_info.rate = !impl->playback_info.rate;
43
pipewire-0.3.71.tar.gz/src/modules/module-jack-tunnel.c -> pipewire-0.3.72.tar.gz/src/modules/module-jack-tunnel.c Changed
95
 
1
@@ -193,6 +193,7 @@
2
    uint32_t jack_xrun;
3
 
4
    unsigned int do_disconnect:1;
5
+   unsigned int triggered:1;
6
    unsigned int done:1;
7
    unsigned int new_xrun:1;
8
    unsigned int fix_midi:1;
9
@@ -323,6 +324,11 @@
10
    struct impl *impl = s->impl;
11
    uint32_t i, n_samples = position->clock.duration;
12
 
13
+   if (impl->mode & MODE_SINK && impl->triggered) {
14
+       impl->triggered = false;
15
+       return;
16
+   }
17
+
18
    for (i = 0; i < s->n_ports; i++) {
19
        struct port *p = s->portsi;
20
        float *src, *dst;
21
@@ -342,7 +348,7 @@
22
        else
23
            do_volume(dst, src, &s->volume, i, n_samples);
24
    }
25
-   pw_log_trace_fp("done %u", impl->frame_time);
26
+   pw_log_trace_fp("done %u %u", impl->frame_time, n_samples);
27
    if (impl->mode & MODE_SINK) {
28
        impl->done = true;
29
        jack.cycle_signal(impl->client, 0);
30
@@ -355,6 +361,14 @@
31
    struct impl *impl = s->impl;
32
    uint32_t i, n_samples = position->clock.duration;
33
 
34
+   if (impl->mode == MODE_SOURCE && !impl->triggered) {
35
+       pw_log_trace_fp("done %u", impl->frame_time);
36
+       impl->done = true;
37
+       jack.cycle_signal(impl->client, 0);
38
+       return;
39
+   }
40
+   impl->triggered = false;
41
+
42
    for (i = 0; i < s->n_ports; i++) {
43
        struct port *p = s->portsi;
44
        float *src, *dst;
45
@@ -373,11 +387,6 @@
46
        else
47
            do_volume(dst, src, &s->volume, i, n_samples);
48
    }
49
-   pw_log_trace_fp("done %u", impl->frame_time);
50
-   if (impl->mode == MODE_SOURCE) {
51
-       impl->done = true;
52
-       jack.cycle_signal(impl->client, 0);
53
-   }
54
 }
55
 
56
 static void stream_io_changed(void *data, void *port_data, uint32_t id, void *area, uint32_t size)
57
@@ -606,9 +615,7 @@
58
    const struct spa_pod *params4;
59
    uint8_t buffer1024;
60
    struct spa_pod_builder b;
61
-   struct spa_latency_info latency;
62
 
63
-   spa_zero(latency);
64
    n_params = 0;
65
    spa_pod_builder_init(&b, buffer, sizeof(buffer));
66
 
67
@@ -703,11 +710,14 @@
68
        }
69
        if (impl->mode & MODE_SINK && sink_running) {
70
            impl->done = false;
71
+           impl->triggered = true;
72
            pw_filter_trigger_process(impl->sink.filter);
73
        } else if (impl->mode == MODE_SOURCE && source_running) {
74
            impl->done = false;
75
+           impl->triggered = true;
76
            pw_filter_trigger_process(impl->source.filter);
77
        } else {
78
+           pw_log_trace_fp("done %d", nframes);
79
            jack.cycle_signal(impl->client, 0);
80
        }
81
    }
82
@@ -801,9 +811,9 @@
83
 
84
            jack.port_get_latency_range(port->jack_port, mode, &range);
85
 
86
-           latency.direction = s->direction;
87
-           latency.min_rate = range.min;
88
-           latency.max_rate = range.max;
89
+           latency = SPA_LATENCY_INFO(s->direction,
90
+                   .min_rate = range.min,
91
+                   .max_rate = range.max);
92
            pw_log_debug("port latency %d %d %d", mode, range.min, range.max);
93
 
94
            if (spa_latency_info_compare(&latency, &port->latencys->direction)) {
95
pipewire-0.3.71.tar.gz/src/modules/module-jackdbus-detect.c -> pipewire-0.3.72.tar.gz/src/modules/module-jackdbus-detect.c Changed
10
 
1
@@ -38,7 +38,7 @@
2
  * ## Example configuration
3
  *\code{.unparsed}
4
  * context.modules = 
5
- *  {   name = libpipewire-jackdbus-detect
6
+ *  {   name = libpipewire-module-jackdbus-detect
7
  *      args {
8
  *         #jack.server    = null
9
  *         #tunnel.mode    = duplex
10
pipewire-0.3.71.tar.gz/src/modules/module-loopback.c -> pipewire-0.3.72.tar.gz/src/modules/module-loopback.c Changed
89
 
1
@@ -180,6 +180,7 @@
2
    unsigned int do_disconnect:1;
3
    unsigned int recalc_delay:1;
4
 
5
+   struct spa_audio_info_raw delay_info;
6
    float target_delay;
7
    struct spa_ringbuffer buffer;
8
    uint8_t *buffer_data;
9
@@ -195,7 +196,7 @@
10
 
11
 static void recalculate_delay(struct impl *impl)
12
 {
13
-   uint32_t target = impl->capture_info.rate * impl->target_delay, cdelay, pdelay;
14
+   uint32_t target = impl->delay_info.rate * impl->target_delay, cdelay, pdelay;
15
    uint32_t delay, w;
16
    struct pw_time pwt;
17
 
18
@@ -232,11 +233,20 @@
19
        impl->recalc_delay = false;
20
    }
21
 
22
-   if ((in = pw_stream_dequeue_buffer(impl->capture)) == NULL)
23
-       pw_log_debug("out of capture buffers: %m");
24
+   in = NULL;
25
+   while (true) {
26
+       struct pw_buffer *t;
27
+       if ((t = pw_stream_dequeue_buffer(impl->capture)) == NULL)
28
+           break;
29
+       if (in)
30
+           pw_stream_queue_buffer(impl->capture, in);
31
+       in = t;
32
+   }
33
+   if (in == NULL)
34
+       pw_log_debug("%p: out of capture buffers: %m", impl);
35
 
36
    if ((out = pw_stream_dequeue_buffer(impl->playback)) == NULL)
37
-       pw_log_debug("out of playback buffers: %m");
38
+       pw_log_debug("%p: out of playback buffers: %m", impl);
39
 
40
    if (in != NULL && out != NULL) {
41
        uint32_t outsize = UINT32_MAX;
42
@@ -347,11 +357,11 @@
43
 static void recalculate_buffer(struct impl *impl)
44
 {
45
    if (impl->target_delay > 0.0f) {
46
-       uint32_t delay = impl->capture_info.rate * impl->target_delay;
47
+       uint32_t delay = impl->delay_info.rate * impl->target_delay;
48
        void *data;
49
 
50
        impl->buffer_size = (delay + (1u<<15)) * 4;
51
-       data = realloc(impl->buffer_data, impl->buffer_size * impl->capture_info.channels);
52
+       data = realloc(impl->buffer_data, impl->buffer_size * impl->delay_info.channels);
53
        if (data == NULL) {
54
            pw_log_warn("can't allocate delay buffer, delay disabled: %m");
55
            impl->buffer_size = 0;
56
@@ -385,7 +395,7 @@
57
            info.channels > SPA_AUDIO_MAX_CHANNELS)
58
            return;
59
 
60
-       impl->capture_info = info;
61
+       impl->delay_info = info;
62
        recalculate_buffer(impl);
63
        break;
64
    }
65
@@ -696,6 +706,23 @@
66
    parse_audio_info(impl->capture_props, &impl->capture_info);
67
    parse_audio_info(impl->playback_props, &impl->playback_info);
68
 
69
+   if (!impl->capture_info.rate && !impl->playback_info.rate) {
70
+       if (pw_properties_get(impl->playback_props, "resample.disable") == NULL)
71
+           pw_properties_set(impl->playback_props, "resample.disable", "true");
72
+       if (pw_properties_get(impl->capture_props, "resample.disable") == NULL)
73
+           pw_properties_set(impl->capture_props, "resample.disable", "true");
74
+   } else if (impl->capture_info.rate && !impl->playback_info.rate)
75
+       impl->playback_info.rate = impl->capture_info.rate;
76
+   else if (impl->playback_info.rate && !impl->capture_info.rate)
77
+       impl->capture_info.rate = !impl->playback_info.rate;
78
+   else if (impl->capture_info.rate != impl->playback_info.rate) {
79
+       pw_log_warn("Both capture and playback rate are set, but"
80
+           " they are different. Using the highest of two. This behaviour"
81
+           " is deprecated, please use equal rates in the module config");
82
+       impl->playback_info.rate = impl->capture_info.rate =
83
+           SPA_MAX(impl->playback_info.rate, impl->capture_info.rate);
84
+   }
85
+
86
    if (pw_properties_get(impl->capture_props, PW_KEY_MEDIA_NAME) == NULL)
87
        pw_properties_setf(impl->capture_props, PW_KEY_MEDIA_NAME, "%s input",
88
                pw_properties_get(impl->capture_props, PW_KEY_NODE_DESCRIPTION));
89
pipewire-0.3.72.tar.gz/src/modules/module-netjack2 Added
2
 
1
+(directory)
2
pipewire-0.3.72.tar.gz/src/modules/module-netjack2-driver.c Added
201
 
1
@@ -0,0 +1,1374 @@
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 <arpa/inet.h>
17
+#include <netinet/ip.h>
18
+#include <netinet/in.h>
19
+#include <net/if.h>
20
+#include <math.h>
21
+
22
+#include "config.h"
23
+
24
+#include <spa/utils/result.h>
25
+#include <spa/utils/string.h>
26
+#include <spa/utils/json.h>
27
+#include <spa/debug/types.h>
28
+#include <spa/pod/builder.h>
29
+#include <spa/param/audio/format-utils.h>
30
+#include <spa/param/latency-utils.h>
31
+#include <spa/param/audio/raw.h>
32
+
33
+#include <pipewire/impl.h>
34
+#include <pipewire/i18n.h>
35
+#include <pipewire/private.h>
36
+
37
+#include "module-netjack2/packets.h"
38
+#include "module-netjack2/peer.c"
39
+
40
+#ifndef IPTOS_DSCP
41
+#define IPTOS_DSCP_MASK 0xfc
42
+#define IPTOS_DSCP(x) ((x) & IPTOS_DSCP_MASK)
43
+#endif
44
+
45
+/** \page page_module_netjack2_driver PipeWire Module: Netjack2 driver
46
+ *
47
+ * The netjack2-driver module provides a source or sink that is following a
48
+ * netjack2 driver.
49
+ *
50
+ * ## Module Options
51
+ *
52
+ * - `driver.mode`: the driver mode, sink|source|duplex, default duplex
53
+ * - `local.ifname = <str>`: interface name to use
54
+ * - `net.ip =<str>`: multicast IP address, default "225.3.19.154"
55
+ * - `net.port =<int>`: control port, default "19000"
56
+ * - `net.mtu = <int>`: MTU to use, default 1500
57
+ * - `net.ttl = <int>`: TTL to use, default 1
58
+ * - `net.loop = <bool>`: loopback multicast, default false
59
+ * - `netjack2.client-name`: the name of the NETJACK2 client.
60
+ * - `netjack2.save`: if jack port connections should be save automatically. Can also be
61
+ *                   placed per stream.
62
+ * - `netjack2.latency`: the latency in cycles, default 2
63
+ * - `audio.channels`: the number of audio ports. Can also be added to the stream props.
64
+ * - `midi.ports`: the number of midi ports. Can also be added to the stream props.
65
+ * - `source.props`: Extra properties for the source filter.
66
+ * - `sink.props`: Extra properties for the sink filter.
67
+ *
68
+ * ## General options
69
+ *
70
+ * Options with well-known behavior.
71
+ *
72
+ * - \ref PW_KEY_REMOTE_NAME
73
+ * - \ref PW_KEY_AUDIO_CHANNELS
74
+ * - \ref SPA_KEY_AUDIO_POSITION
75
+ * - \ref PW_KEY_NODE_NAME
76
+ * - \ref PW_KEY_NODE_DESCRIPTION
77
+ * - \ref PW_KEY_NODE_GROUP
78
+ * - \ref PW_KEY_NODE_VIRTUAL
79
+ * - \ref PW_KEY_MEDIA_CLASS
80
+ * - \ref PW_KEY_TARGET_OBJECT to specify the remote node.name or serial.id to link to
81
+ *
82
+ * ## Example configuration of a duplex sink/source
83
+ *
84
+ *\code{.unparsed}
85
+ * context.modules = 
86
+ * {   name = libpipewire-module-netjack2-driver
87
+ *     args = {
88
+ *         #driver.mode          = duplex
89
+ *         #netjack2.client-name = PipeWire
90
+ *         #netjack2.save        = false
91
+ *         #netjack2.latency     = 2
92
+ *         #midi.ports           = 0
93
+ *         #audio.channels       = 2
94
+ *         #audio.position       =  FL FR 
95
+ *         source.props = {
96
+ *             # extra sink properties
97
+ *         }
98
+ *         sink.props = {
99
+ *             # extra sink properties
100
+ *         }
101
+ *     }
102
+ * }
103
+ * 
104
+ *\endcode
105
+ */
106
+
107
+#define NAME "netjack2-driver"
108
+
109
+PW_LOG_TOPIC_STATIC(mod_topic, "mod." NAME);
110
+#define PW_LOG_TOPIC_DEFAULT mod_topic
111
+
112
+#define MAX_PORTS  128
113
+
114
+#define DEFAULT_NET_IP     "225.3.19.154"
115
+#define DEFAULT_NET_PORT   19000
116
+#define DEFAULT_NET_TTL        1
117
+#define DEFAULT_NET_MTU        1500
118
+#define DEFAULT_NET_LOOP   false
119
+#define DEFAULT_NET_DSCP   34 /* Default to AES-67 AF41 (34) */
120
+#define MAX_MTU            9000
121
+
122
+#define DEFAULT_NETWORK_LATENCY    2
123
+#define NETWORK_MAX_LATENCY    30
124
+
125
+#define DEFAULT_CLIENT_NAME    "PipeWire"
126
+#define DEFAULT_CHANNELS   2
127
+#define DEFAULT_POSITION   " FL FR "
128
+#define DEFAULT_MIDI_PORTS 1
129
+
130
+#define FOLLOWER_INIT_TIMEOUT  1
131
+#define FOLLOWER_INIT_RETRY    -1
132
+
133
+#define MODULE_USAGE   "( remote.name=<remote> ) "             \
134
+           "( driver.mode=<sink|source|duplex> ) "         \
135
+           "( local.ifname=<interface name> ) "            \
136
+           "( net.ip=<ip address to use, default 225.3.19.154> ) " \
137
+           "( net.port=<port to use, default 19000> ) "        \
138
+           "( net.mtu=<MTU to use, default 1500> ) "       \
139
+           "( net.ttl=<TTL to use, default 1> ) "          \
140
+           "( net.loop=<loopback, default false> ) "       \
141
+           "( netjack2.client-name=<name of the NETJACK2 client> ) "   \
142
+           "( netjack2.save=<bool, save ports> ) "         \
143
+           "( netjack2.latency=<latency in cycles, default 2> ) "  \
144
+           "( midi.ports=<number of midi ports> ) "        \
145
+           "( audio.channels=<number of channels> ) "      \
146
+           "( audio.position=<channel map> ) "         \
147
+           "( source.props=<properties> ) "            \
148
+           "( sink.props=<properties> ) "
149
+
150
+
151
+static const struct spa_dict_item module_props = {
152
+   { PW_KEY_MODULE_AUTHOR, "Wim Taymans <wim.taymans@gmail.com>" },
153
+   { PW_KEY_MODULE_DESCRIPTION, "Create a netjack2 driver" },
154
+   { PW_KEY_MODULE_USAGE, MODULE_USAGE },
155
+   { PW_KEY_MODULE_VERSION, PACKAGE_VERSION },
156
+};
157
+
158
+struct port {
159
+   enum spa_direction direction;
160
+   struct spa_latency_info latency2;
161
+   bool latency_changed2;
162
+   unsigned int is_midi:1;
163
+};
164
+
165
+struct stream {
166
+   struct impl *impl;
167
+
168
+   enum spa_direction direction;
169
+   struct pw_properties *props;
170
+   struct pw_filter *filter;
171
+   struct spa_hook listener;
172
+
173
+   struct spa_audio_info_raw info;
174
+
175
+   uint32_t n_midi;
176
+   uint32_t n_ports;
177
+   struct port *portsMAX_PORTS;
178
+
179
+   struct volume volume;
180
+
181
+   uint32_t active_audio_ports;
182
+   uint32_t active_midi_ports;
183
+
184
+   unsigned int running:1;
185
+};
186
+
187
+struct impl {
188
+   struct pw_context *context;
189
+   struct pw_loop *main_loop;
190
+   struct pw_data_loop *data_loop;
191
+   struct spa_system *system;
192
+
193
+#define MODE_SINK  (1<<0)
194
+#define MODE_SOURCE    (1<<1)
195
+#define MODE_DUPLEX    (MODE_SINK|MODE_SOURCE)
196
+   uint32_t mode;
197
+   struct pw_properties *props;
198
+
199
+   bool loop;
200
+   int ttl;
201
pipewire-0.3.72.tar.gz/src/modules/module-netjack2-manager.c Added
201
 
1
@@ -0,0 +1,1418 @@
2
+/* PipeWire */
3
+/* SPDX-FileCopyrightText: Copyright © 2023 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 <sys/ioctl.h>
12
+#include <fcntl.h>
13
+#include <unistd.h>
14
+#include <stdlib.h>
15
+#include <signal.h>
16
+#include <limits.h>
17
+#include <arpa/inet.h>
18
+#include <netinet/ip.h>
19
+#include <netinet/in.h>
20
+#include <net/if.h>
21
+#include <math.h>
22
+
23
+#include "config.h"
24
+
25
+#include <spa/utils/result.h>
26
+#include <spa/utils/string.h>
27
+#include <spa/utils/json.h>
28
+#include <spa/debug/types.h>
29
+#include <spa/pod/builder.h>
30
+#include <spa/param/audio/format-utils.h>
31
+#include <spa/param/latency-utils.h>
32
+#include <spa/param/audio/raw.h>
33
+
34
+#include <pipewire/impl.h>
35
+#include <pipewire/i18n.h>
36
+#include <pipewire/private.h>
37
+
38
+#include "module-netjack2/packets.h"
39
+
40
+#include "module-netjack2/peer.c"
41
+
42
+#ifndef IPTOS_DSCP
43
+#define IPTOS_DSCP_MASK 0xfc
44
+#define IPTOS_DSCP(x) ((x) & IPTOS_DSCP_MASK)
45
+#endif
46
+
47
+/** \page page_module_netjack2_manager PipeWire Module: Netjack2 manager
48
+ *
49
+ * The netjack2 manager module listens for new netjack2 driver messages and will
50
+ * start a communication channel with them.
51
+ *
52
+ * ## Module Options
53
+ *
54
+ * - `local.ifname = <str>`: interface name to use
55
+ * - `net.ip =<str>`: multicast IP address, default "225.3.19.154"
56
+ * - `net.port =<int>`: control port, default "19000"
57
+ * - `net.mtu = <int>`: MTU to use, default 1500
58
+ * - `net.ttl = <int>`: TTL to use, default 1
59
+ * - `net.loop = <bool>`: loopback multicast, default false
60
+ * - `netjack2.connect`: if jack ports should be connected automatically. Can also be
61
+ *                   placed per stream.
62
+ * - `netjack2.sample-rate`: the sample rate to use, default 48000
63
+ * - `netjack2.period-size`: the buffer size to use, default 1024
64
+ * - `netjack2.encoding`: the encoding, float|opus|int, default float
65
+ * - `netjack2.kbps`: the number of kilobits per second when encoding, default 64
66
+ * - `audio.channels`: the number of audio ports. Can also be added to the stream props.
67
+ * - `midi.ports`: the number of midi ports. Can also be added to the stream props.
68
+ * - `source.props`: Extra properties for the source filter.
69
+ * - `sink.props`: Extra properties for the sink filter.
70
+ *
71
+ * ## General options
72
+ *
73
+ * Options with well-known behavior.
74
+ *
75
+ * - \ref PW_KEY_REMOTE_NAME
76
+ * - \ref PW_KEY_AUDIO_CHANNELS
77
+ * - \ref SPA_KEY_AUDIO_POSITION
78
+ * - \ref PW_KEY_NODE_NAME
79
+ * - \ref PW_KEY_NODE_DESCRIPTION
80
+ * - \ref PW_KEY_NODE_GROUP
81
+ * - \ref PW_KEY_NODE_VIRTUAL
82
+ * - \ref PW_KEY_MEDIA_CLASS
83
+ * - \ref PW_KEY_TARGET_OBJECT to specify the remote node.name or serial.id to link to
84
+ *
85
+ * ## Example configuration of a duplex sink/source
86
+ *
87
+ *\code{.unparsed}
88
+ * context.modules = 
89
+ * {   name = libpipewire-module-netjack2-manager
90
+ *     args = {
91
+ *         #netjack2.connect     = true
92
+ *         #netjack2.sample-rate = 48000
93
+ *         #netjack2.period-size = 1024
94
+ *         #netjack2.encoding    = float # float|opus
95
+ *         #netjack2.kbps        = 64
96
+ *         #midi.ports           = 0
97
+ *         #audio.channels       = 2
98
+ *         #audio.position       =  FL FR 
99
+ *         source.props = {
100
+ *             # extra sink properties
101
+ *         }
102
+ *         sink.props = {
103
+ *             # extra sink properties
104
+ *         }
105
+ *     }
106
+ * }
107
+ * 
108
+ *\endcode
109
+ */
110
+
111
+#define NAME "netjack2-manager"
112
+
113
+PW_LOG_TOPIC_STATIC(mod_topic, "mod." NAME);
114
+#define PW_LOG_TOPIC_DEFAULT mod_topic
115
+
116
+#define MAX_PORTS  128
117
+
118
+#define DEFAULT_NET_IP     "225.3.19.154"
119
+#define DEFAULT_NET_PORT   19000
120
+#define DEFAULT_NET_TTL        1
121
+#define DEFAULT_NET_MTU        1500
122
+#define DEFAULT_NET_LOOP   false
123
+#define DEFAULT_NET_DSCP   34 /* Default to AES-67 AF41 (34) */
124
+#define MAX_MTU            9000
125
+
126
+
127
+#define NETWORK_MAX_LATENCY    30
128
+
129
+#define DEFAULT_SAMPLE_RATE    48000
130
+#define DEFAULT_PERIOD_SIZE    1024
131
+#define DEFAULT_ENCODING   "float"
132
+#define DEFAULT_KBPS       64
133
+#define DEFAULT_CHANNELS   2
134
+#define DEFAULT_POSITION   " FL FR "
135
+#define DEFAULT_MIDI_PORTS 1
136
+
137
+#define MODULE_USAGE   "( remote.name=<remote> ) "             \
138
+           "( local.ifname=<interface name> ) "            \
139
+           "( net.ip=<ip address to use, default 225.3.19.154> ) " \
140
+           "( net.port=<port to use, default 19000> ) "        \
141
+           "( net.mtu=<MTU to use, default 1500> ) "       \
142
+           "( net.ttl=<TTL to use, default 1> ) "          \
143
+           "( net.loop=<loopback, default false> ) "       \
144
+           "( netjack2.connect=<bool, autoconnect ports> ) "   \
145
+           "( netjack2.sample-rate=<sampl erate, default 48000> ) "\
146
+           "( netjack2.period-size=<period size, default 1024> ) " \
147
+           "( midi.ports=<number of midi ports> ) "        \
148
+           "( audio.channels=<number of channels> ) "      \
149
+           "( audio.position=<channel map> ) "         \
150
+           "( source.props=<properties> ) "            \
151
+           "( sink.props=<properties> ) "
152
+
153
+
154
+static const struct spa_dict_item module_props = {
155
+   { PW_KEY_MODULE_AUTHOR, "Wim Taymans <wim.taymans@gmail.com>" },
156
+   { PW_KEY_MODULE_DESCRIPTION, "Create a netjack2 manager" },
157
+   { PW_KEY_MODULE_USAGE, MODULE_USAGE },
158
+   { PW_KEY_MODULE_VERSION, PACKAGE_VERSION },
159
+};
160
+
161
+static void parse_audio_info(const struct pw_properties *props, struct spa_audio_info_raw *info);
162
+
163
+struct port {
164
+   enum spa_direction direction;
165
+   struct spa_latency_info latency2;
166
+   bool latency_changed2;
167
+   unsigned int is_midi:1;
168
+};
169
+
170
+struct stream {
171
+   struct impl *impl;
172
+   struct follower *follower;
173
+
174
+   enum spa_direction direction;
175
+   struct pw_properties *props;
176
+   struct pw_filter *filter;
177
+   struct spa_hook listener;
178
+
179
+   struct spa_audio_info_raw info;
180
+
181
+   uint32_t n_midi;
182
+   uint32_t n_ports;
183
+   struct port *portsMAX_PORTS;
184
+
185
+   struct volume volume;
186
+
187
+   uint32_t active_audio_ports;
188
+   uint32_t active_midi_ports;
189
+
190
+   unsigned int running:1;
191
+   unsigned int ready:1;
192
+};
193
+
194
+struct follower {
195
+   struct spa_list link;
196
+   struct impl *impl;
197
+
198
+   struct spa_io_position *position;
199
+
200
+   struct stream source;
201
pipewire-0.3.72.tar.gz/src/modules/module-netjack2/packets.h Added
196
 
1
@@ -0,0 +1,194 @@
2
+/* PipeWire */
3
+/* SPDX-FileCopyrightText: Copyright © 2023 Wim Taymans <wim.taymans@gmail.com> */
4
+/* SPDX-License-Identifier: MIT */
5
+
6
+#ifndef PIPEWIRE_NETJACK2_H
7
+#define PIPEWIRE_NETJACK2_H
8
+
9
+#ifdef __cplusplus
10
+extern "C" {
11
+#endif
12
+
13
+#define JACK_CLIENT_NAME_SIZE  64
14
+#define JACK_SERVER_NAME_SIZE  256
15
+
16
+struct nj2_session_params {
17
+   char type8;                 /* packet type ('param') */
18
+#define NJ2_NETWORK_PROTOCOL   8
19
+   uint32_t version;               /* version */
20
+#define NJ2_ID_FOLLOWER_AVAILABLE  0       /* a follower is available */
21
+#define NJ2_ID_FOLLOWER_SETUP      1       /* follower configuration */
22
+#define NJ2_ID_START_DRIVER        2       /* follower is ready, start driver */
23
+#define NJ2_ID_START_FOLLOWER      3       /* driver is ready, activate follower */
24
+#define NJ2_ID_STOP_DRIVER     4       /* driver must stop */
25
+   int32_t packet_id;              /* indicates the packet type */
26
+   char nameJACK_CLIENT_NAME_SIZE;     /* follower's name */
27
+   char driver_nameJACK_SERVER_NAME_SIZE;  /* driver hostname (network) */
28
+   char follower_nameJACK_SERVER_NAME_SIZE;    /* follower hostname (network) */
29
+   uint32_t mtu;                   /* connection mtu */
30
+   uint32_t id;                    /* follower's ID */
31
+   uint32_t transport_sync;            /* is the transport synced ? */
32
+   int32_t send_audio_channels;            /* number of driver->follower channels */
33
+   int32_t recv_audio_channels;            /* number of follower->driver channels */
34
+   int32_t send_midi_channels;         /* number of driver->follower midi channels */
35
+   int32_t recv_midi_channels;         /* number of follower->driver midi channels */
36
+   uint32_t sample_rate;               /* session sample rate */
37
+   uint32_t period_size;               /* period size */
38
+#define NJ2_ENCODER_FLOAT  0
39
+#define NJ2_ENCODER_INT        1
40
+#define NJ2_ENCODER_CELT   2
41
+#define NJ2_ENCODER_OPUS   3
42
+   uint32_t sample_encoder;            /* samples encoder */
43
+   uint32_t kbps;                  /* KB per second for CELT encoder */
44
+   uint32_t follower_sync_mode;            /* is the follower in sync mode ? */
45
+   uint32_t network_latency;           /* network latency */
46
+} __attribute__ ((packed));
47
+
48
+static inline void nj2_dump_session_params(struct nj2_session_params *params)
49
+{
50
+   pw_log_info("Type:          '%s'", params->type);
51
+   pw_log_info("Version:       %u", ntohl(params->version));
52
+   pw_log_info("packet ID:     %d", ntohl(params->packet_id));
53
+   pw_log_info("Name:          '%s'", params->name);
54
+   pw_log_info("Driver Name:   '%s'", params->driver_name);
55
+   pw_log_info("Follower Name: '%s'", params->follower_name);
56
+   pw_log_info("MTU:           %u", ntohl(params->mtu));
57
+   pw_log_info("ID:            %u", ntohl(params->id));
58
+   pw_log_info("TransportSync: %u", ntohl(params->transport_sync));
59
+   pw_log_info("Audio Send:    %d", ntohl(params->send_audio_channels));
60
+   pw_log_info("Audio Recv:    %d", ntohl(params->recv_audio_channels));
61
+   pw_log_info("MIDI Send:     %d", ntohl(params->send_midi_channels));
62
+   pw_log_info("MIDI Recv:     %d", ntohl(params->recv_midi_channels));
63
+   pw_log_info("Sample Rate:   %u", ntohl(params->sample_rate));
64
+   pw_log_info("Period Size:   %u", ntohl(params->period_size));
65
+   pw_log_info("Encoder:       %u", ntohl(params->sample_encoder));
66
+   pw_log_info("KBps:          %u", ntohl(params->kbps));
67
+   pw_log_info("Follower Sync: %u", ntohl(params->follower_sync_mode));
68
+   pw_log_info("Latency:       %u", ntohl(params->network_latency));
69
+}
70
+
71
+static inline void nj2_session_params_ntoh(struct nj2_session_params *host,
72
+       const struct nj2_session_params *net)
73
+{
74
+   memcpy(host, net, sizeof(*host));
75
+   host->version = ntohl(net->version);
76
+   host->packet_id = ntohl(net->packet_id);
77
+   host->mtu = ntohl(net->mtu);
78
+   host->id = ntohl(net->id);
79
+   host->transport_sync = ntohl(net->transport_sync);
80
+   host->send_audio_channels = ntohl(net->send_audio_channels);
81
+   host->recv_audio_channels = ntohl(net->recv_audio_channels);
82
+   host->send_midi_channels = ntohl(net->send_midi_channels);
83
+   host->recv_midi_channels = ntohl(net->recv_midi_channels);
84
+   host->sample_rate = ntohl(net->sample_rate);
85
+   host->period_size = ntohl(net->period_size);
86
+   host->sample_encoder = ntohl(net->sample_encoder);
87
+   host->kbps = ntohl(net->kbps);
88
+   host->follower_sync_mode = ntohl(net->follower_sync_mode);
89
+   host->network_latency = ntohl(net->network_latency);
90
+}
91
+
92
+static inline void nj2_session_params_hton(struct nj2_session_params *net,
93
+       const struct nj2_session_params *host)
94
+{
95
+   memcpy(net, host, sizeof(*net));
96
+   net->version = htonl(host->version);
97
+   net->packet_id = htonl(host->packet_id);
98
+   net->mtu = htonl(host->mtu);
99
+   net->id = htonl(host->id);
100
+   net->transport_sync = htonl(host->transport_sync);
101
+   net->send_audio_channels = htonl(host->send_audio_channels);
102
+   net->recv_audio_channels = htonl(host->recv_audio_channels);
103
+   net->send_midi_channels = htonl(host->send_midi_channels);
104
+   net->recv_midi_channels = htonl(host->recv_midi_channels);
105
+   net->sample_rate = htonl(host->sample_rate);
106
+   net->period_size = htonl(host->period_size);
107
+   net->sample_encoder = htonl(host->sample_encoder);
108
+   net->kbps = htonl(host->kbps);
109
+   net->follower_sync_mode = htonl(host->follower_sync_mode);
110
+   net->network_latency = htonl(host->network_latency);
111
+}
112
+
113
+struct nj2_packet_header {
114
+   char type8;         /* packet type ('headr') */
115
+   uint32_t data_type;     /* 'a' for audio, 'm' for midi and 's' for sync */
116
+   uint32_t data_stream;       /* 's' for send, 'r' for return */
117
+   uint32_t id;            /* unique ID of the follower */
118
+   uint32_t num_packets;       /* number of data packets of the cycle */
119
+   uint32_t packet_size;       /* packet size in bytes */
120
+   uint32_t active_ports;      /* number of active ports */
121
+   uint32_t cycle;         /* process cycle counter */
122
+   uint32_t sub_cycle;     /* midi/audio subcycle counter */
123
+   int32_t frames;         /* process cycle size in frames (can be -1 to indicate entire buffer) */
124
+   uint32_t is_last;       /* is it the last packet of a given cycle ('y' or 'n') */
125
+} __attribute__ ((packed));
126
+
127
+#define UDP_HEADER_SIZE 64     /* 40 bytes for IP header in IPV6, 20 in IPV4, 8 for UDP, so take 64 */
128
+
129
+#define PACKET_AVAILABLE_SIZE(mtu) (mtu - UDP_HEADER_SIZE - sizeof(struct nj2_packet_header))
130
+
131
+static inline void nj2_dump_packet_header(struct nj2_packet_header *header)
132
+{
133
+   pw_log_info("Type:         %s", header->type);
134
+   pw_log_info("Data Type:    %c", ntohl(header->data_type));
135
+   pw_log_info("Data Stream:  %c", ntohl(header->data_stream));
136
+   pw_log_info("ID:           %u", ntohl(header->id));
137
+   pw_log_info("Num Packets:  %u", ntohl(header->num_packets));
138
+   pw_log_info("Packet Size:  %u", ntohl(header->packet_size));
139
+   pw_log_info("Active Ports: %u", ntohl(header->active_ports));
140
+   pw_log_info("Cycle:        %u", ntohl(header->cycle));
141
+   pw_log_info("Sub Cycle:    %u", ntohl(header->sub_cycle));
142
+   pw_log_info("Frames        %d", ntohl(header->frames));
143
+   pw_log_info("Is Last:      %u", ntohl(header->is_last));
144
+}
145
+
146
+#define MIDI_INLINE_MAX 4
147
+
148
+struct nj2_midi_event {
149
+   uint32_t time;      /**< Sample index at which event is valid */
150
+   uint32_t size;      /**< Number of bytes of data in the event */
151
+   union {
152
+       uint32_t offset;            /**< offset in buffer */
153
+       uint8_t bufferMIDI_INLINE_MAX;  /**< Raw inline data */
154
+   };
155
+};
156
+
157
+struct nj2_midi_buffer {
158
+#define MIDI_BUFFER_MAGIC 0x900df00d
159
+   uint32_t magic;
160
+   uint32_t buffer_size;
161
+   uint32_t nframes;
162
+   uint32_t write_pos;
163
+   uint32_t event_count;
164
+   uint32_t lost_events;
165
+
166
+   struct nj2_midi_event event1;
167
+};
168
+
169
+static inline void nj2_midi_buffer_hton(struct nj2_midi_buffer *net,
170
+       const struct nj2_midi_buffer *host)
171
+{
172
+   net->magic = htonl(host->magic);
173
+   net->buffer_size = htonl(host->buffer_size);
174
+   net->nframes = htonl(host->nframes);
175
+   net->write_pos = htonl(host->write_pos);
176
+   net->event_count = htonl(host->event_count);
177
+   net->lost_events = htonl(host->lost_events);
178
+}
179
+
180
+static inline void nj2_midi_buffer_ntoh(struct nj2_midi_buffer *host,
181
+       const struct nj2_midi_buffer *net)
182
+{
183
+   host->magic = ntohl(net->magic);
184
+   host->buffer_size = ntohl(net->buffer_size);
185
+   host->nframes = ntohl(net->nframes);
186
+   host->write_pos = ntohl(net->write_pos);
187
+   host->event_count = ntohl(net->event_count);
188
+   host->lost_events = ntohl(net->lost_events);
189
+}
190
+
191
+#ifdef __cplusplus
192
+}
193
+#endif
194
+
195
+#endif /* PIPEWIRE_NETJACK2_H */
196
pipewire-0.3.72.tar.gz/src/modules/module-netjack2/peer.c Added
201
 
1
@@ -0,0 +1,1031 @@
2
+
3
+#include <byteswap.h>
4
+
5
+#ifdef HAVE_OPUS_CUSTOM
6
+#include <opus/opus.h>
7
+#include <opus/opus_custom.h>
8
+#endif
9
+
10
+#define MAX_BUFFER_FRAMES  8192
11
+
12
+struct volume {
13
+   bool mute;
14
+   uint32_t n_volumes;
15
+   float volumesSPA_AUDIO_MAX_CHANNELS;
16
+};
17
+
18
+static inline float bswap_f32(float f)
19
+{
20
+   union {
21
+       float f;
22
+       uint32_t u;
23
+   } v;
24
+   v.f = f;
25
+   v.u = bswap_32(v.u);
26
+   return v.f;
27
+}
28
+
29
+static inline void do_volume(float *dst, const float *src, struct volume *vol,
30
+       uint32_t ch, uint32_t n_samples, bool recv)
31
+{
32
+   float v = vol->mute ? 0.0f : vol->volumesch;
33
+   uint32_t i;
34
+
35
+   if (v == 0.0f || src == NULL)
36
+       memset(dst, 0, n_samples * sizeof(float));
37
+   else if (v == 1.0f) {
38
+#if __BYTE_ORDER == __BIG_ENDIAN
39
+       for (i = 0; i < n_samples; i++)
40
+           dsti = bswap_f32(srci);
41
+#else
42
+       memcpy(dst, src, n_samples * sizeof(float));
43
+#endif
44
+
45
+   } else {
46
+#if __BYTE_ORDER == __BIG_ENDIAN
47
+       if (recv) {
48
+           for (i = 0; i < n_samples; i++)
49
+               dsti = bswap_f32(srci) * v;
50
+       } else {
51
+           for (i = 0; i < n_samples; i++)
52
+               dsti = bswap_f32(srci * v);
53
+       }
54
+#else
55
+       for (i = 0; i < n_samples; i++)
56
+           dsti = srci * v;
57
+#endif
58
+   }
59
+}
60
+
61
+#define ITOF(type,v,scale) \
62
+   (((type)(v)) * (1.0f / (scale)))
63
+#define FTOI(type,v,scale,min,max) \
64
+   (type)(SPA_CLAMPF((v) * (scale), min, max))
65
+
66
+#define S16_MIN            -32768
67
+#define S16_MAX            32767
68
+#define S16_SCALE      32768.0f
69
+#define S16_TO_F32(v)      ITOF(int16_t, v, S16_SCALE)
70
+#define F32_TO_S16(v)      FTOI(int16_t, v, S16_SCALE, S16_MIN, S16_MAX)
71
+
72
+static inline void do_volume_to_s16(int16_t *dst, const float *src, struct volume *vol,
73
+       uint32_t ch, uint32_t n_samples)
74
+{
75
+   float v = vol->mute ? 0.0f : vol->volumesch;
76
+   uint32_t i;
77
+
78
+   if (v == 0.0f || src == NULL)
79
+       memset(dst, 0, n_samples * sizeof(int16_t));
80
+   else if (v == 1.0f) {
81
+       for (i = 0; i < n_samples; i++)
82
+           dsti = F32_TO_S16(srci);
83
+   } else {
84
+       for (i = 0; i < n_samples; i++)
85
+           dsti = F32_TO_S16(srci * v);
86
+   }
87
+}
88
+
89
+static inline void do_volume_from_s16(float *dst, const int16_t *src, struct volume *vol,
90
+       uint32_t ch, uint32_t n_samples)
91
+{
92
+   float v = vol->mute ? 0.0f : vol->volumesch;
93
+   uint32_t i;
94
+
95
+   if (v == 0.0f || src == NULL)
96
+       memset(dst, 0, n_samples * sizeof(float));
97
+   else if (v == 1.0f) {
98
+       for (i = 0; i < n_samples; i++)
99
+           dsti = S16_TO_F32(srci);
100
+   } else {
101
+       for (i = 0; i < n_samples; i++)
102
+           dsti = S16_TO_F32(srci) * v;
103
+   }
104
+}
105
+
106
+struct netjack2_peer {
107
+   int fd;
108
+
109
+   uint32_t our_stream;
110
+   uint32_t other_stream;
111
+   struct nj2_session_params params;
112
+   struct nj2_packet_header sync;
113
+   uint32_t cycle;
114
+
115
+   struct volume *send_volume;
116
+   struct volume *recv_volume;
117
+
118
+   void *midi_data;
119
+   uint32_t midi_size;
120
+
121
+   float *empty;
122
+   void *encoded_data;
123
+   uint32_t encoded_size;
124
+   uint32_t max_encoded_size;
125
+#ifdef HAVE_OPUS_CUSTOM
126
+   OpusCustomMode *opus_config;
127
+   OpusCustomEncoder **opus_enc;
128
+   OpusCustomDecoder **opus_dec;
129
+#endif
130
+
131
+   unsigned fix_midi:1;
132
+};
133
+
134
+static int netjack2_init(struct netjack2_peer *peer)
135
+{
136
+   int res = 0;
137
+
138
+   peer->empty = calloc(MAX_BUFFER_FRAMES, sizeof(float));
139
+
140
+   peer->midi_size = peer->params.period_size * sizeof(float) *
141
+       SPA_MAX(peer->params.send_midi_channels, peer->params.recv_midi_channels);
142
+   peer->midi_data = calloc(1, peer->midi_size);
143
+
144
+   if (peer->params.sample_encoder == NJ2_ENCODER_INT) {
145
+       peer->max_encoded_size = peer->params.period_size * sizeof(int16_t);
146
+       peer->encoded_size = peer->max_encoded_size *
147
+           SPA_MAX(peer->params.send_audio_channels, peer->params.recv_audio_channels);
148
+       if ((peer->encoded_data = calloc(1, peer->encoded_size)) == NULL)
149
+           goto error_errno;
150
+   } else if (peer->params.sample_encoder == NJ2_ENCODER_OPUS) {
151
+#ifdef HAVE_OPUS_CUSTOM
152
+       int32_t i;
153
+       peer->max_encoded_size = (peer->params.kbps * peer->params.period_size * 1024) /
154
+           (peer->params.sample_rate * 8) + sizeof(uint16_t);
155
+       peer->encoded_size = peer->max_encoded_size *
156
+           SPA_MAX(peer->params.send_audio_channels, peer->params.recv_audio_channels);
157
+       if ((peer->encoded_data = calloc(1, peer->encoded_size)) == NULL)
158
+           goto error_errno;
159
+       if ((peer->opus_config = opus_custom_mode_create(peer->params.sample_rate,
160
+               peer->params.period_size, &res)) == NULL)
161
+           goto error_opus;
162
+       if ((peer->opus_enc = calloc(peer->params.send_audio_channels,
163
+                       sizeof(OpusCustomEncoder*))) == NULL)
164
+           goto error_errno;
165
+
166
+       for (i = 0; i < peer->params.send_audio_channels; i++) {
167
+           if ((peer->opus_enci = opus_custom_encoder_create(peer->opus_config,
168
+                   1, &res)) == NULL)
169
+               goto error_opus;
170
+           opus_custom_encoder_ctl(peer->opus_enci,
171
+               OPUS_SET_BITRATE(peer->params.kbps*1024)); // bits per second
172
+           opus_custom_encoder_ctl(peer->opus_enci,
173
+               OPUS_SET_COMPLEXITY(10));
174
+           opus_custom_encoder_ctl(peer->opus_enci,
175
+               OPUS_SET_SIGNAL(OPUS_SIGNAL_MUSIC));
176
+           opus_custom_encoder_ctl(peer->opus_enci,
177
+               OPUS_SET_SIGNAL(OPUS_APPLICATION_RESTRICTED_LOWDELAY));
178
+       }
179
+       if ((peer->opus_dec = calloc(peer->params.recv_audio_channels,
180
+               sizeof(OpusCustomDecoder*))) == NULL)
181
+           goto error_errno;
182
+       for (i = 0; i < peer->params.recv_audio_channels; i++) {
183
+           if ((peer->opus_deci = opus_custom_decoder_create(peer->opus_config,
184
+                   1, &res)) == NULL)
185
+               goto error_opus;
186
+       }
187
+#else
188
+       return -ENOTSUP;
189
+#endif
190
+
191
+   }
192
+   return res;
193
+error_errno:
194
+   pw_log_warn("error: %m");
195
+   return -errno;
196
+#ifdef HAVE_OPUS_CUSTOM
197
+error_opus:
198
+   pw_log_warn("error: %d", res);
199
+   return -EINVAL;
200
+#endif
201
pipewire-0.3.71.tar.gz/src/modules/module-profiler.c -> pipewire-0.3.72.tar.gz/src/modules/module-profiler.c Changed
126
 
1
@@ -168,7 +168,8 @@
2
    struct impl *impl = data;
3
    struct spa_pod_builder b;
4
    struct spa_pod_frame f2;
5
-   struct pw_node_activation *a = node->rt.activation;
6
+   uint32_t id = node->info.id;
7
+   struct pw_node_activation *a = node->rt.target.activation;
8
    struct spa_io_position *pos = &a->position;
9
    struct pw_node_target *t;
10
    int32_t filled;
11
@@ -205,42 +206,48 @@
12
 
13
    spa_pod_builder_prop(&b, SPA_PROFILER_driverBlock, 0);
14
    spa_pod_builder_add_struct(&b,
15
-           SPA_POD_Int(node->info.id),
16
+           SPA_POD_Int(id),
17
            SPA_POD_String(node->name),
18
            SPA_POD_Long(a->prev_signal_time),
19
            SPA_POD_Long(a->signal_time),
20
            SPA_POD_Long(a->awake_time),
21
            SPA_POD_Long(a->finish_time),
22
            SPA_POD_Int(a->status),
23
-           SPA_POD_Fraction(&node->latency));
24
+           SPA_POD_Fraction(&node->latency),
25
+           SPA_POD_Int(a->xrun_count));
26
 
27
    spa_list_for_each(t, &node->rt.target_list, link) {
28
        struct pw_impl_node *n = t->node;
29
        struct pw_node_activation *na;
30
        struct spa_fraction latency;
31
 
32
-       if (n == NULL || n == node)
33
+       if (t->id == id || t->flags & PW_NODE_TARGET_PEER)
34
            continue;
35
 
36
-       latency = n->latency;
37
-       if (n->force_quantum != 0)
38
-           latency.num = n->force_quantum;
39
-       if (n->force_rate != 0)
40
-           latency.denom = n->force_rate;
41
-       else if (n->rate.denom != 0)
42
-           latency.denom = n->rate.denom;
43
-
44
-       na = n->rt.activation;
45
+       if (n != NULL) {
46
+           latency = n->latency;
47
+           if (n->force_quantum != 0)
48
+               latency.num = n->force_quantum;
49
+           if (n->force_rate != 0)
50
+               latency.denom = n->force_rate;
51
+           else if (n->rate.denom != 0)
52
+               latency.denom = n->rate.denom;
53
+       } else {
54
+           spa_zero(latency);
55
+       }
56
+
57
+       na = t->activation;
58
        spa_pod_builder_prop(&b, SPA_PROFILER_followerBlock, 0);
59
        spa_pod_builder_add_struct(&b,
60
-           SPA_POD_Int(n->info.id),
61
-           SPA_POD_String(n->name),
62
+           SPA_POD_Int(t->id),
63
+           SPA_POD_String(t->name),
64
            SPA_POD_Long(a->signal_time),
65
            SPA_POD_Long(na->signal_time),
66
            SPA_POD_Long(na->awake_time),
67
            SPA_POD_Long(na->finish_time),
68
            SPA_POD_Int(na->status),
69
-           SPA_POD_Fraction(&latency));
70
+           SPA_POD_Fraction(&latency),
71
+           SPA_POD_Int(na->xrun_count));
72
    }
73
    spa_pod_builder_pop(&b, &f0);
74
 
75
@@ -275,19 +282,11 @@
76
    .complete = context_do_profile,
77
 };
78
 
79
-static int do_stop(struct spa_loop *loop,
80
-       bool async, uint32_t seq, const void *data, size_t size, void *user_data)
81
-{
82
-   struct impl *impl = user_data;
83
-   spa_hook_remove(&impl->context_listener);
84
-   return 0;
85
-}
86
-
87
 static void stop_listener(struct impl *impl)
88
 {
89
    if (impl->listening) {
90
-       pw_loop_invoke(impl->data_loop,
91
-                       do_stop, SPA_ID_INVALID, NULL, 0, true, impl);
92
+       pw_context_driver_remove_listener(impl->context,
93
+           &impl->context_listener);
94
        impl->listening = false;
95
    }
96
 }
97
@@ -307,16 +306,6 @@
98
 };
99
 
100
 static int
101
-do_start(struct spa_loop *loop,
102
-       bool async, uint32_t seq, const void *data, size_t size, void *user_data)
103
-{
104
-   struct impl *impl = user_data;
105
-   spa_hook_list_append(&impl->context->driver_listener_list,
106
-           &impl->context_listener,
107
-           &context_events, impl);
108
-   return 0;
109
-}
110
-static int
111
 global_bind(void *object, struct pw_impl_client *client, uint32_t permissions,
112
             uint32_t version, uint32_t id)
113
 {
114
@@ -340,8 +329,9 @@
115
 
116
    if (++impl->busy == 1) {
117
        pw_log_info("%p: starting profiler", impl);
118
-       pw_loop_invoke(impl->data_loop,
119
-                       do_start, SPA_ID_INVALID, NULL, 0, false, impl);
120
+       pw_context_driver_add_listener(impl->context,
121
+           &impl->context_listener,
122
+           &context_events, impl);
123
        impl->listening = true;
124
    }
125
    return 0;
126
pipewire-0.3.71.tar.gz/src/modules/module-protocol-native.c -> pipewire-0.3.72.tar.gz/src/modules/module-protocol-native.c Changed
21
 
1
@@ -919,12 +919,18 @@
2
 
3
        proxy = pw_core_find_proxy(this, msg->id);
4
        if (proxy == NULL || proxy->zombie) {
5
+           uint32_t i;
6
+
7
            if (proxy == NULL)
8
                pw_log_error("%p: could not find proxy %u", this, msg->id);
9
            else
10
                pw_log_debug("%p: zombie proxy %u", this, msg->id);
11
 
12
-           /* FIXME close fds */
13
+           /* close fds */
14
+           for (i = 0; i < msg->n_fds; i++) {
15
+               pw_log_debug("%p: close fd:%d", conn, msg->fdsi);
16
+               close(msg->fdsi);
17
+           }
18
            continue;
19
        }
20
 
21
pipewire-0.3.71.tar.gz/src/modules/module-protocol-native/connection.c -> pipewire-0.3.72.tar.gz/src/modules/module-protocol-native/connection.c Changed
44
 
1
@@ -205,7 +205,7 @@
2
        char cmsgbufCMSG_SPACE(MAX_FDS_MSG * sizeof(int));
3
        struct cmsghdr align;
4
    } cmsgbuf;
5
-   int n_fds = 0;
6
+   int i, n_fds = 0, *fds;
7
    size_t avail;
8
 
9
    avail = buf->buffer_maxsize - buf->buffer_size;
10
@@ -242,10 +242,13 @@
11
            continue;
12
 
13
        n_fds = cmsg_data_length(cmsg) / sizeof(int);
14
+       fds = (int*)CMSG_DATA(cmsg);
15
        if (n_fds + buf->n_fds > MAX_FDS)
16
            goto too_many_fds;
17
-       memcpy(&buf->fdsbuf->n_fds, CMSG_DATA(cmsg), n_fds * sizeof(int));
18
-       buf->n_fds += n_fds;
19
+       for (i = 0; i < n_fds; i++) {
20
+           pw_log_debug("connection %p: buffer:%p got fd:%d", conn, buf, fdsi);
21
+           buf->fdsbuf->n_fds++ = fdsi;
22
+       }
23
    }
24
    pw_log_trace("connection %p: %d read %zd bytes and %d fds", conn, conn->fd, len,
25
             n_fds);
26
@@ -272,7 +275,7 @@
27
 {
28
    uint32_t i;
29
 
30
-   pw_log_debug("clear fds:%d", fds);
31
+   pw_log_debug("%p clear fds:%d n_fds:%d", buf, fds, buf->n_fds);
32
    if (fds) {
33
        for (i = 0; i < buf->n_fds; i++) {
34
            pw_log_debug("%p: close fd:%d", buf, buf->fdsi);
35
@@ -848,6 +851,8 @@
36
 {
37
    struct impl *impl = SPA_CONTAINER_OF(conn, struct impl, this);
38
 
39
+   pw_log_debug("%p: clear", conn);
40
+
41
    clear_buffer(&impl->out, true);
42
    clear_buffer(&impl->in, true);
43
 
44
pipewire-0.3.71.tar.gz/src/modules/module-protocol-pulse/modules/module-gsettings.c -> pipewire-0.3.72.tar.gz/src/modules/module-protocol-pulse/modules/module-gsettings.c Changed
85
 
1
@@ -137,6 +137,27 @@
2
    return 0;
3
 }
4
 
5
+static bool schema_exists(const char *schema_id)
6
+{
7
+   GSettingsSchemaSource *source;
8
+   GSettingsSchema *schema;
9
+
10
+   source = g_settings_schema_source_get_default();
11
+   if (!source) {
12
+       pw_log_error("gsettings schema source not found");
13
+       return false;
14
+   }
15
+
16
+   schema = g_settings_schema_source_lookup(source, schema_id, TRUE);
17
+   if (!schema) {
18
+       pw_log_error("required gsettings schema %s does not exist", schema_id);
19
+       return false;
20
+   }
21
+
22
+   g_settings_schema_unref(schema);
23
+   return true;
24
+}
25
+
26
 static void handle_module_group(struct module_gsettings_data *d, gchar *name)
27
 {
28
    struct impl *impl = d->module->impl;
29
@@ -147,6 +168,9 @@
30
 
31
    snprintf(p, sizeof(p), PA_GSETTINGS_MODULE_GROUPS_PATH"%s/", name);
32
 
33
+   if (!schema_exists(PA_GSETTINGS_MODULE_GROUP_SCHEMA))
34
+       return;
35
+
36
    settings = g_settings_new_with_path(PA_GSETTINGS_MODULE_GROUP_SCHEMA, p);
37
    if (settings == NULL)
38
        return;
39
@@ -198,12 +222,20 @@
40
    struct module_gsettings_data *data = module->user_data;
41
    gchar **name;
42
 
43
+   /* Check the required schema files are installed. If not, Glib will
44
+    * abort in g_settings_new */
45
+   if (!schema_exists(PA_GSETTINGS_MODULE_GROUPS_SCHEMA) ||
46
+           !schema_exists(PA_GSETTINGS_MODULE_GROUP_SCHEMA))
47
+       return -EIO;
48
+
49
    data->context = g_main_context_new();
50
    g_main_context_push_thread_default(data->context);
51
 
52
    data->settings = g_settings_new(PA_GSETTINGS_MODULE_GROUPS_SCHEMA);
53
-   if (data->settings == NULL)
54
+   if (data->settings == NULL) {
55
+       g_main_context_pop_thread_default(data->context);
56
        return -EIO;
57
+   }
58
 
59
    data->group_names = g_settings_list_children(data->settings);
60
 
61
@@ -238,15 +270,19 @@
62
    struct module_gsettings_data *d = module->user_data;
63
    struct group *g;
64
 
65
-   g_main_context_invoke(d->context, do_stop, d);
66
-   pw_thread_utils_join(d->thr, NULL);
67
-   g_main_context_unref(d->context);
68
+   if (d->context) {
69
+       g_main_context_invoke(d->context, do_stop, d);
70
+       if (d->thr)
71
+           pw_thread_utils_join(d->thr, NULL);
72
+       g_main_context_unref(d->context);
73
+   }
74
 
75
    spa_list_consume(g, &d->groups, link)
76
        unload_module(d, g);
77
 
78
    g_strfreev(d->group_names);
79
-   g_object_unref(G_OBJECT(d->settings));
80
+   if (d->settings)
81
+       g_object_unref(G_OBJECT(d->settings));
82
    return 0;
83
 }
84
 
85
pipewire-0.3.72.tar.gz/src/modules/module-protocol-pulse/modules/module-virtual-sink.c Added
181
 
1
@@ -0,0 +1,179 @@
2
+/* PipeWire */
3
+/* SPDX-FileCopyrightText: Copyright © 2023 Wim Taymans <wim.taymans@gmail.com> */
4
+/* SPDX-License-Identifier: MIT */
5
+
6
+#include <spa/param/audio/format-utils.h>
7
+#include <spa/utils/hook.h>
8
+#include <spa/utils/json.h>
9
+#include <pipewire/pipewire.h>
10
+
11
+#include "../defs.h"
12
+#include "../module.h"
13
+
14
+#define NAME "virtual-sink"
15
+
16
+PW_LOG_TOPIC_STATIC(mod_topic, "mod." NAME);
17
+#define PW_LOG_TOPIC_DEFAULT mod_topic
18
+
19
+struct module_virtual_sink_data {
20
+   struct module *module;
21
+
22
+   struct pw_impl_module *mod;
23
+   struct spa_hook mod_listener;
24
+
25
+   struct pw_properties *global_props;
26
+   struct pw_properties *capture_props;
27
+   struct pw_properties *playback_props;
28
+};
29
+
30
+static void module_destroy(void *data)
31
+{
32
+   struct module_virtual_sink_data *d = data;
33
+   spa_hook_remove(&d->mod_listener);
34
+   d->mod = NULL;
35
+   module_schedule_unload(d->module);
36
+}
37
+
38
+static const struct pw_impl_module_events module_events = {
39
+   PW_VERSION_IMPL_MODULE_EVENTS,
40
+   .destroy = module_destroy
41
+};
42
+
43
+static int module_virtual_sink_load(struct module *module)
44
+{
45
+   struct module_virtual_sink_data *data = module->user_data;
46
+   FILE *f;
47
+   char *args;
48
+   size_t size;
49
+
50
+   pw_properties_setf(data->capture_props, PW_KEY_NODE_GROUP, "virtual-sink-%u", module->index);
51
+   pw_properties_setf(data->playback_props, PW_KEY_NODE_GROUP, "virtual-sink-%u", module->index);
52
+   pw_properties_setf(data->capture_props, "pulse.module.id", "%u", module->index);
53
+   pw_properties_setf(data->playback_props, "pulse.module.id", "%u", module->index);
54
+
55
+   if ((f = open_memstream(&args, &size)) == NULL)
56
+       return -errno;
57
+
58
+   fprintf(f, "{");
59
+   pw_properties_serialize_dict(f, &data->global_props->dict, 0);
60
+   fprintf(f, " capture.props = {");
61
+   pw_properties_serialize_dict(f, &data->capture_props->dict, 0);
62
+   fprintf(f, " } playback.props = {");
63
+   pw_properties_serialize_dict(f, &data->playback_props->dict, 0);
64
+   fprintf(f, " } }");
65
+   fclose(f);
66
+
67
+   data->mod = pw_context_load_module(module->impl->context,
68
+           "libpipewire-module-loopback",
69
+           args, NULL);
70
+   free(args);
71
+
72
+   if (data->mod == NULL)
73
+       return -errno;
74
+
75
+   pw_impl_module_add_listener(data->mod,
76
+           &data->mod_listener,
77
+           &module_events, data);
78
+
79
+   return 0;
80
+}
81
+
82
+static int module_virtual_sink_unload(struct module *module)
83
+{
84
+   struct module_virtual_sink_data *d = module->user_data;
85
+
86
+   if (d->mod) {
87
+       spa_hook_remove(&d->mod_listener);
88
+       pw_impl_module_destroy(d->mod);
89
+       d->mod = NULL;
90
+   }
91
+
92
+   pw_properties_free(d->capture_props);
93
+   pw_properties_free(d->playback_props);
94
+   pw_properties_free(d->global_props);
95
+
96
+   return 0;
97
+}
98
+
99
+static const struct spa_dict_item module_virtual_sink_info = {
100
+   { PW_KEY_MODULE_AUTHOR, "Wim Taymans <wim.taymans@gmail.com>" },
101
+   { PW_KEY_MODULE_DESCRIPTION, "Virtual sink" },
102
+   { PW_KEY_MODULE_USAGE, "sink_name=<name for the sink> "
103
+               "sink_properties=<properties for the sink> "
104
+               "master=<name of sink to filter> "
105
+               "channels=<number of channels> "
106
+               "channel_map=<channel map> "
107
+               "use_volume_sharing=<yes or no> "
108
+               "force_flat_volume=<yes or no> " },
109
+   { PW_KEY_MODULE_VERSION, PACKAGE_VERSION },
110
+};
111
+
112
+static int module_virtual_sink_prepare(struct module * const module)
113
+{
114
+   struct module_virtual_sink_data * const d = module->user_data;
115
+   struct pw_properties * const props = module->props;
116
+   struct pw_properties *global_props = NULL, *playback_props = NULL, *capture_props = NULL;
117
+   const char *str;
118
+   struct spa_audio_info_raw info = { 0 };
119
+   int res;
120
+
121
+   PW_LOG_TOPIC_INIT(mod_topic);
122
+
123
+   global_props = pw_properties_new(NULL, NULL);
124
+   capture_props = pw_properties_new(NULL, NULL);
125
+   playback_props = pw_properties_new(NULL, NULL);
126
+   if (!global_props || !capture_props || !playback_props) {
127
+       res = -EINVAL;
128
+       goto out;
129
+   }
130
+
131
+   if ((str = pw_properties_get(props, "sink_name")) != NULL) {
132
+       pw_properties_set(global_props, PW_KEY_NODE_NAME, str);
133
+       pw_properties_set(global_props, PW_KEY_NODE_DESCRIPTION, str);
134
+       pw_properties_set(props, "sink_name", NULL);
135
+   } else {
136
+       pw_properties_set(global_props, PW_KEY_NODE_NAME, "vsink");
137
+       pw_properties_set(global_props, PW_KEY_NODE_DESCRIPTION, "Virtual Sink");
138
+   }
139
+   if ((str = pw_properties_get(props, "sink_properties")) != NULL) {
140
+       module_args_add_props(capture_props, str);
141
+       pw_properties_set(props, "sink_properties", NULL);
142
+   }
143
+   pw_properties_set(playback_props, PW_KEY_NODE_PASSIVE, "true");
144
+   if (pw_properties_get(capture_props, PW_KEY_MEDIA_CLASS) == NULL)
145
+       pw_properties_set(capture_props, PW_KEY_MEDIA_CLASS, "Audio/Sink");
146
+
147
+   if ((str = pw_properties_get(props, "master")) != NULL) {
148
+       pw_properties_set(playback_props, PW_KEY_TARGET_OBJECT, str);
149
+       pw_properties_set(props, "master", NULL);
150
+   }
151
+
152
+   if (module_args_to_audioinfo_keys(module->impl, props,
153
+               NULL, NULL, "channels", "channel_map", &info) < 0) {
154
+       res = -EINVAL;
155
+       goto out;
156
+   }
157
+   audioinfo_to_properties(&info, global_props);
158
+
159
+   d->module = module;
160
+   d->global_props = global_props;
161
+   d->capture_props = capture_props;
162
+   d->playback_props = playback_props;
163
+
164
+   return 0;
165
+out:
166
+   pw_properties_free(global_props);
167
+   pw_properties_free(playback_props);
168
+   pw_properties_free(capture_props);
169
+
170
+   return res;
171
+}
172
+
173
+DEFINE_MODULE_INFO(module_virtual_sink) = {
174
+   .name = "module-virtual-sink",
175
+   .prepare = module_virtual_sink_prepare,
176
+   .load = module_virtual_sink_load,
177
+   .unload = module_virtual_sink_unload,
178
+   .properties = &SPA_DICT_INIT_ARRAY(module_virtual_sink_info),
179
+   .data_size = sizeof(struct module_virtual_sink_data),
180
+};
181
pipewire-0.3.72.tar.gz/src/modules/module-protocol-pulse/modules/module-virtual-source.c Added
190
 
1
@@ -0,0 +1,188 @@
2
+/* PipeWire */
3
+/* SPDX-FileCopyrightText: Copyright © 2021 Wim Taymans <wim.taymans@gmail.com> */
4
+/* SPDX-FileCopyrightText: Copyright © 2021 Arun Raghavan <arun@asymptotic.io> */
5
+/* SPDX-License-Identifier: MIT */
6
+
7
+#include <spa/param/audio/format-utils.h>
8
+#include <spa/utils/hook.h>
9
+#include <spa/utils/json.h>
10
+#include <pipewire/pipewire.h>
11
+
12
+#include "../defs.h"
13
+#include "../module.h"
14
+
15
+#define NAME "virtual-source"
16
+
17
+PW_LOG_TOPIC_STATIC(mod_topic, "mod." NAME);
18
+#define PW_LOG_TOPIC_DEFAULT mod_topic
19
+
20
+struct module_virtual_source_data {
21
+   struct module *module;
22
+
23
+   struct pw_impl_module *mod;
24
+   struct spa_hook mod_listener;
25
+
26
+   struct pw_properties *global_props;
27
+   struct pw_properties *capture_props;
28
+   struct pw_properties *playback_props;
29
+};
30
+
31
+static void module_destroy(void *data)
32
+{
33
+   struct module_virtual_source_data *d = data;
34
+   spa_hook_remove(&d->mod_listener);
35
+   d->mod = NULL;
36
+   module_schedule_unload(d->module);
37
+}
38
+
39
+static const struct pw_impl_module_events module_events = {
40
+   PW_VERSION_IMPL_MODULE_EVENTS,
41
+   .destroy = module_destroy
42
+};
43
+
44
+static int module_virtual_source_load(struct module *module)
45
+{
46
+   struct module_virtual_source_data *data = module->user_data;
47
+   FILE *f;
48
+   char *args;
49
+   size_t size;
50
+
51
+   pw_properties_setf(data->capture_props, PW_KEY_NODE_GROUP, "virtual-source-%u", module->index);
52
+   pw_properties_setf(data->playback_props, PW_KEY_NODE_GROUP, "virtual-source-%u", module->index);
53
+   pw_properties_setf(data->capture_props, "pulse.module.id", "%u", module->index);
54
+   pw_properties_setf(data->playback_props, "pulse.module.id", "%u", module->index);
55
+
56
+   if ((f = open_memstream(&args, &size)) == NULL)
57
+       return -errno;
58
+
59
+   fprintf(f, "{");
60
+   pw_properties_serialize_dict(f, &data->global_props->dict, 0);
61
+   fprintf(f, " capture.props = {");
62
+   pw_properties_serialize_dict(f, &data->capture_props->dict, 0);
63
+   fprintf(f, " } playback.props = {");
64
+   pw_properties_serialize_dict(f, &data->playback_props->dict, 0);
65
+   fprintf(f, " } }");
66
+   fclose(f);
67
+
68
+   data->mod = pw_context_load_module(module->impl->context,
69
+           "libpipewire-module-loopback",
70
+           args, NULL);
71
+   free(args);
72
+
73
+   if (data->mod == NULL)
74
+       return -errno;
75
+
76
+   pw_impl_module_add_listener(data->mod,
77
+           &data->mod_listener,
78
+           &module_events, data);
79
+
80
+   return 0;
81
+}
82
+
83
+static int module_virtual_source_unload(struct module *module)
84
+{
85
+   struct module_virtual_source_data *d = module->user_data;
86
+
87
+   if (d->mod) {
88
+       spa_hook_remove(&d->mod_listener);
89
+       pw_impl_module_destroy(d->mod);
90
+       d->mod = NULL;
91
+   }
92
+
93
+   pw_properties_free(d->capture_props);
94
+   pw_properties_free(d->playback_props);
95
+   pw_properties_free(d->global_props);
96
+
97
+   return 0;
98
+}
99
+
100
+static const struct spa_dict_item module_virtual_source_info = {
101
+   { PW_KEY_MODULE_AUTHOR, "Arun Raghavan <arun@asymptotic.io>" },
102
+   { PW_KEY_MODULE_DESCRIPTION, "Loopback from source to sink" },
103
+   { PW_KEY_MODULE_USAGE, "source_name=<name for the source> "
104
+               "source_properties=<properties for the source> "
105
+               "master=<name of source to filter> "
106
+               "uplink_sink=<name> (optional)"
107
+               "channels=<number of channels> "
108
+               "channel_map=<channel map> "
109
+               "use_volume_sharing=<yes or no> "
110
+               "force_flat_volume=<yes or no> " },
111
+   { PW_KEY_MODULE_VERSION, PACKAGE_VERSION },
112
+};
113
+
114
+static int module_virtual_source_prepare(struct module * const module)
115
+{
116
+   struct module_virtual_source_data * const d = module->user_data;
117
+   struct pw_properties * const props = module->props;
118
+   struct pw_properties *global_props = NULL, *playback_props = NULL, *capture_props = NULL;
119
+   const char *str;
120
+   struct spa_audio_info_raw info = { 0 };
121
+   int res;
122
+
123
+   PW_LOG_TOPIC_INIT(mod_topic);
124
+
125
+   global_props = pw_properties_new(NULL, NULL);
126
+   capture_props = pw_properties_new(NULL, NULL);
127
+   playback_props = pw_properties_new(NULL, NULL);
128
+   if (!global_props || !capture_props || !playback_props) {
129
+       res = -EINVAL;
130
+       goto out;
131
+   }
132
+
133
+   if ((str = pw_properties_get(props, "source_name")) != NULL) {
134
+       pw_properties_set(global_props, PW_KEY_NODE_NAME, str);
135
+       pw_properties_set(global_props, PW_KEY_NODE_DESCRIPTION, str);
136
+       pw_properties_set(props, "source_name", NULL);
137
+   } else {
138
+       pw_properties_set(global_props, PW_KEY_NODE_NAME, "vsource");
139
+       pw_properties_set(global_props, PW_KEY_NODE_DESCRIPTION, "Virtual Source");
140
+   }
141
+   if ((str = pw_properties_get(props, "source_properties")) != NULL) {
142
+       module_args_add_props(playback_props, str);
143
+       pw_properties_set(props, "source_properties", NULL);
144
+   }
145
+   pw_properties_set(capture_props, PW_KEY_NODE_PASSIVE, "true");
146
+   if (pw_properties_get(playback_props, PW_KEY_MEDIA_CLASS) == NULL)
147
+       pw_properties_set(playback_props, PW_KEY_MEDIA_CLASS, "Audio/Source");
148
+
149
+   if ((str = pw_properties_get(props, "master")) != NULL) {
150
+       if (spa_strendswith(str, ".monitor")) {
151
+           pw_properties_setf(capture_props, PW_KEY_TARGET_OBJECT,
152
+                   "%.*s", (int)strlen(str)-8, str);
153
+           pw_properties_set(capture_props, PW_KEY_STREAM_CAPTURE_SINK,
154
+                   "true");
155
+       } else {
156
+           pw_properties_set(capture_props, PW_KEY_TARGET_OBJECT, str);
157
+       }
158
+       pw_properties_set(props, "master", NULL);
159
+   }
160
+
161
+   if (module_args_to_audioinfo_keys(module->impl, props,
162
+               NULL, NULL, "channels", "channel_map", &info) < 0) {
163
+       res = -EINVAL;
164
+       goto out;
165
+   }
166
+   audioinfo_to_properties(&info, global_props);
167
+
168
+   d->module = module;
169
+   d->global_props = global_props;
170
+   d->capture_props = capture_props;
171
+   d->playback_props = playback_props;
172
+
173
+   return 0;
174
+out:
175
+   pw_properties_free(global_props);
176
+   pw_properties_free(playback_props);
177
+   pw_properties_free(capture_props);
178
+
179
+   return res;
180
+}
181
+
182
+DEFINE_MODULE_INFO(module_virtual_source) = {
183
+   .name = "module-virtual-source",
184
+   .prepare = module_virtual_source_prepare,
185
+   .load = module_virtual_source_load,
186
+   .unload = module_virtual_source_unload,
187
+   .properties = &SPA_DICT_INIT_ARRAY(module_virtual_source_info),
188
+   .data_size = sizeof(struct module_virtual_source_data),
189
+};
190
pipewire-0.3.71.tar.gz/src/modules/module-protocol-pulse/pulse-server.c -> pipewire-0.3.72.tar.gz/src/modules/module-protocol-pulse/pulse-server.c Changed
178
 
1
@@ -1097,13 +1097,17 @@
2
 
3
    switch (id) {
4
    case SPA_PROP_channelVolumes:
5
-       stream->volume.channels = control->n_values;
6
-       memcpy(stream->volume.values, control->values, control->n_values * sizeof(float));
7
-       pw_log_info("stream %p: volume changed %f", stream, stream->volume.values0);
8
+       if (!stream->volume_set) {
9
+           stream->volume.channels = control->n_values;
10
+           memcpy(stream->volume.values, control->values, control->n_values * sizeof(float));
11
+           pw_log_info("stream %p: volume changed %f", stream, stream->volume.values0);
12
+       }
13
        break;
14
    case SPA_PROP_mute:
15
-       stream->muted = control->values0 >= 0.5;
16
-       pw_log_info("stream %p: mute changed %d", stream, stream->muted);
17
+       if (!stream->muted_set) {
18
+           stream->muted = control->values0 >= 0.5;
19
+           pw_log_info("stream %p: mute changed %d", stream, stream->muted);
20
+       }
21
        break;
22
    }
23
 }
24
@@ -1208,11 +1212,13 @@
25
        struct pw_manager_object *peer;
26
 
27
        if (stream->volume_set) {
28
+           stream->volume_set = false;
29
            pw_stream_set_control(stream->stream,
30
                SPA_PROP_channelVolumes, stream->volume.channels, stream->volume.values, 0);
31
        }
32
        if (stream->muted_set) {
33
            float val = stream->muted ? 1.0f : 0.0f;
34
+           stream->muted_set = false;
35
            pw_stream_set_control(stream->stream,
36
                SPA_PROP_mute, 1, &val, 0);
37
        }
38
@@ -1586,6 +1592,8 @@
39
    const struct spa_pod *paramsMAX_FORMATS;
40
    uint8_t buffer4096;
41
    struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buffer, sizeof(buffer));
42
+   struct pw_manager_object *o;
43
+   bool is_monitor;
44
 
45
    props = pw_properties_copy(client->props);
46
    if (props == NULL)
47
@@ -1632,22 +1640,17 @@
48
                TAG_INVALID) < 0)
49
            goto error_protocol;
50
    }
51
+   o = find_device(client, sink_index, sink_name, true, &is_monitor);
52
 
53
    spa_zero(fix_ss);
54
    spa_zero(fix_map);
55
-   if (fix_format || fix_rate || fix_channels) {
56
-       struct pw_manager_object *o;
57
-       bool is_monitor;
58
-
59
-       o = find_device(client, sink_index, sink_name, true, &is_monitor);
60
-       if (o != NULL) {
61
-           struct device_info dev_info = DEVICE_INFO_INIT(PW_DIRECTION_OUTPUT);
62
-           collect_device_info(o, NULL, &dev_info, is_monitor, &impl->defs);
63
-           fix_ss.format = fix_format ? dev_info.ss.format : 0;
64
-           fix_ss.rate = fix_rate ? dev_info.ss.rate : 0;
65
-           fix_ss.channels = fix_channels ? dev_info.ss.channels : 0;
66
-           fix_map = dev_info.map;
67
-       }
68
+   if ((fix_format || fix_rate || fix_channels) && o != NULL) {
69
+       struct device_info dev_info = DEVICE_INFO_INIT(PW_DIRECTION_OUTPUT);
70
+       collect_device_info(o, NULL, &dev_info, is_monitor, &impl->defs);
71
+       fix_ss.format = fix_format ? dev_info.ss.format : 0;
72
+       fix_ss.rate = fix_rate ? dev_info.ss.rate : 0;
73
+       fix_ss.channels = fix_channels ? dev_info.ss.channels : 0;
74
+       fix_map = dev_info.map;
75
    }
76
 
77
    if (client->version >= 13) {
78
@@ -1772,6 +1775,9 @@
79
        flags |= PW_STREAM_FLAG_DONT_RECONNECT;
80
 
81
    if (sink_name != NULL) {
82
+       if (o != NULL)
83
+           sink_name = pw_properties_get(o->props,
84
+                   PW_KEY_NODE_NAME);
85
        pw_properties_set(props,
86
                PW_KEY_TARGET_OBJECT, sink_name);
87
    } else if (sink_index != SPA_ID_INVALID && sink_index != 0) {
88
@@ -1858,6 +1864,8 @@
89
    const struct spa_pod *paramsMAX_FORMATS;
90
    uint8_t buffer4096;
91
    struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buffer, sizeof(buffer));
92
+   struct pw_manager_object *o;
93
+   bool is_monitor = false;
94
 
95
    props = pw_properties_copy(client->props);
96
    if (props == NULL)
97
@@ -1922,22 +1930,17 @@
98
                TAG_INVALID) < 0)
99
            goto error_protocol;
100
    }
101
+   o = find_device(client, source_index, source_name, false, &is_monitor);
102
 
103
    spa_zero(fix_ss);
104
    spa_zero(fix_map);
105
-   if (fix_format || fix_rate || fix_channels) {
106
-       struct pw_manager_object *o;
107
-       bool is_monitor;
108
-
109
-       o = find_device(client, source_index, source_name, false, &is_monitor);
110
-       if (o != NULL) {
111
-           struct device_info dev_info = DEVICE_INFO_INIT(PW_DIRECTION_INPUT);
112
-           collect_device_info(o, NULL, &dev_info, is_monitor, &impl->defs);
113
-           fix_ss.format = fix_format ? dev_info.ss.format : 0;
114
-           fix_ss.rate = fix_rate ? dev_info.ss.rate : 0;
115
-           fix_ss.channels = fix_channels ? dev_info.ss.channels : 0;
116
-           fix_map = dev_info.map;
117
-       }
118
+   if ((fix_format || fix_rate || fix_channels) && o != NULL) {
119
+       struct device_info dev_info = DEVICE_INFO_INIT(PW_DIRECTION_INPUT);
120
+       collect_device_info(o, NULL, &dev_info, is_monitor, &impl->defs);
121
+       fix_ss.format = fix_format ? dev_info.ss.format : 0;
122
+       fix_ss.rate = fix_rate ? dev_info.ss.rate : 0;
123
+       fix_ss.channels = fix_channels ? dev_info.ss.channels : 0;
124
+       fix_map = dev_info.map;
125
    }
126
 
127
    if (client->version >= 22) {
128
@@ -2048,16 +2051,21 @@
129
        pw_properties_setf(props,
130
                PW_KEY_TARGET_OBJECT, "%u", source_index);
131
    } else if (source_name != NULL) {
132
+       if (o != NULL)
133
+           source_name = pw_properties_get(o->props,
134
+                   PW_KEY_NODE_NAME);
135
        if (spa_strendswith(source_name, ".monitor")) {
136
+           is_monitor = true;
137
            pw_properties_setf(props,
138
                    PW_KEY_TARGET_OBJECT,
139
                    "%.*s", (int)strlen(source_name)-8, source_name);
140
-           pw_properties_set(props,
141
-                   PW_KEY_STREAM_CAPTURE_SINK, "true");
142
        } else {
143
            pw_properties_set(props,
144
                    PW_KEY_TARGET_OBJECT, source_name);
145
        }
146
+       if (is_monitor)
147
+           pw_properties_set(props,
148
+                   PW_KEY_STREAM_CAPTURE_SINK, "true");
149
    }
150
 
151
    stream->stream = pw_stream_new(client->core, name, props);
152
@@ -2453,6 +2461,7 @@
153
                return NULL;
154
            sink = true;
155
            find_default = true;
156
+           monitor = true;
157
        } else if (spa_streq(name, DEFAULT_SOURCE)) {
158
            if (sink)
159
                return NULL;
160
@@ -2908,10 +2917,15 @@
161
        (index != SPA_ID_INVALID && name != NULL))
162
        return -EINVAL;
163
 
164
-   if (command == COMMAND_SET_SINK_VOLUME)
165
+   if (command == COMMAND_SET_SINK_VOLUME) {
166
+       if (client->quirks & QUIRK_BLOCK_SINK_VOLUME)
167
+           return -EPERM;
168
        direction = PW_DIRECTION_OUTPUT;
169
-   else
170
+   } else {
171
+       if (client->quirks & QUIRK_BLOCK_SOURCE_VOLUME)
172
+           return -EPERM;
173
        direction = PW_DIRECTION_INPUT;
174
+   }
175
 
176
    o = find_device(client, index, name, direction == PW_DIRECTION_OUTPUT, &is_monitor);
177
    if (o == NULL || (info = o->info) == NULL || info->props == NULL)
178
pipewire-0.3.71.tar.gz/src/modules/module-protocol-pulse/quirks.c -> pipewire-0.3.72.tar.gz/src/modules/module-protocol-pulse/quirks.c Changed
10
 
1
@@ -17,6 +17,8 @@
2
    static const struct { const char *key; uint64_t value; } quirk_keys = {
3
        { "force-s16-info", QUIRK_FORCE_S16_FORMAT },
4
        { "remove-capture-dont-move", QUIRK_REMOVE_CAPTURE_DONT_MOVE },
5
+       { "block-source-volume", QUIRK_BLOCK_SOURCE_VOLUME },
6
+       { "block-sink-volume", QUIRK_BLOCK_SINK_VOLUME },
7
    };
8
    SPA_FOR_EACH_ELEMENT_VAR(quirk_keys, i) {
9
        if (spa_streq(str, i->key))
10
pipewire-0.3.71.tar.gz/src/modules/module-protocol-pulse/quirks.h -> pipewire-0.3.72.tar.gz/src/modules/module-protocol-pulse/quirks.h Changed
10
 
1
@@ -10,6 +10,8 @@
2
 #define QUIRK_FORCE_S16_FORMAT         (1ull<<0)   /** forces S16 sample format in sink and source
3
                                  * info */
4
 #define QUIRK_REMOVE_CAPTURE_DONT_MOVE     (1ull<<1)   /** removes the capture stream DONT_MOVE flag */
5
+#define QUIRK_BLOCK_SOURCE_VOLUME      (1ull<<2)   /** block volume changes to sources */
6
+#define QUIRK_BLOCK_SINK_VOLUME            (1ull<<3)   /** block volume changes to sinks */
7
 
8
 int client_update_quirks(struct client *client);
9
 
10
pipewire-0.3.71.tar.gz/src/modules/module-protocol-pulse/stream.c -> pipewire-0.3.72.tar.gz/src/modules/module-protocol-pulse/stream.c Changed
36
 
1
@@ -179,11 +179,18 @@
2
    missing -= stream->requested;
3
    missing -= avail;
4
 
5
-   if (missing <= 0)
6
+   if (missing <= 0) {
7
+       pw_log_debug("stream %p: (tlen:%u - req:%"PRIi64" - avail:%"PRIi64") <= 0",
8
+               stream, stream->attr.tlength, stream->requested, avail);
9
        return 0;
10
+   }
11
 
12
-   if (missing < stream->attr.minreq && !stream_prebuf_active(stream, avail))
13
+   if (missing < stream->attr.minreq && !stream_prebuf_active(stream, avail)) {
14
+       pw_log_debug("stream %p: (tlen:%u - req:%"PRIi64" - avail:%"PRIi64") <= minreq:%u",
15
+               stream, stream->attr.tlength, stream->requested, avail,
16
+               stream->attr.minreq);
17
        return 0;
18
+   }
19
 
20
    stream->requested += missing;
21
 
22
@@ -304,11 +311,12 @@
23
    uint32_t size;
24
 
25
    size = stream_pop_missing(stream);
26
-   pw_log_debug("stream %p: REQUEST channel:%d %u", stream, stream->channel, size);
27
 
28
    if (size == 0)
29
        return 0;
30
 
31
+   pw_log_debug("stream %p: REQUEST channel:%d %u", stream, stream->channel, size);
32
+
33
    msg = message_alloc(impl, -1, 0);
34
    message_put(msg,
35
        TAG_U32, COMMAND_REQUEST,
36
pipewire-0.3.71.tar.gz/src/modules/module-raop-discover.c -> pipewire-0.3.72.tar.gz/src/modules/module-raop-discover.c Changed
177
 
1
@@ -126,9 +126,6 @@
2
 
3
 struct tunnel_info {
4
    const char *name;
5
-   const char *host_name;
6
-   const char *ip;
7
-   const char *port;
8
 };
9
 
10
 #define TUNNEL_INFO(...) ((struct tunnel_info){ __VA_ARGS__ })
11
@@ -151,9 +148,6 @@
12
        return NULL;
13
 
14
    t->info.name = strdup(info->name);
15
-   t->info.host_name = strdup(info->host_name);
16
-   t->info.ip = strdup(info->ip);
17
-   t->info.port = strdup(info->port);
18
    spa_list_append(&impl->tunnel_list, &t->link);
19
 
20
    return t;
21
@@ -171,7 +165,11 @@
22
 
23
 static void free_tunnel(struct tunnel *t)
24
 {
25
-   pw_impl_module_destroy(t->module);
26
+   spa_list_remove(&t->link);
27
+   if (t->module)
28
+       pw_impl_module_destroy(t->module);
29
+   free((char *) t->info.name);
30
+   free(t);
31
 }
32
 
33
 static void impl_free(struct impl *impl)
34
@@ -284,16 +282,8 @@
35
 static void submodule_destroy(void *data)
36
 {
37
    struct tunnel *t = data;
38
-
39
-   spa_list_remove(&t->link);
40
    spa_hook_remove(&t->module_listener);
41
-
42
-   free((char *) t->info.name);
43
-   free((char *) t->info.host_name);
44
-   free((char *) t->info.ip);
45
-   free((char *) t->info.port);
46
-
47
-   free(t);
48
+   t->module = NULL;
49
 }
50
 
51
 static const struct pw_impl_module_events submodule_events = {
52
@@ -304,19 +294,18 @@
53
 struct match_info {
54
    struct impl *impl;
55
    struct pw_properties *props;
56
-   struct tunnel_info *tinfo;
57
+   struct tunnel *tunnel;
58
    bool matched;
59
 };
60
 
61
 static int create_stream(struct impl *impl, struct pw_properties *props,
62
-       struct tunnel_info *tinfo)
63
+       struct tunnel *t)
64
 {
65
    FILE *f;
66
    char *args;
67
    size_t size;
68
    int res = 0;
69
    struct pw_impl_module *mod;
70
-   struct tunnel *t;
71
 
72
    if ((f = open_memstream(&args, &size)) == NULL) {
73
        res = -errno;
74
@@ -341,16 +330,7 @@
75
                 goto done;
76
    }
77
 
78
-   t = make_tunnel(impl, tinfo);
79
-   if (t == NULL) {
80
-       res = -errno;
81
-       pw_log_error("Can't make tunnel: %m");
82
-       pw_impl_module_destroy(mod);
83
-       goto done;
84
-   }
85
-
86
    pw_impl_module_add_listener(mod, &t->module_listener, &submodule_events, t);
87
-
88
    t->module = mod;
89
 done:
90
    return res;
91
@@ -365,7 +345,7 @@
92
    i->matched = true;
93
    if (spa_streq(action, "create-stream")) {
94
        pw_properties_update_string(i->props, str, len);
95
-       create_stream(i->impl, i->props, i->tinfo);
96
+       create_stream(i->impl, i->props, i->tunnel);
97
    }
98
    return res;
99
 }
100
@@ -377,10 +357,12 @@
101
 {
102
    struct impl *impl = userdata;
103
    struct tunnel_info tinfo;
104
-   const char *str, *port_str;
105
+   struct tunnel *t;
106
+   const char *str;
107
    AvahiStringList *l;
108
    struct pw_properties *props = NULL;
109
    char atAVAHI_ADDRESS_STR_MAX;
110
+   int ipv;
111
 
112
    if (event != AVAHI_RESOLVER_FOUND) {
113
        pw_log_error("Resolving of '%s' failed: %s", name,
114
@@ -388,7 +370,19 @@
115
        goto done;
116
    }
117
 
118
-   avahi_address_snprint(at, sizeof(at), a);
119
+   tinfo = TUNNEL_INFO(.name = name);
120
+
121
+   t = find_tunnel(impl, &tinfo);
122
+   if (t == NULL)
123
+       t = make_tunnel(impl, &tinfo);
124
+   if (t == NULL) {
125
+       pw_log_error("Can't make tunnel: %m");
126
+       goto done;
127
+   }
128
+   if (t->module != NULL) {
129
+       pw_log_info("found duplicate mdns entry - skipping tunnel creation");
130
+       goto done;
131
+   }
132
 
133
    props = pw_properties_new(NULL, NULL);
134
    if (props == NULL) {
135
@@ -396,7 +390,10 @@
136
        goto done;
137
    }
138
 
139
+   avahi_address_snprint(at, sizeof(at), a);
140
+   ipv = protocol == AVAHI_PROTO_INET ? 4 : 6;
141
    pw_properties_setf(props, "raop.ip", "%s", at);
142
+   pw_properties_setf(props, "raop.ip.version", "%d", ipv);
143
    pw_properties_setf(props, "raop.port", "%u", port);
144
    pw_properties_setf(props, "raop.name", "%s", name);
145
    pw_properties_setf(props, "raop.hostname", "%s", host_name);
146
@@ -413,20 +410,13 @@
147
        avahi_free(value);
148
    }
149
 
150
-   port_str = pw_properties_get(props, "raop.port");
151
-
152
-   tinfo = TUNNEL_INFO(.name = name,
153
-           .host_name = host_name,
154
-           .ip = at,
155
-           .port = port_str);
156
-
157
    if ((str = pw_properties_get(impl->properties, "stream.rules")) == NULL)
158
        str = DEFAULT_CREATE_RULES;
159
    if (str != NULL) {
160
        struct match_info minfo = {
161
            .impl = impl,
162
            .props = props,
163
-           .tinfo = &tinfo,
164
+           .tunnel = t,
165
        };
166
        pw_conf_match_rules(str, strlen(str), NAME, &props->dict,
167
                rule_matched, &minfo);
168
@@ -459,7 +449,7 @@
169
    switch (event) {
170
    case AVAHI_BROWSER_NEW:
171
        if (t != NULL) {
172
-           pw_log_debug("found duplicate mdns entry - skipping tunnel creation");
173
+           pw_log_info("found duplicate mdns entry - skipping tunnel creation");
174
            return;
175
        }
176
        if (!(avahi_service_resolver_new(impl->client,
177
pipewire-0.3.71.tar.gz/src/modules/module-raop-sink.c -> pipewire-0.3.72.tar.gz/src/modules/module-raop-sink.c Changed
201
 
1
@@ -122,6 +122,9 @@
2
 #define FRAMES_PER_TCP_PACKET 4096
3
 #define FRAMES_PER_UDP_PACKET 352
4
 
5
+#define RAOP_LATENCY_MIN   11025u
6
+#define DEFAULT_LATENCY_MS "1000"
7
+
8
 #define DEFAULT_TCP_AUDIO_PORT   6000
9
 #define DEFAULT_UDP_AUDIO_PORT   6000
10
 #define DEFAULT_UDP_CONTROL_PORT 6001
11
@@ -143,8 +146,6 @@
12
 #define DEFAULT_CHANNELS 2
13
 #define DEFAULT_POSITION " FL FR "
14
 
15
-#define DEFAULT_LATENCY 22050
16
-
17
 #define VOLUME_MAX  0.0
18
 #define VOLUME_DEF -30.0
19
 #define VOLUME_MIN -144.0
20
@@ -243,7 +244,6 @@
21
    struct spa_source *server_source;
22
 
23
    uint32_t block_size;
24
-   uint32_t delay;
25
    uint32_t latency;
26
 
27
    uint16_t seq;
28
@@ -297,10 +297,10 @@
29
     return ntp | (uint64_t) (ts->tv_sec + 0x83aa7e80) << 32;
30
 }
31
 
32
-static inline uint64_t ntp_now(int clockid)
33
+static inline uint64_t ntp_now(void)
34
 {
35
    struct timespec now;
36
-   clock_gettime(clockid, &now);
37
+   clock_gettime(CLOCK_REALTIME, &now);
38
    return timespec_to_ntp(&now);
39
 }
40
 
41
@@ -309,22 +309,20 @@
42
 {
43
    uint32_t pkt5;
44
    uint32_t rtptime = impl->rtptime;
45
-   uint32_t delay = impl->delay;
46
+   uint32_t latency = impl->latency;
47
    uint64_t transmitted;
48
 
49
    pkt0 = htonl(0x80d40007);
50
    if (impl->first)
51
        pkt0 |= htonl(0x10000000);
52
-   rtptime -= delay;
53
-   pkt1 = htonl(rtptime);
54
-   transmitted = ntp_now(CLOCK_MONOTONIC);
55
+   pkt1 = htonl(rtptime - latency);
56
+   transmitted = ntp_now();
57
    pkt2 = htonl(transmitted >> 32);
58
    pkt3 = htonl(transmitted & 0xffffffff);
59
-   rtptime += delay;
60
    pkt4 = htonl(rtptime);
61
 
62
-   pw_log_debug("sync: delayed:%u now:%"PRIu64" rtptime:%u",
63
-           rtptime - delay, transmitted, rtptime);
64
+   pw_log_debug("sync: first:%d latency:%u now:%"PRIx64" rtptime:%u",
65
+           impl->first, latency, transmitted, rtptime);
66
 
67
    return sendto(impl->control_fd, pkt, sizeof(pkt), 0, dest_addr, addrlen);
68
 }
69
@@ -341,11 +339,11 @@
70
    pkt3 = htonl(remote & 0xffffffff);
71
    pkt4 = htonl(received >> 32);
72
    pkt5 = htonl(received & 0xffffffff);
73
-   transmitted = ntp_now(CLOCK_MONOTONIC);
74
+   transmitted = ntp_now();
75
    pkt6 = htonl(transmitted >> 32);
76
    pkt7 = htonl(transmitted & 0xffffffff);
77
 
78
-   pw_log_debug("sync: remote:%"PRIu64" received:%"PRIu64" transmitted:%"PRIu64,
79
+   pw_log_debug("sync: remote:%"PRIx64" received:%"PRIx64" transmitted:%"PRIx64,
80
            remote, received, transmitted);
81
 
82
    return sendto(impl->timing_fd, pkt, sizeof(pkt), 0, dest_addr, addrlen);
83
@@ -391,8 +389,7 @@
84
    if (!impl->recording)
85
        return 0;
86
 
87
-   impl->sync++;
88
-   if (impl->first || impl->sync == impl->sync_period) {
89
+   if (impl->first || ++impl->sync == impl->sync_period) {
90
        impl->sync = 0;
91
        send_udp_sync_packet(impl, NULL, 0);
92
    }
93
@@ -642,12 +639,17 @@
94
    uint32_t packet8;
95
    ssize_t bytes;
96
 
97
+   if (mask & (SPA_IO_ERR | SPA_IO_HUP)) {
98
+       pw_log_warn("error on timing socket: %08x", mask);
99
+       pw_loop_update_io(impl->loop, impl->timing_source, 0);
100
+       return;
101
+   }
102
    if (mask & SPA_IO_IN) {
103
        uint64_t remote, received;
104
        struct sockaddr_storage sender;
105
        socklen_t sender_size = sizeof(sender);
106
 
107
-       received = ntp_now(CLOCK_MONOTONIC);
108
+       received = ntp_now();
109
        bytes = recvfrom(impl->timing_fd, packet, sizeof(packet), 0,
110
                (struct sockaddr*)&sender, &sender_size);
111
        if (bytes < 0) {
112
@@ -679,13 +681,18 @@
113
    uint32_t packet2;
114
    ssize_t bytes;
115
 
116
+   if (mask & (SPA_IO_ERR | SPA_IO_HUP)) {
117
+       pw_log_warn("error on control socket: %08x", mask);
118
+       pw_loop_update_io(impl->loop, impl->control_source, 0);
119
+       return;
120
+   }
121
    if (mask & SPA_IO_IN) {
122
        uint32_t hdr;
123
        uint16_t seq, num;
124
 
125
        bytes = read(impl->control_fd, packet, sizeof(packet));
126
        if (bytes < 0) {
127
-           pw_log_debug("error reading control packet: %m");
128
+           pw_log_warn("error reading control packet: %m");
129
            return;
130
        }
131
        if (bytes != sizeof(packet)) {
132
@@ -882,15 +889,14 @@
133
    pw_log_info("reply %d", status);
134
 
135
    if ((str = spa_dict_lookup(headers, "Audio-Latency")) != NULL) {
136
-       if (!spa_atou32(str, &impl->latency, 0))
137
-           impl->latency = DEFAULT_LATENCY;
138
-   } else {
139
-       impl->latency = DEFAULT_LATENCY;
140
+       uint32_t l;
141
+       if (spa_atou32(str, &l, 0))
142
+           impl->latency = SPA_MAX(l, impl->latency);
143
    }
144
 
145
    spa_zero(latency);
146
    latency.direction = PW_DIRECTION_INPUT;
147
-   latency.min_rate = latency.max_rate = impl->latency;
148
+   latency.min_rate = latency.max_rate = impl->latency + RAOP_LATENCY_MIN;
149
 
150
    n_params = 0;
151
    spa_pod_builder_init(&b, buffer, sizeof(buffer));
152
@@ -1033,7 +1039,7 @@
153
            if ((impl->timing_fd = connect_socket(impl, SOCK_DGRAM, impl->timing_fd, timing_port)) < 0)
154
                return impl->timing_fd;
155
 
156
-           ntp = ntp_now(CLOCK_MONOTONIC);
157
+           ntp = ntp_now();
158
            send_udp_timing_packet(impl, ntp, ntp, NULL, 0);
159
        }
160
 
161
@@ -1206,8 +1212,6 @@
162
    int res, frames, rsa_len, ip_version;
163
    char *sdp;
164
    char local_ip256;
165
-   int min_latency;
166
-   min_latency = DEFAULT_LATENCY;
167
    host = pw_properties_get(impl->props, "raop.ip");
168
 
169
    if (impl->protocol == PROTO_TCP)
170
@@ -1222,7 +1226,7 @@
171
 
172
    switch (impl->encryption) {
173
    case CRYPTO_NONE:
174
-       asprintf(&sdp, "v=0\r\n"
175
+       sdp = spa_aprintf("v=0\r\n"
176
                "o=iTunes %s 0 IN IP%d %s\r\n"
177
                "s=iTunes\r\n"
178
                "c=IN IP%d %s\r\n"
179
@@ -1232,10 +1236,12 @@
180
                "a=fmtp:96 %d 0 16 40 10 14 2 255 0 0 %u\r\n",
181
                impl->session_id, ip_version, local_ip,
182
                ip_version, host, frames, impl->info.rate);
183
+       if (!sdp)
184
+           return -errno;
185
        break;
186
 
187
    case CRYPTO_AUTH_SETUP:
188
-       asprintf(&sdp, "v=0\r\n"
189
+       sdp = spa_aprintf("v=0\r\n"
190
                "o=iTunes %s 0 IN IP%d %s\r\n"
191
                "s=iTunes\r\n"
192
                "c=IN IP%d %s\r\n"
193
@@ -1246,7 +1252,9 @@
194
                "a=min-latency:%d",
195
                impl->session_id, ip_version, local_ip,
196
                ip_version, host, frames, impl->info.rate,
197
-               min_latency);
198
+               RAOP_LATENCY_MIN);
199
+       if (!sdp)
200
+           return -errno;
201
pipewire-0.3.71.tar.gz/src/modules/module-raop/rtsp-client.c -> pipewire-0.3.72.tar.gz/src/modules/module-raop/rtsp-client.c Changed
25
 
1
@@ -490,6 +490,7 @@
2
        pw_log_error("getaddrinfo: %s", gai_strerror(res));
3
        return -EINVAL;
4
    }
5
+   res = -ENOENT;
6
    for (rp = result; rp != NULL; rp = rp->ai_next) {
7
        fd = socket(rp->ai_family,
8
                rp->ai_socktype | SOCK_CLOEXEC | SOCK_NONBLOCK,
9
@@ -501,12 +502,14 @@
10
        if (res == 0 || (res < 0 && errno == EINPROGRESS))
11
            break;
12
 
13
+       res = -errno;
14
        close(fd);
15
    }
16
    freeaddrinfo(result);
17
 
18
    if (rp == NULL) {
19
-       pw_log_error("Could not connect to %s:%u", hostname, port);
20
+       pw_log_error("Could not connect to %s:%u: %s", hostname, port,
21
+               spa_strerror(res));
22
        return -EINVAL;
23
    }
24
 
25
pipewire-0.3.71.tar.gz/src/modules/module-rtp-sap.c -> pipewire-0.3.72.tar.gz/src/modules/module-rtp-sap.c Changed
161
 
1
@@ -355,8 +355,10 @@
2
    return false;
3
 }
4
 
5
-static int make_send_socket(struct sockaddr_storage *sa, socklen_t salen,
6
-       bool loop, int ttl, char *ifname)
7
+static int make_send_socket(
8
+       struct sockaddr_storage *src, socklen_t src_len,
9
+       struct sockaddr_storage *sa, socklen_t salen,
10
+       bool loop, int ttl)
11
 {
12
    int af, fd, val, res;
13
 
14
@@ -365,6 +367,11 @@
15
        pw_log_error("socket failed: %m");
16
        return -errno;
17
    }
18
+   if (bind(fd, (struct sockaddr*)src, src_len) < 0) {
19
+       res = -errno;
20
+       pw_log_error("bind() failed: %m");
21
+       goto error;
22
+   }
23
    if (connect(fd, (struct sockaddr*)sa, salen) < 0) {
24
        res = -errno;
25
        pw_log_error("connect() failed: %m");
26
@@ -520,43 +527,42 @@
27
        snprintf(dst_ttl, sizeof(dst_ttl), "/%d", sdp->ttl);
28
 
29
    spa_strbuf_init(&buf, buffer, sizeof(buffer));
30
+   /* Don't add any sdp records in between this definition or change the order
31
+      it will break compatibility with Dante/AES67 devices. Add new records to
32
+      the end. */
33
    spa_strbuf_append(&buf,
34
            "v=0\n"
35
            "o=%s %u 0 IN %s %s\n"
36
            "s=%s\n"
37
            "c=IN %s %s%s\n"
38
            "t=%u 0\n"
39
-           "a=recvonly\n"
40
-           "a=tool:PipeWire %s\n"
41
-           "a=type:broadcast\n",
42
+           "m=%s %u RTP/AVP %i\n",
43
            user_name, sdp->ntp, src_ip4 ? "IP4" : "IP6", src_addr,
44
            sdp->session_name,
45
            dst_ip4 ? "IP4" : "IP6", dst_addr, dst_ttl,
46
            sdp->ntp,
47
-           pw_get_library_version());
48
-   spa_strbuf_append(&buf,
49
-           "m=%s %u RTP/AVP %i\n",
50
-           sdp->media_type,
51
-           sdp->dst_port, sdp->payload);
52
+           sdp->media_type, sdp->dst_port, sdp->payload);
53
 
54
    if (sdp->channels) {
55
-       spa_strbuf_append(&buf,
56
-           "a=rtpmap:%i %s/%u/%u\n",
57
-               sdp->payload, sdp->mime_type,
58
-               sdp->rate, sdp->channels);
59
        if (sdp->channelmap0 != 0) {
60
            spa_strbuf_append(&buf,
61
                "i=%d channels: %s\n", sdp->channels,
62
                sdp->channelmap);
63
        }
64
+       spa_strbuf_append(&buf,
65
+           "a=recvonly\n"
66
+           "a=rtpmap:%i %s/%u/%u\n",
67
+               sdp->payload, sdp->mime_type,
68
+               sdp->rate, sdp->channels);
69
    } else {
70
        spa_strbuf_append(&buf,
71
            "a=rtpmap:%i %s/%u\n",
72
                sdp->payload, sdp->mime_type, sdp->rate);
73
    }
74
-   if (sdp->ptime != 0)
75
+
76
+   if (sdp->ptime > 0)
77
        spa_strbuf_append(&buf,
78
-           "a=ptime:%f\n", sdp->ptime);
79
+           "a=ptime:%.6g\n", sdp->ptime);
80
 
81
    if (sdp->ts_refclk != NULL) {
82
        spa_strbuf_append(&buf,
83
@@ -568,6 +574,11 @@
84
        spa_strbuf_append(&buf, "a=mediaclk:sender\n");
85
    }
86
 
87
+   spa_strbuf_append(&buf,
88
+       "a=tool:PipeWire %s\n"
89
+       "a=type:broadcast\n",
90
+       pw_get_library_version());
91
+
92
    pw_log_debug("sending SAP for %u %s", sess->node->id, buffer);
93
 
94
    iov3.iov_base = buffer;
95
@@ -672,6 +683,10 @@
96
    sdp->ttl = pw_properties_get_int32(props, "rtp.ttl", DEFAULT_TTL);
97
    sdp->payload = pw_properties_get_int32(props, "rtp.payload", 127);
98
 
99
+   if ((str = pw_properties_get(props, "rtp.ptime")) != NULL)
100
+       if (!spa_atof(str, &sdp->ptime))
101
+           sdp->ptime = 0.0;
102
+
103
    if ((str = pw_properties_get(props, "rtp.media")) != NULL)
104
        sdp->media_type = strdup(str);
105
    if ((str = pw_properties_get(props, "rtp.mime")) != NULL)
106
@@ -684,7 +699,7 @@
107
        sdp->ts_offset = atoi(str);
108
    if ((str = pw_properties_get(props, "rtp.ts-refclk")) != NULL)
109
        sdp->ts_refclk = strdup(str);
110
-   if ((str = pw_properties_get(props, "rtp.channel-names")) != NULL)
111
+   if ((str = pw_properties_get(props, PW_KEY_NODE_CHANNELNAMES)) != NULL)
112
        snprintf(sdp->channelmap, sizeof(sdp->channelmap), "%s", str);
113
 
114
    pw_log_info("created new session for node:%u", node->id);
115
@@ -884,6 +899,7 @@
116
    pw_properties_setf(props, "rtp.destination.ip", "%s", dst_addr);
117
    pw_properties_setf(props, "rtp.destination.port", "%u", info->dst_port);
118
    pw_properties_setf(props, "rtp.payload", "%u", info->payload);
119
+   pw_properties_setf(props, "rtp.ptime", "%f", info->ptime);
120
    pw_properties_setf(props, "rtp.media", "%s", info->media_type);
121
    pw_properties_setf(props, "rtp.mime", "%s", info->mime_type);
122
    pw_properties_setf(props, "rtp.rate", "%u", info->rate);
123
@@ -1209,9 +1225,9 @@
124
    int fd, res;
125
    struct timespec value, interval;
126
 
127
-   if ((fd = make_send_socket(&impl->sap_addr, impl->sap_len,
128
-                   impl->mcast_loop, impl->ttl,
129
-                   impl->ifname)) < 0)
130
+   if ((fd = make_send_socket(&impl->src_addr, impl->src_len,
131
+                   &impl->sap_addr, impl->sap_len,
132
+                   impl->mcast_loop, impl->ttl)) < 0)
133
        return fd;
134
 
135
    impl->sap_fd = fd;
136
@@ -1456,8 +1472,23 @@
137
    impl->cleanup_interval = pw_properties_get_uint32(impl->props,
138
            "sap.cleanup.sec", DEFAULT_CLEANUP_SEC);
139
 
140
-   if ((str = pw_properties_get(props, "source.ip")) == NULL)
141
+   if ((str = pw_properties_get(props, "source.ip")) == NULL) {
142
        str = DEFAULT_SOURCE_IP;
143
+       if (impl->ifname) {
144
+           int fd = socket(AF_INET, SOCK_DGRAM, 0);
145
+           if (fd >= 0) {
146
+               struct ifreq req;
147
+               spa_zero(req);
148
+               req.ifr_addr.sa_family = AF_INET;
149
+               snprintf(req.ifr_name, sizeof(req.ifr_name), "%s", impl->ifname);
150
+               res = ioctl(fd, SIOCGIFADDR, &req);
151
+               if (res < 0)
152
+                   pw_log_warn("SIOCGIFADDR %s failed: %m", impl->ifname);
153
+               str = inet_ntoa(((struct sockaddr_in *)&req.ifr_addr)->sin_addr);
154
+               close(fd);
155
+           }
156
+       }
157
+   }
158
    if ((res = parse_address(str, port, &impl->src_addr, &impl->src_len)) < 0) {
159
        pw_log_error("invalid source.ip %s: %s", str, spa_strerror(res));
160
        goto out;
161
pipewire-0.3.71.tar.gz/src/modules/module-rtp-source.c -> pipewire-0.3.72.tar.gz/src/modules/module-rtp-source.c Changed
19
 
1
@@ -18,6 +18,7 @@
2
 #include <spa/utils/hook.h>
3
 #include <spa/utils/result.h>
4
 #include <spa/utils/ringbuffer.h>
5
+#include <spa/utils/defs.h>
6
 #include <spa/utils/dll.h>
7
 #include <spa/utils/json.h>
8
 #include <spa/param/audio/format-utils.h>
9
@@ -164,7 +165,8 @@
10
        if (len < 12)
11
            goto short_packet;
12
 
13
-       rtp_stream_receive_packet(impl->stream, buffer, len);
14
+       if (SPA_LIKELY(impl->stream))
15
+           rtp_stream_receive_packet(impl->stream, buffer, len);
16
 
17
        impl->receiving = true;
18
    }
19
pipewire-0.3.71.tar.gz/src/modules/module-rtp/stream.c -> pipewire-0.3.72.tar.gz/src/modules/module-rtp/stream.c Changed
33
 
1
@@ -389,9 +389,20 @@
2
    min_samples = min_ptime * impl->rate / 1000;
3
    max_samples = max_ptime * impl->rate / 1000;
4
 
5
-   impl->psamples = impl->mtu / impl->stride;
6
-   impl->psamples = SPA_CLAMP(impl->psamples, min_samples, max_samples);
7
+   float ptime = 0;
8
+   if ((str = pw_properties_get(props, "rtp.ptime")) != NULL)
9
+       if (!spa_atof(str, &ptime))
10
+           ptime = 0.0;
11
 
12
+   if (ptime) {
13
+       impl->psamples = ptime * impl->rate / 1000;
14
+   } else {
15
+       impl->psamples = impl->mtu / impl->stride;
16
+       impl->psamples = SPA_CLAMP(impl->psamples, min_samples, max_samples);
17
+       if (direction == PW_DIRECTION_OUTPUT)
18
+           pw_properties_setf(props, "rtp.ptime", "%f",
19
+                   impl->psamples * 1000.0 / impl->rate);
20
+   }
21
    latency_msec = pw_properties_get_uint32(props,
22
            "sess.latency.msec", DEFAULT_SESS_LATENCY);
23
    impl->target_buffer = msec_to_samples(impl, latency_msec);
24
@@ -407,8 +418,6 @@
25
    }
26
 
27
    pw_properties_setf(props, "net.mtu", "%u", impl->mtu);
28
-   pw_properties_setf(props, "rtp.ptime", "%u",
29
-           impl->psamples * 1000 / impl->rate);
30
    pw_properties_setf(props, "rtp.media", "%s", impl->format_info->media_type);
31
    pw_properties_setf(props, "rtp.mime", "%s", impl->format_info->mime);
32
    pw_properties_setf(props, "rtp.payload", "%u", impl->payload);
33
pipewire-0.3.71.tar.gz/src/modules/module-session-manager/client-endpoint/endpoint-stream.c -> pipewire-0.3.72.tar.gz/src/modules/module-session-manager/client-endpoint/endpoint-stream.c Changed
44
 
1
@@ -10,6 +10,7 @@
2
 #include <pipewire/extensions/session-manager.h>
3
 
4
 #include <spa/pod/filter.h>
5
+#include <spa/pod/dynamic.h>
6
 
7
 #include "endpoint-stream.h"
8
 #include "client-endpoint.h"
9
@@ -39,8 +40,8 @@
10
    struct endpoint_stream *this = data->stream;
11
    struct spa_pod *result;
12
    struct spa_pod *param;
13
-   uint8_t buffer1024;
14
-   struct spa_pod_builder b = { 0 };
15
+   uint8_t buffer2048;
16
+   struct spa_pod_dynamic_builder b = { 0 };
17
    uint32_t index;
18
    uint32_t next = start;
19
    uint32_t count = 0;
20
@@ -55,15 +56,15 @@
21
        if (param == NULL || !spa_pod_is_object_id(param, id))
22
            continue;
23
 
24
-       spa_pod_builder_init(&b, buffer, sizeof(buffer));
25
-       if (spa_pod_filter(&b, &result, param, filter) != 0)
26
-           continue;
27
-
28
-       pw_log_debug(NAME" %p: %d param %u", this, seq, index);
29
-
30
-       pw_endpoint_stream_resource_param(resource, seq, id, index, next, result);
31
+       spa_pod_dynamic_builder_init(&b, buffer, sizeof(buffer), 4096);
32
+       if (spa_pod_filter(&b.b, &result, param, filter) == 0) {
33
+           pw_log_debug(NAME" %p: %d param %u", this, seq, index);
34
+           pw_endpoint_stream_resource_param(resource, seq, id, index, next, result);
35
+           count++;
36
+       }
37
+       spa_pod_dynamic_builder_clean(&b);
38
 
39
-       if (++count == num)
40
+       if (count == num)
41
            break;
42
    }
43
    return 0;
44
pipewire-0.3.71.tar.gz/src/modules/module-session-manager/client-endpoint/endpoint.c -> pipewire-0.3.72.tar.gz/src/modules/module-session-manager/client-endpoint/endpoint.c Changed
44
 
1
@@ -10,6 +10,7 @@
2
 #include <pipewire/extensions/session-manager.h>
3
 
4
 #include <spa/pod/filter.h>
5
+#include <spa/pod/dynamic.h>
6
 
7
 #include "endpoint.h"
8
 #include "client-endpoint.h"
9
@@ -39,8 +40,8 @@
10
    struct endpoint *this = data->endpoint;
11
    struct spa_pod *result;
12
    struct spa_pod *param;
13
-   uint8_t buffer1024;
14
-   struct spa_pod_builder b = { 0 };
15
+   uint8_t buffer2048;
16
+   struct spa_pod_dynamic_builder b = { 0 };
17
    uint32_t index;
18
    uint32_t next = start;
19
    uint32_t count = 0;
20
@@ -57,15 +58,15 @@
21
        if (param == NULL || !spa_pod_is_object_id(param, id))
22
            continue;
23
 
24
-       spa_pod_builder_init(&b, buffer, sizeof(buffer));
25
-       if (spa_pod_filter(&b, &result, param, filter) != 0)
26
-           continue;
27
-
28
-       pw_log_debug(NAME" %p: %d param %u", this, seq, index);
29
-
30
-       pw_endpoint_resource_param(resource, seq, id, index, next, result);
31
+       spa_pod_dynamic_builder_init(&b, buffer, sizeof(buffer), 4096);
32
+       if (spa_pod_filter(&b.b, &result, param, filter) == 0) {
33
+           pw_log_debug(NAME" %p: %d param %u", this, seq, index);
34
+           pw_endpoint_resource_param(resource, seq, id, index, next, result);
35
+           count++;
36
+       }
37
+       spa_pod_dynamic_builder_clean(&b);
38
 
39
-       if (++count == num)
40
+       if (count == num)
41
            break;
42
    }
43
    return 0;
44
pipewire-0.3.71.tar.gz/src/modules/module-session-manager/client-session/endpoint-link.c -> pipewire-0.3.72.tar.gz/src/modules/module-session-manager/client-session/endpoint-link.c Changed
44
 
1
@@ -10,6 +10,7 @@
2
 #include <pipewire/extensions/session-manager.h>
3
 
4
 #include <spa/pod/filter.h>
5
+#include <spa/pod/dynamic.h>
6
 
7
 #include "endpoint-link.h"
8
 #include "client-session.h"
9
@@ -39,8 +40,8 @@
10
    struct endpoint_link *this = data->link;
11
    struct spa_pod *result;
12
    struct spa_pod *param;
13
-   uint8_t buffer1024;
14
-   struct spa_pod_builder b = { 0 };
15
+   uint8_t buffer2048;
16
+   struct spa_pod_dynamic_builder b = { 0 };
17
    uint32_t index;
18
    uint32_t next = start;
19
    uint32_t count = 0;
20
@@ -55,15 +56,15 @@
21
        if (param == NULL || !spa_pod_is_object_id(param, id))
22
            continue;
23
 
24
-       spa_pod_builder_init(&b, buffer, sizeof(buffer));
25
-       if (spa_pod_filter(&b, &result, param, filter) != 0)
26
-           continue;
27
-
28
-       pw_log_debug(NAME" %p: %d param %u", this, seq, index);
29
-
30
-       pw_endpoint_link_resource_param(resource, seq, id, index, next, result);
31
+       spa_pod_dynamic_builder_init(&b, buffer, sizeof(buffer), 4096);
32
+       if (spa_pod_filter(&b.b, &result, param, filter) == 0) {
33
+           pw_log_debug(NAME" %p: %d param %u", this, seq, index);
34
+           pw_endpoint_link_resource_param(resource, seq, id, index, next, result);
35
+           count++;
36
+       }
37
+       spa_pod_dynamic_builder_clean(&b);
38
 
39
-       if (++count == num)
40
+       if (count == num)
41
            break;
42
    }
43
    return 0;
44
pipewire-0.3.71.tar.gz/src/modules/module-session-manager/client-session/session.c -> pipewire-0.3.72.tar.gz/src/modules/module-session-manager/client-session/session.c Changed
44
 
1
@@ -10,6 +10,7 @@
2
 #include <pipewire/extensions/session-manager.h>
3
 
4
 #include <spa/pod/filter.h>
5
+#include <spa/pod/dynamic.h>
6
 
7
 #include "session.h"
8
 #include "client-session.h"
9
@@ -39,8 +40,8 @@
10
    struct session *this = data->session;
11
    struct spa_pod *result;
12
    struct spa_pod *param;
13
-   uint8_t buffer1024;
14
-   struct spa_pod_builder b = { 0 };
15
+   uint8_t buffer2048;
16
+   struct spa_pod_dynamic_builder b = { 0 };
17
    uint32_t index;
18
    uint32_t next = start;
19
    uint32_t count = 0;
20
@@ -55,15 +56,15 @@
21
        if (param == NULL || !spa_pod_is_object_id(param, id))
22
            continue;
23
 
24
-       spa_pod_builder_init(&b, buffer, sizeof(buffer));
25
-       if (spa_pod_filter(&b, &result, param, filter) != 0)
26
-           continue;
27
-
28
-       pw_log_debug(NAME" %p: %d param %u", this, seq, index);
29
-
30
-       pw_session_resource_param(resource, seq, id, index, next, result);
31
+       spa_pod_dynamic_builder_init(&b, buffer, sizeof(buffer), 4096);
32
+       if (spa_pod_filter(&b.b, &result, param, filter) == 0) {
33
+           pw_log_debug(NAME" %p: %d param %u", this, seq, index);
34
+           pw_session_resource_param(resource, seq, id, index, next, result);
35
+           count++;
36
+       }
37
+       spa_pod_dynamic_builder_clean(&b);
38
 
39
-       if (++count == num)
40
+       if (count == num)
41
            break;
42
    }
43
    return 0;
44
pipewire-0.3.71.tar.gz/src/modules/module-session-manager/endpoint-link.c -> pipewire-0.3.72.tar.gz/src/modules/module-session-manager/endpoint-link.c Changed
44
 
1
@@ -10,6 +10,7 @@
2
 #include <spa/utils/result.h>
3
 #include <spa/pod/builder.h>
4
 #include <spa/pod/filter.h>
5
+#include <spa/pod/dynamic.h>
6
 
7
 #define MAX_PARAMS 32
8
 
9
@@ -84,8 +85,8 @@
10
    struct param_data *pdata;
11
    struct spa_pod *result;
12
    struct spa_pod *param;
13
-   uint8_t buffer1024;
14
-   struct spa_pod_builder b = { 0 };
15
+   uint8_t buffer2048;
16
+   struct spa_pod_dynamic_builder b = { 0 };
17
    uint32_t index;
18
    uint32_t next = start;
19
    uint32_t count = 0;
20
@@ -103,15 +104,15 @@
21
 
22
            param = *pw_array_get_unchecked(&pdata->params, index, struct spa_pod*);
23
 
24
-           spa_pod_builder_init(&b, buffer, sizeof(buffer));
25
-           if (spa_pod_filter(&b, &result, param, filter) != 0)
26
-               continue;
27
+           spa_pod_dynamic_builder_init(&b, buffer, sizeof(buffer), 4096);
28
+           if (spa_pod_filter(&b.b, &result, param, filter) == 0) {
29
+               pw_log_debug(NAME" %p: %d param %u", impl, seq, index);
30
+               pw_endpoint_link_resource_param(d->resource, seq, id, index, next, result);
31
+               count++;
32
+           }
33
+           spa_pod_dynamic_builder_clean(&b);
34
 
35
-           pw_log_debug(NAME" %p: %d param %u", impl, seq, index);
36
-
37
-           pw_endpoint_link_resource_param(d->resource, seq, id, index, next, result);
38
-
39
-           if (++count == num)
40
+           if (count == num)
41
                return 0;
42
        }
43
    }
44
pipewire-0.3.71.tar.gz/src/modules/module-session-manager/endpoint-stream.c -> pipewire-0.3.72.tar.gz/src/modules/module-session-manager/endpoint-stream.c Changed
44
 
1
@@ -10,6 +10,7 @@
2
 #include <spa/utils/result.h>
3
 #include <spa/pod/builder.h>
4
 #include <spa/pod/filter.h>
5
+#include <spa/pod/dynamic.h>
6
 
7
 #define MAX_PARAMS 32
8
 
9
@@ -84,8 +85,8 @@
10
    struct param_data *pdata;
11
    struct spa_pod *result;
12
    struct spa_pod *param;
13
-   uint8_t buffer1024;
14
-   struct spa_pod_builder b = { 0 };
15
+   uint8_t buffer2048;
16
+   struct spa_pod_dynamic_builder b = { 0 };
17
    uint32_t index;
18
    uint32_t next = start;
19
    uint32_t count = 0;
20
@@ -103,15 +104,15 @@
21
 
22
            param = *pw_array_get_unchecked(&pdata->params, index, struct spa_pod*);
23
 
24
-           spa_pod_builder_init(&b, buffer, sizeof(buffer));
25
-           if (spa_pod_filter(&b, &result, param, filter) != 0)
26
-               continue;
27
+           spa_pod_dynamic_builder_init(&b, buffer, sizeof(buffer), 4096);
28
+           if (spa_pod_filter(&b.b, &result, param, filter) == 0) {
29
+               pw_log_debug(NAME" %p: %d param %u", impl, seq, index);
30
+               pw_endpoint_stream_resource_param(d->resource, seq, id, index, next, result);
31
+               count++;
32
+           }
33
+           spa_pod_dynamic_builder_clean(&b);
34
 
35
-           pw_log_debug(NAME" %p: %d param %u", impl, seq, index);
36
-
37
-           pw_endpoint_stream_resource_param(d->resource, seq, id, index, next, result);
38
-
39
-           if (++count == num)
40
+           if (count == num)
41
                return 0;
42
        }
43
    }
44
pipewire-0.3.71.tar.gz/src/modules/module-session-manager/endpoint.c -> pipewire-0.3.72.tar.gz/src/modules/module-session-manager/endpoint.c Changed
44
 
1
@@ -9,6 +9,7 @@
2
 
3
 #include <spa/utils/result.h>
4
 #include <spa/pod/builder.h>
5
+#include <spa/pod/dynamic.h>
6
 #include <spa/pod/filter.h>
7
 
8
 #define MAX_PARAMS 32
9
@@ -84,8 +85,8 @@
10
    struct param_data *pdata;
11
    struct spa_pod *result;
12
    struct spa_pod *param;
13
-   uint8_t buffer1024;
14
-   struct spa_pod_builder b = { 0 };
15
+   uint8_t buffer2048;
16
+   struct spa_pod_dynamic_builder b = { 0 };
17
    uint32_t index;
18
    uint32_t next = start;
19
    uint32_t count = 0;
20
@@ -103,15 +104,15 @@
21
 
22
            param = *pw_array_get_unchecked(&pdata->params, index, struct spa_pod*);
23
 
24
-           spa_pod_builder_init(&b, buffer, sizeof(buffer));
25
-           if (spa_pod_filter(&b, &result, param, filter) != 0)
26
-               continue;
27
+           spa_pod_dynamic_builder_init(&b, buffer, sizeof(buffer), 4096);
28
+           if (spa_pod_filter(&b.b, &result, param, filter) == 0) {
29
+               pw_log_debug(NAME" %p: %d param %u", impl, seq, index);
30
+               pw_endpoint_resource_param(d->resource, seq, id, index, next, result);
31
+               count++;
32
+           }
33
+           spa_pod_dynamic_builder_clean(&b);
34
 
35
-           pw_log_debug(NAME" %p: %d param %u", impl, seq, index);
36
-
37
-           pw_endpoint_resource_param(d->resource, seq, id, index, next, result);
38
-
39
-           if (++count == num)
40
+           if (count == num)
41
                return 0;
42
        }
43
    }
44
pipewire-0.3.71.tar.gz/src/modules/module-session-manager/session.c -> pipewire-0.3.72.tar.gz/src/modules/module-session-manager/session.c Changed
44
 
1
@@ -10,6 +10,7 @@
2
 #include <spa/utils/result.h>
3
 #include <spa/pod/builder.h>
4
 #include <spa/pod/filter.h>
5
+#include <spa/pod/dynamic.h>
6
 
7
 #define MAX_PARAMS 32
8
 
9
@@ -84,8 +85,8 @@
10
    struct param_data *pdata;
11
    struct spa_pod *result;
12
    struct spa_pod *param;
13
-   uint8_t buffer1024;
14
-   struct spa_pod_builder b = { 0 };
15
+   uint8_t buffer2048;
16
+   struct spa_pod_dynamic_builder b = { 0 };
17
    uint32_t index;
18
    uint32_t next = start;
19
    uint32_t count = 0;
20
@@ -103,15 +104,15 @@
21
 
22
            param = *pw_array_get_unchecked(&pdata->params, index, struct spa_pod*);
23
 
24
-           spa_pod_builder_init(&b, buffer, sizeof(buffer));
25
-           if (spa_pod_filter(&b, &result, param, filter) != 0)
26
-               continue;
27
+           spa_pod_dynamic_builder_init(&b, buffer, sizeof(buffer), 4096);
28
+           if (spa_pod_filter(&b.b, &result, param, filter) == 0) {
29
+               pw_log_debug(NAME" %p: %d param %u", impl, seq, index);
30
+               pw_session_resource_param(d->resource, seq, id, index, next, result);
31
+               count++;
32
+           }
33
+           spa_pod_dynamic_builder_clean(&b);
34
 
35
-           pw_log_debug(NAME" %p: %d param %u", impl, seq, index);
36
-
37
-           pw_session_resource_param(d->resource, seq, id, index, next, result);
38
-
39
-           if (++count == num)
40
+           if (count == num)
41
                return 0;
42
        }
43
    }
44
pipewire-0.3.71.tar.gz/src/modules/module-zeroconf-discover.c -> pipewire-0.3.72.tar.gz/src/modules/module-zeroconf-discover.c Changed
147
 
1
@@ -85,11 +85,7 @@
2
 };
3
 
4
 struct tunnel_info {
5
-   AvahiIfIndex interface;
6
-   AvahiProtocol protocol;
7
    const char *name;
8
-   const char *type;
9
-   const char *domain;
10
 };
11
 
12
 #define TUNNEL_INFO(...) ((struct tunnel_info){ __VA_ARGS__ })
13
@@ -111,11 +107,7 @@
14
    if (t == NULL)
15
        return NULL;
16
 
17
-   t->info.interface = info->interface;
18
-   t->info.protocol = info->protocol;
19
    t->info.name = strdup(info->name);
20
-   t->info.type = strdup(info->type);
21
-   t->info.domain = strdup(info->domain);
22
    spa_list_append(&impl->tunnel_list, &t->link);
23
 
24
    return t;
25
@@ -125,11 +117,7 @@
26
 {
27
    struct tunnel *t;
28
    spa_list_for_each(t, &impl->tunnel_list, link) {
29
-       if (t->info.interface == info->interface &&
30
-           t->info.protocol == info->protocol &&
31
-           spa_streq(t->info.name, info->name) &&
32
-           spa_streq(t->info.type, info->type) &&
33
-           spa_streq(t->info.domain, info->domain))
34
+       if (spa_streq(t->info.name, info->name))
35
            return t;
36
    }
37
    return NULL;
38
@@ -137,7 +125,12 @@
39
 
40
 static void free_tunnel(struct tunnel *t)
41
 {
42
-   pw_impl_module_destroy(t->module);
43
+   spa_list_remove(&t->link);
44
+   if (t->module)
45
+       pw_impl_module_destroy(t->module);
46
+   free((char *) t->info.name);
47
+
48
+   free(t);
49
 }
50
 
51
 static void impl_free(struct impl *impl)
52
@@ -226,14 +219,8 @@
53
 {
54
    struct tunnel *t = data;
55
 
56
-   spa_list_remove(&t->link);
57
    spa_hook_remove(&t->module_listener);
58
-
59
-   free((char *) t->info.name);
60
-   free((char *) t->info.type);
61
-   free((char *) t->info.domain);
62
-
63
-   free(t);
64
+   t->module = NULL;
65
 }
66
 
67
 static const struct pw_impl_module_events submodule_events = {
68
@@ -264,11 +251,20 @@
69
                avahi_strerror(avahi_client_errno(impl->client)));
70
        goto done;
71
    }
72
-   tinfo = TUNNEL_INFO(.interface = interface,
73
-           .protocol = protocol,
74
-           .name = name,
75
-           .type = type,
76
-           .domain = domain);
77
+
78
+   tinfo = TUNNEL_INFO(.name = name);
79
+
80
+   t = find_tunnel(impl, &tinfo);
81
+   if (t == NULL)
82
+       t = make_tunnel(impl, &tinfo);
83
+   if (t == NULL) {
84
+       pw_log_error("Can't make tunnel: %m");
85
+       goto done;
86
+   }
87
+   if (t->module != NULL) {
88
+       pw_log_info("found duplicate mdns entry - skipping tunnel creation");
89
+       goto done;
90
+   }
91
 
92
    props = pw_properties_new(NULL, NULL);
93
    if (props == NULL) {
94
@@ -346,8 +342,6 @@
95
    fprintf(f, "}");
96
         fclose(f);
97
 
98
-   pw_properties_free(props);
99
-
100
    pw_log_info("loading module args:'%s'", args);
101
    mod = pw_context_load_module(impl->context,
102
            "libpipewire-module-pulse-tunnel",
103
@@ -359,19 +353,13 @@
104
                 goto done;
105
    }
106
 
107
-   t = make_tunnel(impl, &tinfo);
108
-   if (t == NULL) {
109
-       pw_log_error("Can't make tunnel: %m");
110
-       pw_impl_module_destroy(mod);
111
-       goto done;
112
-   }
113
-
114
    pw_impl_module_add_listener(mod, &t->module_listener, &submodule_events, t);
115
 
116
    t->module = mod;
117
 
118
 done:
119
    avahi_service_resolver_free(r);
120
+   pw_properties_free(props);
121
 }
122
 
123
 
124
@@ -386,18 +374,16 @@
125
    if (flags & AVAHI_LOOKUP_RESULT_LOCAL)
126
        return;
127
 
128
-   info = TUNNEL_INFO(.interface = interface,
129
-           .protocol = protocol,
130
-           .name = name,
131
-           .type = type,
132
-           .domain = domain);
133
+   info = TUNNEL_INFO(.name = name);
134
 
135
    t = find_tunnel(impl, &info);
136
 
137
    switch (event) {
138
    case AVAHI_BROWSER_NEW:
139
-       if (t != NULL)
140
+       if (t != NULL) {
141
+           pw_log_info("found duplicate mdns entry - skipping tunnel creation");
142
            return;
143
+       }
144
        if (!(avahi_service_resolver_new(impl->client,
145
                        interface, protocol,
146
                        name, type, domain,
147
pipewire-0.3.71.tar.gz/src/modules/spa/module-node-factory.c -> pipewire-0.3.72.tar.gz/src/modules/spa/module-node-factory.c Changed
136
 
1
@@ -49,8 +49,100 @@
2
    struct pw_resource *resource;
3
    struct spa_hook resource_listener;
4
    unsigned int linger:1;
5
+
6
+   struct pw_core *core;
7
+   struct spa_hook core_listener;
8
+   struct spa_hook core_proxy_listener;
9
+   struct pw_proxy *proxy;
10
+   struct spa_hook proxy_listener;
11
+};
12
+
13
+static void proxy_removed(void *_data)
14
+{
15
+   struct node_data *nd = _data;
16
+   pw_log_debug("%p: removed", nd);
17
+   pw_proxy_destroy(nd->proxy);
18
+}
19
+
20
+static void proxy_destroy(void *_data)
21
+{
22
+   struct node_data *nd = _data;
23
+   pw_log_debug("%p: destroy", nd);
24
+   spa_hook_remove(&nd->proxy_listener);
25
+   nd->proxy = NULL;
26
+   if (nd->node)
27
+       pw_impl_node_destroy(nd->node);
28
+}
29
+
30
+static const struct pw_proxy_events proxy_events = {
31
+   PW_VERSION_PROXY_EVENTS,
32
+   .removed = proxy_removed,
33
+   .destroy = proxy_destroy,
34
 };
35
 
36
+static void core_error(void *data, uint32_t id, int seq, int res, const char *message)
37
+{
38
+   struct node_data *nd = data;
39
+
40
+   pw_log_error("error id:%u seq:%d res:%d (%s): %s",
41
+           id, seq, res, spa_strerror(res), message);
42
+
43
+   if (id == PW_ID_CORE && res == -EPIPE)
44
+       pw_impl_node_destroy(nd->node);
45
+}
46
+
47
+static const struct pw_core_events core_events = {
48
+   PW_VERSION_CORE_EVENTS,
49
+   .error = core_error,
50
+};
51
+
52
+static void core_removed(void *d)
53
+{
54
+   struct node_data *nd = d;
55
+   pw_log_debug("%p: removed", nd);
56
+   spa_hook_remove(&nd->core_proxy_listener);
57
+   spa_hook_remove(&nd->core_listener);
58
+   nd->core = NULL;
59
+   if (nd->node)
60
+       pw_impl_node_destroy(nd->node);
61
+}
62
+
63
+static const struct pw_proxy_events core_proxy_events = {
64
+   .removed = core_removed,
65
+};
66
+
67
+static int export_node(struct node_data *nd, struct pw_properties *props)
68
+{
69
+   const char *str;
70
+
71
+   str = pw_properties_get(props, PW_KEY_REMOTE_NAME);
72
+   nd->core = pw_context_connect(nd->data->context,
73
+           pw_properties_new(
74
+               PW_KEY_REMOTE_NAME, str,
75
+               NULL),
76
+           0);
77
+   if (nd->core == NULL) {
78
+       pw_log_error("can't connect: %m");
79
+       return -errno;
80
+   }
81
+   pw_proxy_add_listener((struct pw_proxy*)nd->core,
82
+           &nd->core_proxy_listener,
83
+           &core_proxy_events, nd);
84
+   pw_core_add_listener(nd->core,
85
+           &nd->core_listener,
86
+           &core_events, nd);
87
+
88
+   pw_log_debug("%p: export node %p", nd, nd->node);
89
+   nd->proxy = pw_core_export(nd->core,
90
+           PW_TYPE_INTERFACE_Node, NULL, nd->node, 0);
91
+   if (nd->proxy == NULL)
92
+       return -errno;
93
+
94
+   pw_proxy_add_listener(nd->proxy, &nd->proxy_listener, &proxy_events, nd);
95
+
96
+   return 0;
97
+}
98
+
99
 static void resource_destroy(void *data)
100
 {
101
    struct node_data *nd = data;
102
@@ -78,6 +170,10 @@
103
        spa_hook_remove(&nd->resource_listener);
104
        nd->resource = NULL;
105
    }
106
+   if (nd->core) {
107
+       pw_core_disconnect(nd->core);
108
+       nd->core = NULL;
109
+   }
110
 }
111
 
112
 static const struct pw_impl_node_events node_events = {
113
@@ -145,6 +241,11 @@
114
 
115
        pw_resource_add_listener(nd->resource, &nd->resource_listener, &resource_events, nd);
116
    }
117
+   if (pw_properties_get_bool(properties, PW_KEY_OBJECT_EXPORT, false)) {
118
+       res = export_node(nd, properties);
119
+       if (res < 0)
120
+           goto error_export;
121
+   }
122
    return node;
123
 
124
 error_properties:
125
@@ -160,6 +261,10 @@
126
    pw_resource_errorf_id(resource, new_id, res, "can't bind node");
127
    pw_impl_node_destroy(node);
128
    goto error_exit;
129
+error_export:
130
+   pw_resource_errorf_id(resource, new_id, res, "can't export node");
131
+   pw_impl_node_destroy(node);
132
+   goto error_exit;
133
 
134
 error_exit_cleanup:
135
    pw_properties_free(properties);
136
pipewire-0.3.71.tar.gz/src/pipewire/context.c -> pipewire-0.3.72.tar.gz/src/pipewire/context.c Changed
86
 
1
@@ -478,6 +478,61 @@
2
    spa_hook_list_append(&context->listener_list, listener, events, data);
3
 }
4
 
5
+struct listener_data {
6
+   struct spa_hook *listener;
7
+   const struct pw_context_driver_events *events;
8
+   void *data;
9
+};
10
+
11
+static int
12
+do_add_listener(struct spa_loop *loop,
13
+       bool async, uint32_t seq, const void *data, size_t size, void *user_data)
14
+{
15
+   struct pw_context *context = user_data;
16
+   const struct listener_data *d = data;
17
+   spa_hook_list_append(&context->driver_listener_list,
18
+           d->listener, d->events, d->data);
19
+   return 0;
20
+}
21
+
22
+SPA_EXPORT
23
+void pw_context_driver_add_listener(struct pw_context *context,
24
+             struct spa_hook *listener,
25
+             const struct pw_context_driver_events *events,
26
+             void *data)
27
+{
28
+   struct listener_data d = {
29
+       .listener = listener,
30
+       .events = events,
31
+       .data = data };
32
+   struct pw_impl_node *n;
33
+   spa_list_for_each(n, &context->driver_list, driver_link) {
34
+       SPA_FLAG_SET(n->rt.target.activation->flags, PW_NODE_ACTIVATION_FLAG_PROFILER);
35
+   }
36
+   pw_loop_invoke(context->data_loop,
37
+                       do_add_listener, SPA_ID_INVALID, &d, sizeof(d), false, context);
38
+}
39
+
40
+static int do_remove_listener(struct spa_loop *loop,
41
+       bool async, uint32_t seq, const void *data, size_t size, void *user_data)
42
+{
43
+   struct spa_hook *listener = user_data;
44
+   spa_hook_remove(listener);
45
+   return 0;
46
+}
47
+
48
+SPA_EXPORT
49
+void pw_context_driver_remove_listener(struct pw_context *context,
50
+             struct spa_hook *listener)
51
+{
52
+   struct pw_impl_node *n;
53
+   spa_list_for_each(n, &context->driver_list, driver_link) {
54
+       SPA_FLAG_CLEAR(n->rt.target.activation->flags, PW_NODE_ACTIVATION_FLAG_PROFILER);
55
+   }
56
+   pw_loop_invoke(context->data_loop,
57
+                       do_remove_listener, SPA_ID_INVALID, NULL, 0, true, listener);
58
+}
59
+
60
 SPA_EXPORT
61
 const struct spa_support *pw_context_get_support(struct pw_context *context, uint32_t *n_support)
62
 {
63
@@ -1190,7 +1245,7 @@
64
    /* clean up the flags first */
65
    spa_list_for_each(n, &context->node_list, link) {
66
        n->visited = false;
67
-       n->runnable = n->always_process;
68
+       n->runnable = n->always_process && n->active;
69
    }
70
 
71
    get_quantums(context, &def_quantum, &min_quantum, &max_quantum, &lim_quantum, &rate_quantum);
72
@@ -1213,9 +1268,10 @@
73
            collect_nodes(context, n, &collect);
74
            move_to_driver(context, &collect, n);
75
        }
76
-       /* from now on we are only interested in active driving nodes.
77
-        * We're going to see if there are active followers. */
78
-       if (!n->driving || !n->active)
79
+       /* from now on we are only interested in active driving nodes
80
+        * with a driver_priority. We're going to see if there are
81
+        * active followers. */
82
+       if (!n->driving || !n->active || n->priority_driver <= 0)
83
            continue;
84
 
85
        /* first active driving node is fallback */
86
pipewire-0.3.71.tar.gz/src/pipewire/core.c -> pipewire-0.3.72.tar.gz/src/pipewire/core.c Changed
13
 
1
@@ -479,7 +479,9 @@
2
 int pw_core_disconnect(struct pw_core *core)
3
 {
4
    pw_log_debug("%p: disconnect", core);
5
-   pw_proxy_remove(&core->proxy);
6
-   pw_proxy_destroy(&core->proxy);
7
+   if (!core->removed)
8
+       pw_proxy_remove(&core->proxy);
9
+   if (!core->destroyed)
10
+       pw_proxy_destroy(&core->proxy);
11
    return 0;
12
 }
13
pipewire-0.3.71.tar.gz/src/pipewire/extensions/client-node.h -> pipewire-0.3.72.tar.gz/src/pipewire/extensions/client-node.h Changed
10
 
1
@@ -22,7 +22,7 @@
2
  */
3
 #define PW_TYPE_INTERFACE_ClientNode       PW_TYPE_INFO_INTERFACE_BASE "ClientNode"
4
 
5
-#define PW_VERSION_CLIENT_NODE         4
6
+#define PW_VERSION_CLIENT_NODE         5
7
 struct pw_client_node;
8
 
9
 #define PW_EXTENSION_MODULE_CLIENT_NODE        PIPEWIRE_MODULE_PREFIX "module-client-node"
10
pipewire-0.3.71.tar.gz/src/pipewire/filter.c -> pipewire-0.3.72.tar.gz/src/pipewire/filter.c Changed
201
 
1
@@ -151,6 +151,8 @@
2
    unsigned int warn_mlock:1;
3
    unsigned int process_rt:1;
4
    unsigned int driving:1;
5
+   unsigned int trigger:1;
6
+   int in_emit_param_changed;
7
 };
8
 
9
 static int get_param_index(uint32_t id)
10
@@ -275,6 +277,8 @@
11
 {
12
    struct param *p, *t;
13
    struct spa_list *param_list;
14
+   bool found = false;
15
+   int i, idx;
16
 
17
    if (port)
18
        param_list = &port->param_list;
19
@@ -284,10 +288,42 @@
20
    spa_list_for_each_safe(p, t, param_list, link) {
21
        if (id == SPA_ID_INVALID ||
22
            (p->id == id && !(p->flags & PARAM_FLAG_LOCKED))) {
23
+           found = true;
24
            spa_list_remove(&p->link);
25
            free(p);
26
        }
27
    }
28
+   if (found) {
29
+       if (id == SPA_ID_INVALID) {
30
+           if (port) {
31
+               port->info.change_mask |= SPA_PORT_CHANGE_MASK_PARAMS;
32
+               for (i = 0; i < N_PORT_PARAMS; i++) {
33
+                   port->paramsi.flags &= ~SPA_PARAM_INFO_READ;
34
+                   port->paramsi.user++;
35
+               }
36
+           } else {
37
+               impl->info.change_mask |= SPA_NODE_CHANGE_MASK_PARAMS;
38
+               for (i = 0; i < N_NODE_PARAMS; i++) {
39
+                   impl->paramsi.flags &= ~SPA_PARAM_INFO_READ;
40
+                   impl->paramsi.user++;
41
+               }
42
+           }
43
+       } else {
44
+           if (port) {
45
+               if ((idx = get_port_param_index(id)) != -1) {
46
+                   port->info.change_mask |= SPA_PORT_CHANGE_MASK_PARAMS;
47
+                   port->paramsidx.flags &= ~SPA_PARAM_INFO_READ;
48
+                   port->paramsidx.user++;
49
+               }
50
+           } else {
51
+               if ((idx = get_param_index(id)) != -1) {
52
+                   impl->info.change_mask |= SPA_NODE_CHANGE_MASK_PARAMS;
53
+                   impl->paramsidx.flags &= ~SPA_PARAM_INFO_READ;
54
+                   impl->paramsidx.user++;
55
+               }
56
+           }
57
+       }
58
+   }
59
 }
60
 
61
 static struct port *alloc_port(struct filter *filter,
62
@@ -433,12 +469,19 @@
63
    return enum_params(impl, &impl->param_list, seq, id, start, num, filter);
64
 }
65
 
66
-static int impl_set_param(void *object, uint32_t id, uint32_t flags, const struct spa_pod *param)
67
+static inline void emit_param_changed(struct filter *impl, void *port,
68
+       uint32_t id, const struct spa_pod *param)
69
 {
70
-   struct filter *impl = object;
71
    struct pw_filter *filter = &impl->this;
72
+   if (impl->in_emit_param_changed++ == 0)
73
+       pw_filter_emit_param_changed(filter, port, id, param);
74
+   impl->in_emit_param_changed--;
75
+}
76
 
77
-   pw_filter_emit_param_changed(filter, NULL, id, param);
78
+static int impl_set_param(void *object, uint32_t id, uint32_t flags, const struct spa_pod *param)
79
+{
80
+   struct filter *impl = object;
81
+   emit_param_changed(impl, NULL, id, param);
82
    return 0;
83
 }
84
 
85
@@ -776,7 +819,6 @@
86
 
87
 static int handle_latency(struct filter *impl, struct port *port, const struct spa_pod *param)
88
 {
89
-   struct pw_filter *filter = &impl->this;
90
    struct spa_latency_info info;
91
    int res;
92
 
93
@@ -796,7 +838,7 @@
94
        return 0;
95
 
96
    if (SPA_FLAG_IS_SET(impl->flags, PW_FILTER_FLAG_CUSTOM_LATENCY)) {
97
-       pw_filter_emit_param_changed(filter, port->user_data,
98
+       emit_param_changed(impl, port->user_data,
99
                SPA_PARAM_Latency, param);
100
    } else {
101
        default_latency(impl, port, info.direction);
102
@@ -848,7 +890,7 @@
103
    }
104
 
105
    if (emit)
106
-       pw_filter_emit_param_changed(filter, port->user_data, id, param);
107
+       emit_param_changed(impl, port->user_data, id, param);
108
 
109
    if (filter->state == PW_FILTER_STATE_ERROR)
110
        return filter->error_res;
111
@@ -966,12 +1008,12 @@
112
 
113
 static void call_process(struct filter *impl)
114
 {
115
-   pw_log_trace("%p: call process", impl);
116
+   pw_log_trace_fp("%p: call process", impl);
117
    if (SPA_FLAG_IS_SET(impl->flags, PW_FILTER_FLAG_RT_PROCESS)) {
118
-       spa_callbacks_call(&impl->rt_callbacks, struct pw_filter_events,
119
-               process, 0, impl->rt.position);
120
-   }
121
-   else {
122
+       if (impl->rt_callbacks.funcs)
123
+           spa_callbacks_call_fast(&impl->rt_callbacks, struct pw_filter_events,
124
+                   process, 0, impl->rt.position);
125
+   } else {
126
        pw_loop_invoke(impl->main_loop,
127
            do_call_process, 1, NULL, 0, false, impl);
128
    }
129
@@ -1000,6 +1042,7 @@
130
    struct port *p;
131
    struct buffer *b;
132
    bool drained = true;
133
+   int res = 0;
134
 
135
    pw_log_trace_fp("%p: do process %p", impl, impl->rt.position);
136
 
137
@@ -1041,6 +1084,7 @@
138
            continue;
139
 
140
        if (p->direction == SPA_DIRECTION_INPUT) {
141
+           res |= SPA_STATUS_NEED_DATA;
142
            if (SPA_UNLIKELY(io->status != SPA_STATUS_HAVE_DATA))
143
                continue;
144
 
145
@@ -1053,17 +1097,21 @@
146
            }
147
            io->status = SPA_STATUS_NEED_DATA;
148
        } else {
149
-           if (SPA_UNLIKELY(io->status == SPA_STATUS_HAVE_DATA))
150
+           if (SPA_UNLIKELY(io->status == SPA_STATUS_HAVE_DATA)) {
151
+               res |= SPA_STATUS_HAVE_DATA;
152
                continue;
153
+           }
154
 
155
            if ((b = pop_queue(p, &p->queued)) != NULL) {
156
                pw_log_trace_fp("%p: pop %d %p", impl, b->id, io);
157
                io->buffer_id = b->id;
158
                io->status = SPA_STATUS_HAVE_DATA;
159
+               res |= SPA_STATUS_HAVE_DATA;
160
                drained = false;
161
            } else {
162
                io->buffer_id = SPA_ID_INVALID;
163
                io->status = SPA_STATUS_NEED_DATA;
164
+               res |= SPA_STATUS_NEED_DATA;
165
            }
166
        }
167
    }
168
@@ -1071,7 +1119,7 @@
169
    if (SPA_UNLIKELY(drained && impl->draining))
170
        call_drained(impl);
171
 
172
-   return SPA_STATUS_NEED_DATA | SPA_STATUS_HAVE_DATA;
173
+   return res;
174
 }
175
 
176
 static const struct spa_node_methods impl_node = {
177
@@ -1429,10 +1477,22 @@
178
    free(impl);
179
 }
180
 
181
+static int
182
+do_remove_callbacks(struct spa_loop *loop,
183
+                 bool async, uint32_t seq, const void *data, size_t size, void *user_data)
184
+{
185
+   struct filter *impl = user_data;
186
+   spa_zero(impl->rt_callbacks);
187
+   return 0;
188
+}
189
+
190
 static void hook_removed(struct spa_hook *hook)
191
 {
192
    struct filter *impl = hook->priv;
193
-   spa_zero(impl->rt_callbacks);
194
+   if (impl->data_loop)
195
+       pw_loop_invoke(impl->data_loop, do_remove_callbacks, 1, NULL, 0, true, impl);
196
+   else
197
+       spa_zero(impl->rt_callbacks);
198
    hook->priv = NULL;
199
    hook->removed = NULL;
200
 }
201
pipewire-0.3.71.tar.gz/src/pipewire/filter.h -> pipewire-0.3.72.tar.gz/src/pipewire/filter.h Changed
13
 
1
@@ -104,6 +104,11 @@
2
    PW_FILTER_FLAG_CUSTOM_LATENCY   = (1 << 3), /**< don't call the default latency algorithm
3
                              *  but emit the param_changed event for the
4
                              *  ports when Latency params are received. */
5
+   PW_FILTER_FLAG_TRIGGER      = (1 << 4), /**< the filter will not be scheduled
6
+                             *  automatically but _trigger_process()
7
+                             *  needs to be called. This can be used
8
+                             *  when the filter depends on processing
9
+                             *  of other filters. */
10
 };
11
 
12
 enum pw_filter_port_flags {
13
pipewire-0.3.71.tar.gz/src/pipewire/impl-link.c -> pipewire-0.3.72.tar.gz/src/pipewire/impl-link.c Changed
192
 
1
@@ -27,7 +27,6 @@
2
 struct impl {
3
    struct pw_impl_link this;
4
 
5
-   unsigned int io_set:1;
6
    unsigned int activated:1;
7
 
8
    struct pw_work_queue *work;
9
@@ -57,7 +56,7 @@
10
    struct pw_node_peer *peer;
11
 
12
    spa_list_for_each(peer, &onode->peer_list, link) {
13
-       if (peer->target.node == inode) {
14
+       if (peer->target.id == inode->info.id) {
15
            pw_log_debug("exiting peer %p from %p to %p", peer, onode, inode);
16
            peer->ref++;
17
            return peer;
18
@@ -70,10 +69,8 @@
19
    peer->ref = 1;
20
    peer->output = onode;
21
    peer->active_count = 0;
22
-   peer->target.node = inode;
23
-   peer->target.activation = inode->rt.activation;
24
-   peer->target.system = inode->data_system;
25
-   peer->target.fd = inode->source.fd;
26
+   copy_target(&peer->target, &inode->rt.target);
27
+   peer->target.flags = PW_NODE_TARGET_PEER;
28
 
29
    spa_list_append(&onode->peer_list, &peer->link);
30
    pw_log_debug("new peer %p from %p to %p", peer, onode, inode);
31
@@ -105,8 +102,8 @@
32
            peer->target.active = true;
33
        }
34
    }
35
-   pw_log_trace("%p: node:%p state:%p pending:%d/%d", peer->output,
36
-           peer->target.node, state, state->pending, state->required);
37
+   pw_log_trace("%p: node:%s state:%p pending:%d/%d", peer->output,
38
+           peer->target.name, state, state->pending, state->required);
39
 }
40
 
41
 static void pw_node_peer_deactivate(struct pw_node_peer *peer)
42
@@ -122,8 +119,8 @@
43
            peer->target.active = false;
44
        }
45
    }
46
-   pw_log_trace("%p: node:%p state:%p pending:%d/%d", peer->output,
47
-           peer->target.node, state, state->pending, state->required);
48
+   pw_log_trace("%p: node:%s state:%p pending:%d/%d", peer->output,
49
+           peer->target.name, state, state->pending, state->required);
50
 }
51
 
52
 
53
@@ -160,12 +157,16 @@
54
             pw_link_state_as_string(state), error);
55
 
56
    if (state == PW_LINK_STATE_ERROR) {
57
-       pw_log_error("(%s) %s -> error (%s)", link->name,
58
-               pw_link_state_as_string(old), error);
59
+       pw_log_error("(%s) %s -> error (%s) (%s-%s)", link->name,
60
+               pw_link_state_as_string(old), error,
61
+               pw_impl_port_state_as_string(link->output->state),
62
+               pw_impl_port_state_as_string(link->input->state));
63
    } else {
64
-       pw_log_info("(%s) %s -> %s", link->name,
65
+       pw_log_info("(%s) %s -> %s (%s-%s)", link->name,
66
                pw_link_state_as_string(old),
67
-               pw_link_state_as_string(state));
68
+               pw_link_state_as_string(state),
69
+               pw_impl_port_state_as_string(link->output->state),
70
+               pw_impl_port_state_as_string(link->input->state));
71
    }
72
 
73
    pw_impl_link_emit_state_changed(link, old, state, error);
74
@@ -485,7 +486,6 @@
75
 {
76
    int res = 0;
77
 
78
-   mix->io = data;
79
    pw_log_debug("%p: %s port %p %d.%d set io: %d %p %zd", this,
80
            pw_direction_as_string(port->direction),
81
            port, port->port_id, mix->port.port_id, id, data, size);
82
@@ -641,15 +641,9 @@
83
         bool async, uint32_t seq, const void *data, size_t size, void *user_data)
84
 {
85
    struct pw_impl_link *this = user_data;
86
-
87
    pw_log_trace("%p: activate", this);
88
-
89
-   spa_list_append(&this->output->rt.mix_list, &this->rt.out_mix.rt_link);
90
-   spa_list_append(&this->input->rt.mix_list, &this->rt.in_mix.rt_link);
91
-
92
    if (this->peer)
93
        pw_node_peer_activate(this->peer);
94
-
95
    return 0;
96
 }
97
 
98
@@ -661,20 +655,21 @@
99
    pw_log_debug("%p: activate activated:%d state:%s", this, impl->activated,
100
            pw_link_state_as_string(this->info.state));
101
 
102
-   if (impl->activated || !this->prepared ||
103
+   if (this->destroyed || impl->activated || !this->prepared ||
104
        !impl->inode->runnable || !impl->onode->runnable)
105
        return 0;
106
 
107
-   if (!impl->io_set) {
108
-       if ((res = port_set_io(this, this->output, SPA_IO_Buffers, this->io,
109
-               sizeof(struct spa_io_buffers), &this->rt.out_mix)) < 0)
110
-           return res;
111
+   if ((res = port_set_io(this, this->input, SPA_IO_Buffers, this->io,
112
+           sizeof(struct spa_io_buffers), &this->rt.in_mix)) < 0)
113
+       return res;
114
 
115
-       if ((res = port_set_io(this, this->input, SPA_IO_Buffers, this->io,
116
-               sizeof(struct spa_io_buffers), &this->rt.in_mix)) < 0)
117
-           return res;
118
-       impl->io_set = true;
119
+   if ((res = port_set_io(this, this->output, SPA_IO_Buffers, this->io,
120
+           sizeof(struct spa_io_buffers), &this->rt.out_mix)) < 0) {
121
+       port_set_io(this, this->input, SPA_IO_Buffers, NULL, 0,
122
+               &this->rt.in_mix);
123
+       return res;
124
    }
125
+
126
    pw_loop_invoke(this->output->node->data_loop,
127
           do_activate_link, SPA_ID_INVALID, NULL, 0, false, this);
128
 
129
@@ -828,7 +823,7 @@
130
    if (!impl->inode->active || !impl->onode->active)
131
        return 0;
132
 
133
-   if (this->preparing || this->prepared)
134
+   if (this->destroyed || this->preparing || this->prepared)
135
        return 0;
136
 
137
    this->preparing = true;
138
@@ -844,15 +839,9 @@
139
           bool async, uint32_t seq, const void *data, size_t size, void *user_data)
140
 {
141
         struct pw_impl_link *this = user_data;
142
-
143
-   pw_log_trace("%p: disable %p and %p", this, &this->rt.in_mix, &this->rt.out_mix);
144
-
145
-   spa_list_remove(&this->rt.out_mix.rt_link);
146
-   spa_list_remove(&this->rt.in_mix.rt_link);
147
-
148
+   pw_log_trace("%p: disable out %p", this, &this->rt.out_mix);
149
    if (this->peer)
150
        pw_node_peer_deactivate(this->peer);
151
-
152
    return 0;
153
 }
154
 
155
@@ -873,11 +862,11 @@
156
    port_set_io(this, this->input, SPA_IO_Buffers, NULL, 0,
157
            &this->rt.in_mix);
158
 
159
-   impl->io_set = false;
160
    impl->activated = false;
161
    pw_log_info("(%s) deactivated", this->name);
162
-   link_update_state(this, PW_LINK_STATE_PAUSED, 0, NULL);
163
-
164
+   link_update_state(this, this->destroyed ?
165
+           PW_LINK_STATE_INIT : PW_LINK_STATE_PAUSED,
166
+           0, NULL);
167
    return 0;
168
 }
169
 
170
@@ -1306,9 +1295,9 @@
171
             output_node, output->port_id, this->rt.out_mix.port.port_id,
172
             input_node, input->port_id, this->rt.in_mix.port.port_id);
173
 
174
-   this->name = spa_aprintf("%d.%d -> %d.%d",
175
-           output_node->info.id, output->port_id,
176
-           input_node->info.id, input->port_id);
177
+   this->name = spa_aprintf("%d.%d.%d -> %d.%d.%d",
178
+           output_node->info.id, output->port_id, this->rt.out_mix.port.port_id,
179
+           input_node->info.id, input->port_id, this->rt.in_mix.port.port_id);
180
    pw_log_info("(%s) (%s) -> (%s)", this->name, output_node->name, input_node->name);
181
 
182
    pw_impl_port_emit_link_added(output, this);
183
@@ -1448,6 +1437,8 @@
184
 
185
    pw_log_debug("%p: destroy", impl);
186
    pw_log_info("(%s) destroy", link->name);
187
+
188
+   link->destroyed = true;
189
    pw_impl_link_emit_destroy(link);
190
 
191
    pw_impl_link_deactivate(link);
192
pipewire-0.3.71.tar.gz/src/pipewire/impl-node.c -> pipewire-0.3.72.tar.gz/src/pipewire/impl-node.c Changed
201
 
1
@@ -41,8 +41,6 @@
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
@@ -89,21 +87,18 @@
11
        return;
12
 
13
    pw_log_trace("%p: add to driver %p %p %p", this, driver,
14
-           driver->rt.activation, this->rt.activation);
15
+           driver->rt.target.activation, this->rt.target.activation);
16
 
17
    /* let the driver trigger us as part of the processing cycle */
18
    spa_list_append(&driver->rt.target_list, &this->rt.target.link);
19
-   nstate = &this->rt.activation->state0;
20
+   nstate = &this->rt.target.activation->state0;
21
    if (!this->rt.target.active) {
22
        nstate->required++;
23
        this->rt.target.active = true;
24
    }
25
 
26
    /* trigger the driver when we complete */
27
-   this->rt.driver_target.activation = driver->rt.activation;
28
-   this->rt.driver_target.node = driver;
29
-   this->rt.driver_target.system = driver->data_system;
30
-   this->rt.driver_target.fd = driver->source.fd;
31
+   copy_target(&this->rt.driver_target, &driver->rt.target);
32
    spa_list_append(&this->rt.target_list, &this->rt.driver_target.link);
33
 
34
    /* now increment the required states of all this node targets, including
35
@@ -129,13 +124,13 @@
36
    if (this->exported)
37
        return;
38
 
39
-   pw_log_trace("%p: remove from driver %p %p %p",
40
-           this, this->rt.driver_target.node,
41
-           this->rt.driver_target.activation, this->rt.activation);
42
+   pw_log_trace("%p: remove from driver %s %p %p",
43
+           this, this->rt.driver_target.name,
44
+           this->rt.driver_target.activation, this->rt.target.activation);
45
 
46
    spa_list_remove(&this->rt.target.link);
47
 
48
-   nstate = &this->rt.activation->state0;
49
+   nstate = &this->rt.target.activation->state0;
50
    if (this->rt.target.active) {
51
        nstate->required--;
52
        this->rt.target.active = false;
53
@@ -153,7 +148,7 @@
54
    }
55
    spa_list_remove(&this->rt.driver_target.link);
56
 
57
-   this->rt.driver_target.node = NULL;
58
+   spa_zero(this->rt.driver_target);
59
 }
60
 
61
 static int
62
@@ -707,27 +702,31 @@
63
 
64
 static void update_io(struct pw_impl_node *node)
65
 {
66
+   struct pw_node_target *t = &node->rt.target;
67
+
68
    pw_log_debug("%p: id:%d", node, node->info.id);
69
 
70
    if (spa_node_set_io(node->node,
71
                SPA_IO_Position,
72
-               &node->rt.activation->position,
73
+               &t->activation->position,
74
                sizeof(struct spa_io_position)) >= 0) {
75
-       pw_log_debug("%p: set position %p", node, &node->rt.activation->position);
76
-       node->rt.position = &node->rt.activation->position;
77
+       pw_log_debug("%p: set position %p", node, &t->activation->position);
78
+       node->rt.position = &t->activation->position;
79
 
80
        node->target_rate = node->rt.position->clock.target_rate;
81
        node->target_quantum = node->rt.position->clock.target_duration;
82
        node->target_pending = false;
83
+
84
+       pw_impl_node_emit_peer_added(node, node);
85
    } else if (node->driver) {
86
        pw_log_warn("%p: can't set position on driver", node);
87
    }
88
    if (spa_node_set_io(node->node,
89
                SPA_IO_Clock,
90
-               &node->rt.activation->position.clock,
91
+               &t->activation->position.clock,
92
                sizeof(struct spa_io_clock)) >= 0) {
93
-       pw_log_debug("%p: set clock %p", node, &node->rt.activation->position.clock);
94
-       node->rt.clock = &node->rt.activation->position.clock;
95
+       pw_log_debug("%p: set clock %p", node, &t->activation->position.clock);
96
+       node->rt.clock = &t->activation->position.clock;
97
    }
98
 }
99
 
100
@@ -778,9 +777,10 @@
101
        insert_driver(context, this);
102
    this->registered = true;
103
 
104
-   this->rt.activation->position.clock.id = this->global->id;
105
+   this->rt.target.activation->position.clock.id = this->global->id;
106
 
107
    this->info.id = this->global->id;
108
+   this->rt.target.id = this->info.id;
109
    pw_properties_setf(this->properties, PW_KEY_OBJECT_ID, "%d", this->info.id);
110
    pw_properties_setf(this->properties, PW_KEY_OBJECT_SERIAL, "%"PRIu64,
111
            pw_global_get_serial(this->global));
112
@@ -830,8 +830,8 @@
113
 
114
    pw_log_trace("%p: driver:%p->%p", node, node->driver_node, driver);
115
 
116
-   pw_log_trace("%p: set position %p", node, &driver->rt.activation->position);
117
-   node->rt.position = &driver->rt.activation->position;
118
+   pw_log_trace("%p: set position %p", node, &driver->rt.target.activation->position);
119
+   node->rt.position = &driver->rt.target.activation->position;
120
 
121
    node->target_rate = node->rt.position->clock.target_rate;
122
    node->target_quantum = node->rt.position->clock.target_duration;
123
@@ -845,7 +845,7 @@
124
 
125
 static void remove_segment_owner(struct pw_impl_node *driver, uint32_t node_id)
126
 {
127
-   struct pw_node_activation *a = driver->rt.activation;
128
+   struct pw_node_activation *a = driver->rt.target.activation;
129
    ATOMIC_CAS(a->segment_owner0, node_id, 0);
130
    ATOMIC_CAS(a->segment_owner1, node_id, 0);
131
 }
132
@@ -890,7 +890,7 @@
133
 
134
    if ((res = spa_node_set_io(node->node,
135
            SPA_IO_Position,
136
-           &driver->rt.activation->position,
137
+           &driver->rt.target.activation->position,
138
            sizeof(struct spa_io_position))) < 0) {
139
        pw_log_debug("%p: set position: %s", node, spa_strerror(res));
140
    }
141
@@ -901,6 +901,9 @@
142
 
143
    pw_impl_node_emit_driver_changed(node, old, driver);
144
 
145
+   pw_impl_node_emit_peer_added(driver, node);
146
+   pw_impl_node_emit_peer_removed(old, node);
147
+
148
    return 0;
149
 }
150
 
151
@@ -922,6 +925,7 @@
152
        (node->name == NULL || !spa_streq(node->name, str))) {
153
        free(node->name);
154
        node->name = strdup(str);
155
+       snprintf(node->rt.target.name, sizeof(node->rt.target.name), "%s", node->name);
156
        pw_log_debug("%p: name '%s'", node, node->name);
157
    }
158
 
159
@@ -950,9 +954,9 @@
160
    if (trigger != node->trigger) {
161
        node->trigger = trigger;
162
        if (trigger)
163
-           node->rt.activation->state0.required++;
164
+           node->rt.target.activation->state0.required++;
165
        else
166
-           node->rt.activation->state0.required--;
167
+           node->rt.target.activation->state0.required--;
168
    }
169
 
170
    /* group defines what nodes are scheduled together */
171
@@ -1077,27 +1081,40 @@
172
    return "unknown";
173
 }
174
 
175
-static void dump_states(struct pw_impl_node *driver)
176
+static void update_xrun_stats(struct pw_node_activation *a, uint64_t trigger, uint64_t delay)
177
+{
178
+   a->xrun_count++;
179
+   a->xrun_time = trigger;
180
+   a->xrun_delay = delay;
181
+   a->max_delay = SPA_MAX(a->max_delay, delay);
182
+}
183
+
184
+static void check_states(struct pw_impl_node *driver, uint64_t nsec)
185
 {
186
    struct pw_node_target *t;
187
-   struct pw_node_activation *na = driver->rt.activation;
188
+   struct pw_node_activation *na = driver->rt.target.activation;
189
    struct spa_io_clock *cl = &na->position.clock;
190
+   enum spa_log_level level = SPA_LOG_LEVEL_DEBUG;
191
+
192
+   if (ratelimit_test(&driver->rt.rate_limit, nsec, SPA_LOG_LEVEL_DEBUG))
193
+       level = SPA_LOG_LEVEL_INFO;
194
 
195
    spa_list_for_each(t, &driver->rt.target_list, link) {
196
        struct pw_node_activation *a = t->activation;
197
        struct pw_node_activation_state *state = &a->state0;
198
-       if (t->node == NULL)
199
-           continue;
200
+
201
pipewire-0.3.71.tar.gz/src/pipewire/impl-port.c -> pipewire-0.3.72.tar.gz/src/pipewire/impl-port.c Changed
201
 
1
@@ -27,6 +27,7 @@
2
 struct impl {
3
    struct pw_impl_port this;
4
    struct spa_node mix_node;   /**< mix node implementation */
5
+   struct spa_list mix_list;
6
 
7
    struct spa_list param_list;
8
    struct spa_list pending_list;
9
@@ -69,7 +70,7 @@
10
    port->info.change_mask = 0;
11
 }
12
 
13
-static const char *port_state_as_string(enum pw_impl_port_state state)
14
+const char *pw_impl_port_state_as_string(enum pw_impl_port_state state)
15
 {
16
    switch (state) {
17
    case PW_IMPL_PORT_STATE_ERROR:
18
@@ -100,7 +101,8 @@
19
    pw_log(state == PW_IMPL_PORT_STATE_ERROR ?
20
            SPA_LOG_LEVEL_ERROR : SPA_LOG_LEVEL_DEBUG,
21
        "%p: state %s -> %s (%s)", port,
22
-       port_state_as_string(old), port_state_as_string(state), error);
23
+       pw_impl_port_state_as_string(old),
24
+       pw_impl_port_state_as_string(state), error);
25
 
26
    pw_impl_port_emit_state_changed(port, old, state, error);
27
 
28
@@ -111,6 +113,72 @@
29
    }
30
 }
31
 
32
+static struct pw_impl_port_mix *find_mix(struct pw_impl_port *port,
33
+       enum spa_direction direction, uint32_t port_id)
34
+{
35
+   struct pw_impl_port_mix *mix;
36
+   spa_list_for_each(mix, &port->mix_list, link) {
37
+       if (mix->port.direction == direction && mix->port.port_id == port_id)
38
+           return mix;
39
+   }
40
+   return NULL;
41
+}
42
+
43
+static int
44
+do_add_mix(struct spa_loop *loop,
45
+        bool async, uint32_t seq, const void *data, size_t size, void *user_data)
46
+{
47
+   struct pw_impl_port_mix *mix = user_data;
48
+   struct pw_impl_port *this = mix->p;
49
+   struct impl *impl = SPA_CONTAINER_OF(this, struct impl, this);
50
+   pw_log_trace("%p: add mix %p", this, mix);
51
+   if (!mix->active) {
52
+       spa_list_append(&impl->mix_list, &mix->rt_link);
53
+       mix->active = true;
54
+   }
55
+   return 0;
56
+}
57
+
58
+static int
59
+do_remove_mix(struct spa_loop *loop,
60
+        bool async, uint32_t seq, const void *data, size_t size, void *user_data)
61
+{
62
+   struct pw_impl_port_mix *mix = user_data;
63
+   struct pw_impl_port *this = mix->p;
64
+   pw_log_trace("%p: remove mix %p", this, mix);
65
+   if (mix->active) {
66
+       spa_list_remove(&mix->rt_link);
67
+       mix->active = false;
68
+   }
69
+   return 0;
70
+}
71
+
72
+static int port_set_io(void *object,
73
+       enum spa_direction direction, uint32_t port_id, uint32_t id,
74
+       void *data, size_t size)
75
+{
76
+   struct impl *impl = object;
77
+   struct pw_impl_port *this = &impl->this;
78
+   struct pw_impl_port_mix *mix;
79
+
80
+   mix = find_mix(this, direction, port_id);
81
+   if (mix == NULL)
82
+       return -ENOENT;
83
+
84
+   if (id == SPA_IO_Buffers) {
85
+       if (data == NULL || size == 0) {
86
+           pw_loop_invoke(this->node->data_loop,
87
+                  do_remove_mix, SPA_ID_INVALID, NULL, 0, true, mix);
88
+           mix->io = NULL;
89
+       } else if (data != NULL && size >= sizeof(struct spa_io_buffers)) {
90
+           mix->io = data;
91
+           pw_loop_invoke(this->node->data_loop,
92
+                  do_add_mix, SPA_ID_INVALID, NULL, 0, false, mix);
93
+       }
94
+   }
95
+   return 0;
96
+}
97
+
98
 static int tee_process(void *object)
99
 {
100
    struct impl *impl = object;
101
@@ -119,7 +187,7 @@
102
    struct spa_io_buffers *io = &this->rt.io;
103
 
104
    pw_log_trace_fp("%p: tee input %d %d", this, io->status, io->buffer_id);
105
-   spa_list_for_each(mix, &this->rt.mix_list, rt_link) {
106
+   spa_list_for_each(mix, &impl->mix_list, rt_link) {
107
        pw_log_trace_fp("%p: port %d %p->%p %d", this,
108
                mix->port.port_id, io, mix->io, mix->io->buffer_id);
109
        *mix->io = *io;
110
@@ -136,13 +204,13 @@
111
 
112
    pw_log_trace_fp("%p: tee reuse buffer %d %d", this, port_id, buffer_id);
113
    spa_node_port_reuse_buffer(this->node->node, this->port_id, buffer_id);
114
-
115
    return 0;
116
 }
117
 
118
 static const struct spa_node_methods schedule_tee_node = {
119
    SPA_VERSION_NODE_METHODS,
120
    .process = tee_process,
121
+   .port_set_io = port_set_io,
122
    .port_reuse_buffer = tee_reuse_buffer,
123
 };
124
 
125
@@ -156,7 +224,7 @@
126
    if (SPA_UNLIKELY(PW_IMPL_PORT_IS_CONTROL(this)))
127
        return SPA_STATUS_HAVE_DATA | SPA_STATUS_NEED_DATA;
128
 
129
-   spa_list_for_each(mix, &this->rt.mix_list, rt_link) {
130
+   spa_list_for_each(mix, &impl->mix_list, rt_link) {
131
        pw_log_trace_fp("%p: mix input %d %p->%p %d %d", this,
132
                mix->port.port_id, mix->io, io, mix->io->status, mix->io->buffer_id);
133
        *io = *mix->io;
134
@@ -169,11 +237,10 @@
135
 static int schedule_mix_reuse_buffer(void *object, uint32_t port_id, uint32_t buffer_id)
136
 {
137
    struct impl *impl = object;
138
-   struct pw_impl_port *this = &impl->this;
139
    struct pw_impl_port_mix *mix;
140
 
141
-   spa_list_for_each(mix, &this->rt.mix_list, rt_link) {
142
-       pw_log_trace_fp("%p: reuse buffer %d %d", this, port_id, buffer_id);
143
+   spa_list_for_each(mix, &impl->mix_list, rt_link) {
144
+       pw_log_trace_fp("%p: reuse buffer %d %d", impl, port_id, buffer_id);
145
        /* FIXME send reuse buffer to peer */
146
        break;
147
    }
148
@@ -183,6 +250,7 @@
149
 static const struct spa_node_methods schedule_mix_node = {
150
    SPA_VERSION_NODE_METHODS,
151
    .process = schedule_mix_input,
152
+   .port_set_io = port_set_io,
153
    .port_reuse_buffer = schedule_mix_reuse_buffer,
154
 };
155
 
156
@@ -263,6 +331,9 @@
157
 
158
    res = pw_impl_port_call_release_mix(port, mix);
159
 
160
+   if (port->destroying)
161
+       return res;
162
+
163
    if ((res = spa_node_remove_port(port->mix, port->direction, port_id)) < 0 &&
164
        res != -ENOTSUP)
165
        pw_log_warn("can't remove mix port %d: %s", port_id, spa_strerror(res));
166
@@ -276,6 +347,8 @@
167
                     port->direction, port->port_id,
168
                     SPA_IO_Buffers,
169
                     NULL, sizeof(port->rt.io));
170
+
171
+       pw_impl_port_set_param(port, SPA_PARAM_Format, 0, NULL);
172
    }
173
    return res;
174
 }
175
@@ -472,8 +545,10 @@
176
    spa_list_init(&impl->param_list);
177
    spa_list_init(&impl->pending_list);
178
    impl->cache_params = true;
179
+   spa_list_init(&impl->mix_list);
180
 
181
    this = &impl->this;
182
+
183
    pw_log_debug("%p: new %s %d", this,
184
            pw_direction_as_string(direction), port_id);
185
 
186
@@ -512,7 +587,6 @@
187
 
188
    spa_list_init(&this->links);
189
    spa_list_init(&this->mix_list);
190
-   spa_list_init(&this->rt.mix_list);
191
    spa_list_init(&this->control_list0);
192
    spa_list_init(&this->control_list1);
193
 
194
@@ -1643,7 +1717,7 @@
195
    int res = 0, res2;
196
 
197
    pw_log_debug("%p: %d:%d.%d: %d buffers flags:%d state:%d n_mix:%d", port,
198
-           port->direction, port->port_id, mix->id,
199
+           port->direction, port->port_id, mix->port.port_id,
200
            n_buffers, flags, port->state, port->n_mix);
201
pipewire-0.3.71.tar.gz/src/pipewire/impl-port.h -> pipewire-0.3.72.tar.gz/src/pipewire/impl-port.h Changed
11
 
1
@@ -98,6 +98,9 @@
2
 /** Get the port id */
3
 uint32_t pw_impl_port_get_id(struct pw_impl_port *port);
4
 
5
+/** Get the port state as a string */
6
+const char *pw_impl_port_state_as_string(enum pw_impl_port_state state);
7
+
8
 /** Get the port parent node or NULL when not yet set */
9
 struct pw_impl_node *pw_impl_port_get_node(struct pw_impl_port *port);
10
 
11
pipewire-0.3.71.tar.gz/src/pipewire/introspect.c -> pipewire-0.3.72.tar.gz/src/pipewire/introspect.c Changed
25
 
1
@@ -212,6 +212,7 @@
2
        }
3
        info->n_params = n_params;
4
        for (; i < info->n_params; i++) {
5
+           spa_zero(info->paramsi);
6
            info->paramsi.id = update->paramsi.id;
7
            info->paramsi.flags = update->paramsi.flags;
8
            info->paramsi.user = 1;
9
@@ -285,6 +286,7 @@
10
        }
11
        info->n_params = n_params;
12
        for (; i < info->n_params; i++) {
13
+           spa_zero(info->paramsi);
14
            info->paramsi.id = update->paramsi.id;
15
            info->paramsi.flags = update->paramsi.flags;
16
            info->paramsi.user = 1;
17
@@ -448,6 +450,7 @@
18
        }
19
        info->n_params = n_params;
20
        for (; i < info->n_params; i++) {
21
+           spa_zero(info->paramsi);
22
            info->paramsi.id = update->paramsi.id;
23
            info->paramsi.flags = update->paramsi.flags;
24
            info->paramsi.user = 1;
25
pipewire-0.3.71.tar.gz/src/pipewire/keys.h -> pipewire-0.3.72.tar.gz/src/pipewire/keys.h Changed
11
 
1
@@ -52,7 +52,8 @@
2
 #define PW_KEY_OBJECT_LINGER       "object.linger"     /**< the object lives on even after the client
3
                                  *  that created it has been destroyed */
4
 #define PW_KEY_OBJECT_REGISTER     "object.register"   /**< If the object should be registered. */
5
-
6
+#define PW_KEY_OBJECT_EXPORT       "object.export"     /**< If the object should be exported,
7
+                                 *  since 0.3.72 */
8
 
9
 /* config */
10
 #define PW_KEY_CONFIG_PREFIX       "config.prefix"     /**< a config prefix directory */
11
pipewire-0.3.71.tar.gz/src/pipewire/private.h -> pipewire-0.3.72.tar.gz/src/pipewire/private.h Changed
101
 
1
@@ -423,6 +423,13 @@
2
    void (*complete) (void *data, struct pw_impl_node *node);
3
 };
4
 
5
+void pw_context_driver_add_listener(struct pw_context *context,
6
+             struct spa_hook *listener,
7
+             const struct pw_context_driver_events *events,
8
+             void *data);
9
+void pw_context_driver_remove_listener(struct pw_context *context,
10
+             struct spa_hook *listener);
11
+
12
 #define pw_registry_resource(r,m,v,...) pw_resource_call(r, struct pw_registry_events,m,v,##__VA_ARGS__)
13
 #define pw_registry_resource_global(r,...)        pw_registry_resource(r,global,0,__VA_ARGS__)
14
 #define pw_registry_resource_global_remove(r,...) pw_registry_resource(r,global_remove,0,__VA_ARGS__)
15
@@ -584,6 +591,11 @@
16
 
17
 struct pw_node_target {
18
    struct spa_list link;
19
+#define PW_NODE_TARGET_NONE    0
20
+#define PW_NODE_TARGET_PEER    1
21
+   uint32_t flags;
22
+   uint32_t id;
23
+   char name128;
24
    struct pw_impl_node *node;
25
    struct pw_node_activation *activation;
26
    struct spa_system *system;
27
@@ -591,6 +603,16 @@
28
    unsigned int active:1;
29
 };
30
 
31
+static inline void copy_target(struct pw_node_target *dst, const struct pw_node_target *src)
32
+{
33
+   dst->id = src->id;
34
+   memcpy(dst->name, src->name, sizeof(dst->name));
35
+   dst->node = src->node;
36
+   dst->activation = src->activation;
37
+   dst->system = src->system;
38
+   dst->fd = src->fd;
39
+}
40
+
41
 struct pw_node_activation {
42
 #define PW_NODE_ACTIVATION_NOT_TRIGGERED   0
43
 #define PW_NODE_ACTIVATION_TRIGGERED       1
44
@@ -616,9 +638,13 @@
45
                             * used when driver segment_owner has this node id */
46
 
47
    /* for drivers, shared with all nodes */
48
-   uint32_t segment_owner32;           /* id of owners for each segment info struct.
49
+   uint32_t segment_owner16;           /* id of owners for each segment info struct.
50
                             * nodes that want to update segment info need to
51
                             * CAS their node id in this array. */
52
+   uint32_t padding15;
53
+#define PW_NODE_ACTIVATION_FLAG_NONE       0
54
+#define PW_NODE_ACTIVATION_FLAG_PROFILER   (1<<0)  /* the profiler is running */
55
+   uint32_t flags;                 /* extra flags */
56
    struct spa_io_position position;        /* contains current position and segment info.
57
                             * extra info is updated by nodes that have set
58
                             * themselves as owner in the segment structs */
59
@@ -764,7 +790,6 @@
60
    struct {
61
        struct spa_io_clock *clock; /**< io area of the clock or NULL */
62
        struct spa_io_position *position;
63
-       struct pw_node_activation *activation;
64
 
65
        struct spa_list target_list;        /* list of targets to signal after
66
                             * this node */
67
@@ -781,6 +806,8 @@
68
    struct spa_fraction target_rate;
69
    uint64_t target_quantum;
70
 
71
+   uint64_t driver_start;
72
+
73
    void *user_data;                /**< extra user data */
74
 };
75
 
76
@@ -796,6 +823,7 @@
77
    uint32_t id;
78
    uint32_t peer_id;
79
    unsigned int have_buffers:1;
80
+   unsigned int active:1;
81
 };
82
 
83
 struct pw_impl_port_implementation {
84
@@ -884,8 +912,6 @@
85
 
86
    struct {
87
        struct spa_io_buffers io;   /**< io area of the port */
88
-       struct spa_io_clock clock;  /**< io area of the clock */
89
-       struct spa_list mix_list;
90
        struct spa_list node_link;
91
    } rt;                   /**< data only accessed from the data thread */
92
    unsigned int added:1;
93
@@ -964,6 +990,7 @@
94
    unsigned int preparing:1;
95
    unsigned int prepared:1;
96
    unsigned int passive:1;
97
+   unsigned int destroyed:1;
98
 };
99
 
100
 #define pw_resource_emit(o,m,v,...) spa_hook_list_call(&o->listener_list, struct pw_resource_events, m, v, ##__VA_ARGS__)
101
pipewire-0.3.71.tar.gz/src/pipewire/properties.c -> pipewire-0.3.72.tar.gz/src/pipewire/properties.c Changed
32
 
1
@@ -706,8 +706,11 @@
2
    if (value == NULL || len == 0) {
3
        fprintf(file, "%snull%s", LITERAL(c), NORMAL(c));
4
    } else if (spa_json_is_container(value, len) && !c->recurse) {
5
-       len = spa_json_container_len(it, value, len);
6
-       fprintf(file, "%s%.*s%s", CONTAINER(c), len, value, NORMAL(c));
7
+       spa_json_enter_container(it, &sub, value0);
8
+       if (spa_json_container_len(&sub, value, len) == len)
9
+           fprintf(file, "%s%.*s%s", CONTAINER(c), len, value, NORMAL(c));
10
+       else
11
+           encode_string(c, STRING(c), value, len, NORMAL(c));
12
    } else if (spa_json_is_array(value, len)) {
13
        fprintf(file, "");
14
        spa_json_enter(it, &sub);
15
@@ -782,15 +785,11 @@
16
            fprintf(f, "%s%s%s: ", KEY(c), key, NORMAL(c));
17
        }
18
        value = it->value;
19
-
20
        len = value ? strlen(value) : 0;
21
        spa_json_init(&sub, value, len);
22
-       if ((len = spa_json_next(&sub, &value)) < 0)
23
+       if (c->recurse && spa_json_next(&sub, &value) < 0)
24
            break;
25
 
26
-       if (!spa_json_is_container(value, len))
27
-           len = value ? strlen(value) : 0;
28
-
29
        dump(c, c->indent, &sub, value, len);
30
        count++;
31
    }
32
pipewire-0.3.71.tar.gz/src/pipewire/stream.c -> pipewire-0.3.72.tar.gz/src/pipewire/stream.c Changed
201
 
1
@@ -158,6 +158,7 @@
2
    unsigned int using_trigger:1;
3
    unsigned int trigger:1;
4
    int in_set_param;
5
+   int in_emit_param_changed;
6
 };
7
 
8
 static int get_param_index(uint32_t id)
9
@@ -268,14 +269,42 @@
10
 static void clear_params(struct stream *impl, uint32_t id)
11
 {
12
    struct param *p, *t;
13
+   bool found = false;
14
+   int i, idx;
15
 
16
    spa_list_for_each_safe(p, t, &impl->param_list, link) {
17
        if (id == SPA_ID_INVALID ||
18
            (p->id == id && !(p->flags & PARAM_FLAG_LOCKED))) {
19
+           found = true;
20
            spa_list_remove(&p->link);
21
            free(p);
22
        }
23
    }
24
+   if (found) {
25
+       if (id == SPA_ID_INVALID) {
26
+           impl->info.change_mask |= SPA_NODE_CHANGE_MASK_PARAMS;
27
+           for (i = 0; i < N_NODE_PARAMS; i++) {
28
+               impl->paramsi.flags &= ~SPA_PARAM_INFO_READ;
29
+               impl->paramsi.user++;
30
+           }
31
+           impl->port_info.change_mask |= SPA_PORT_CHANGE_MASK_PARAMS;
32
+           for (i = 0; i < N_PORT_PARAMS; i++) {
33
+               impl->port_paramsi.flags &= ~SPA_PARAM_INFO_READ;
34
+               impl->port_paramsi.user++;
35
+           }
36
+       } else {
37
+           if ((idx = get_param_index(id)) != -1) {
38
+               impl->info.change_mask |= SPA_NODE_CHANGE_MASK_PARAMS;
39
+               impl->paramsidx.flags &= ~SPA_PARAM_INFO_READ;
40
+               impl->paramsidx.user++;
41
+           }
42
+           if ((idx = get_port_param_index(id)) != -1) {
43
+               impl->port_info.change_mask |= SPA_PORT_CHANGE_MASK_PARAMS;
44
+               impl->port_paramsidx.flags &= ~SPA_PARAM_INFO_READ;
45
+               impl->port_paramsidx.user++;
46
+           }
47
+       }
48
+   }
49
 }
50
 
51
 static int update_params(struct stream *impl, uint32_t id,
52
@@ -424,13 +453,16 @@
53
 static inline void call_process(struct stream *impl)
54
 {
55
    pw_log_trace_fp("%p: call process rt:%u", impl, impl->process_rt);
56
-   if (impl->direction == SPA_DIRECTION_OUTPUT && update_requested(impl) <= 0)
57
+   if (impl->n_buffers == 0 ||
58
+       (impl->direction == SPA_DIRECTION_OUTPUT && update_requested(impl) <= 0))
59
        return;
60
-   if (impl->process_rt)
61
-       spa_callbacks_call(&impl->rt_callbacks, struct pw_stream_events, process, 0);
62
-   else
63
+   if (impl->process_rt) {
64
+       if (impl->rt_callbacks.funcs)
65
+           spa_callbacks_call_fast(&impl->rt_callbacks, struct pw_stream_events, process, 0);
66
+   } else {
67
        pw_loop_invoke(impl->main_loop,
68
            do_call_process, 1, NULL, 0, false, impl);
69
+   }
70
 }
71
 
72
 static int
73
@@ -561,16 +593,24 @@
74
    return enum_params(object, false, seq, id, start, num, filter);
75
 }
76
 
77
+static inline void emit_param_changed(struct stream *impl,
78
+       uint32_t id, const struct spa_pod *param)
79
+{
80
+   struct pw_stream *stream = &impl->this;
81
+   if (impl->in_emit_param_changed++ == 0)
82
+       pw_stream_emit_param_changed(stream, id, param);
83
+   impl->in_emit_param_changed--;
84
+}
85
+
86
 static int impl_set_param(void *object, uint32_t id, uint32_t flags, const struct spa_pod *param)
87
 {
88
    struct stream *impl = object;
89
-   struct pw_stream *stream = &impl->this;
90
 
91
    if (id != SPA_PARAM_Props)
92
        return -ENOTSUP;
93
 
94
    if (impl->in_set_param == 0)
95
-       pw_stream_emit_param_changed(stream, id, param);
96
+       emit_param_changed(impl, id, param);
97
 
98
    return 0;
99
 }
100
@@ -883,7 +923,7 @@
101
        break;
102
    }
103
 
104
-   pw_stream_emit_param_changed(stream, id, param);
105
+   emit_param_changed(impl, id, param);
106
 
107
    if (stream->state == PW_STREAM_STATE_ERROR)
108
        return stream->error_res;
109
@@ -996,12 +1036,15 @@
110
        /* push new buffer */
111
        pw_log_trace_fp("%p: push %d %p", stream, b->id, io);
112
        if (queue_push(impl, &impl->dequeued, b) == 0) {
113
-           copy_position(impl, impl->dequeued.incount);
114
            if (b->busy)
115
                ATOMIC_INC(b->busy->count);
116
-           call_process(impl);
117
        }
118
    }
119
+   if (!queue_is_empty(impl, &impl->dequeued)) {
120
+       copy_position(impl, impl->dequeued.incount);
121
+       call_process(impl);
122
+   }
123
+
124
    if (io->status != SPA_STATUS_NEED_DATA || io->buffer_id == SPA_ID_INVALID) {
125
        /* pop buffer to recycle */
126
        if ((b = queue_pop(impl, &impl->queued))) {
127
@@ -1342,8 +1385,10 @@
128
 static void node_event_destroy(void *data)
129
 {
130
    struct pw_stream *stream = data;
131
+   struct stream *impl = SPA_CONTAINER_OF(stream, struct stream, this);
132
    spa_hook_remove(&stream->node_listener);
133
    stream->node = NULL;
134
+   impl->data_loop = NULL;
135
 }
136
 
137
 static void node_event_info(void *data, const struct pw_node_info *info)
138
@@ -1515,7 +1560,7 @@
139
    impl->allow_mlock = context->settings.mem_allow_mlock;
140
    impl->warn_mlock = context->settings.mem_warn_mlock;
141
 
142
-   spa_hook_list_append(&impl->context->driver_listener_list,
143
+   pw_context_driver_add_listener(impl->context,
144
            &impl->context_listener,
145
            &context_events, impl);
146
    return impl;
147
@@ -1683,7 +1728,8 @@
148
    spa_hook_list_clean(&impl->hooks);
149
    spa_hook_list_clean(&stream->listener_list);
150
 
151
-   spa_hook_remove(&impl->context_listener);
152
+   pw_context_driver_remove_listener(impl->context,
153
+           &impl->context_listener);
154
 
155
    if (impl->data.context)
156
        pw_context_destroy(impl->data.context);
157
@@ -1692,10 +1738,22 @@
158
    free(impl);
159
 }
160
 
161
+static int
162
+do_remove_callbacks(struct spa_loop *loop,
163
+                 bool async, uint32_t seq, const void *data, size_t size, void *user_data)
164
+{
165
+   struct stream *impl = user_data;
166
+   spa_zero(impl->rt_callbacks);
167
+   return 0;
168
+}
169
+
170
 static void hook_removed(struct spa_hook *hook)
171
 {
172
    struct stream *impl = hook->priv;
173
-   spa_zero(impl->rt_callbacks);
174
+   if (impl->data_loop)
175
+       pw_loop_invoke(impl->data_loop, do_remove_callbacks, 1, NULL, 0, true, impl);
176
+   else
177
+       spa_zero(impl->rt_callbacks);
178
    hook->priv = NULL;
179
    hook->removed = NULL;
180
 }
181
@@ -1953,10 +2011,12 @@
182
        /* XXX this is deprecated but still used by the portal and its apps */
183
        pw_properties_setf(stream->properties, PW_KEY_NODE_TARGET, "%d", target_id);
184
 
185
-   if ((flags & PW_STREAM_FLAG_AUTOCONNECT) &&
186
+   if ((str = getenv("PIPEWIRE_AUTOCONNECT")) != NULL)
187
+       pw_properties_set(stream->properties,
188
+               PW_KEY_NODE_AUTOCONNECT, spa_atob(str) ? "true" : "false");
189
+   else if ((flags & PW_STREAM_FLAG_AUTOCONNECT) &&
190
        pw_properties_get(stream->properties, PW_KEY_NODE_AUTOCONNECT) == NULL) {
191
-       str = getenv("PIPEWIRE_AUTOCONNECT");
192
-       pw_properties_set(stream->properties, PW_KEY_NODE_AUTOCONNECT, str ? str : "true");
193
+       pw_properties_set(stream->properties, PW_KEY_NODE_AUTOCONNECT, "true");
194
    }
195
    if (flags & PW_STREAM_FLAG_DRIVER)
196
        pw_properties_set(stream->properties, PW_KEY_NODE_DRIVER, "true");
197
@@ -2471,7 +2531,7 @@
198
    struct stream *impl = SPA_CONTAINER_OF(stream, struct stream, this);
199
    int res = 0;
200
 
201
pipewire-0.3.71.tar.gz/src/pipewire/stream.h -> pipewire-0.3.72.tar.gz/src/pipewire/stream.h Changed
11
 
1
@@ -151,6 +151,9 @@
2
  *
3
  * \section sec_stream_environment Environment Variables
4
  *
5
+ * The environment variable PIPEWIRE_AUTOCONNECT can be used to override the
6
+ * flag and force apps to autoconnect or not.
7
+ *
8
  */
9
 /** \defgroup pw_stream Stream
10
  *
11
pipewire-0.3.71.tar.gz/src/pipewire/thread-loop.c -> pipewire-0.3.72.tar.gz/src/pipewire/thread-loop.c Changed
53
 
1
@@ -50,7 +50,7 @@
2
 {
3
    int res;
4
    if ((res = pthread_mutex_lock(&this->lock)) != 0)
5
-       pw_log_error("%p: thread:%lu: %s", this, pthread_self(), strerror(res));
6
+       pw_log_error("%p: thread:%p: %s", this, (void *) pthread_self(), strerror(res));
7
    else
8
        this->recurse++;
9
    return -res;
10
@@ -62,7 +62,7 @@
11
    spa_return_val_if_fail(this->recurse > 0, -EIO);
12
    this->recurse--;
13
    if ((res = pthread_mutex_unlock(&this->lock)) != 0) {
14
-       pw_log_error("%p: thread:%lu: %s", this, pthread_self(), strerror(res));
15
+       pw_log_error("%p: thread:%p: %s", this, (void *) pthread_self(), strerror(res));
16
        this->recurse++;
17
    }
18
    return -res;
19
@@ -97,13 +97,13 @@
20
 
21
    /* if lock taken by something else, error */
22
    if ((res = pthread_mutex_trylock(&this->lock)) != 0) {
23
-       pw_log_debug("%p: thread:%lu: %s", this, pthread_self(), strerror(res));
24
+       pw_log_debug("%p: thread:%p: %s", this, (void *) pthread_self(), strerror(res));
25
        return -res;
26
    }
27
    /* we could take the lock, check if we actually locked it somewhere */
28
    res = this->recurse > 0 ? 1 : -EPERM;
29
    if (res < 0)
30
-       pw_log_debug("%p: thread:%lu: recurse:%d", this, pthread_self(), this->recurse);
31
+       pw_log_debug("%p: thread:%p: recurse:%d", this, (void *) pthread_self(), this->recurse);
32
    pthread_mutex_unlock(&this->lock);
33
    return res;
34
 }
35
@@ -398,7 +398,7 @@
36
        while (loop->n_waiting_for_accept > 0) {
37
            int res;
38
            if ((res = pthread_cond_wait(&loop->accept_cond, &loop->lock)) != 0)
39
-               pw_log_error("%p: thread:%lu: %s", loop, pthread_self(), strerror(res));
40
+               pw_log_error("%p: thread:%p: %s", loop, (void *) pthread_self(), strerror(res));
41
        }
42
    }
43
 }
44
@@ -418,7 +418,7 @@
45
    loop->n_waiting++;
46
    loop->recurse--;
47
    if ((res = pthread_cond_wait(&loop->cond, &loop->lock)) != 0)
48
-       pw_log_error("%p: thread:%lu: %s", loop, pthread_self(), strerror(res));
49
+       pw_log_error("%p: thread:%p: %s", loop, (void *) pthread_self(), strerror(res));
50
    loop->recurse++;
51
    loop->n_waiting--;
52
    pw_log_trace("%p, waiting done %d", loop, loop->n_waiting);
53
pipewire-0.3.71.tar.gz/src/tools/pw-top.c -> pipewire-0.3.72.tar.gz/src/tools/pw-top.c Changed
107
 
1
@@ -22,6 +22,8 @@
2
 #define MAX_FORMAT     16
3
 #define MAX_NAME       128
4
 
5
+#define XRUN_INVALID   (uint32_t)-1
6
+
7
 struct driver {
8
    int64_t count;
9
    float cpu_load3;
10
@@ -38,6 +40,7 @@
11
    int64_t awake;
12
    int64_t finish;
13
    struct spa_fraction latency;
14
+   uint32_t xrun_count;
15
 };
16
 
17
 struct node {
18
@@ -49,8 +52,6 @@
19
    struct measurement measurement;
20
    struct driver info;
21
    struct node *driver;
22
-   uint32_t errors;
23
-   int32_t last_error_status;
24
    uint32_t generation;
25
    char formatMAX_FORMAT+1;
26
    struct pw_proxy *proxy;
27
@@ -319,6 +320,7 @@
28
    int res;
29
 
30
    spa_zero(m);
31
+   m.xrun_count = XRUN_INVALID;
32
    if ((res = spa_pod_parse_struct(pod,
33
            SPA_POD_Int(&id),
34
            SPA_POD_String(&name),
35
@@ -327,7 +329,8 @@
36
            SPA_POD_Long(&m.awake),
37
            SPA_POD_Long(&m.finish),
38
            SPA_POD_Int(&m.status),
39
-           SPA_POD_Fraction(&m.latency))) < 0)
40
+           SPA_POD_Fraction(&m.latency),
41
+           SPA_POD_OPT_Int(&m.xrun_count))) < 0)
42
        return res;
43
 
44
    if ((n = find_node(d, id)) == NULL)
45
@@ -338,12 +341,6 @@
46
    n->info = point->info;
47
    point->driver = n;
48
    n->generation = d->generation;
49
-
50
-   if (m.status != 3) {
51
-       n->errors++;
52
-       if (n->last_error_status == -1)
53
-           n->last_error_status = m.status;
54
-   }
55
    return 0;
56
 }
57
 
58
@@ -356,6 +353,7 @@
59
    int res;
60
 
61
    spa_zero(m);
62
+   m.xrun_count = XRUN_INVALID;
63
    if ((res = spa_pod_parse_struct(pod,
64
            SPA_POD_Int(&id),
65
            SPA_POD_String(&name),
66
@@ -364,7 +362,8 @@
67
            SPA_POD_Long(&m.awake),
68
            SPA_POD_Long(&m.finish),
69
            SPA_POD_Int(&m.status),
70
-           SPA_POD_Fraction(&m.latency))) < 0)
71
+           SPA_POD_Fraction(&m.latency),
72
+           SPA_POD_OPT_Int(&m.xrun_count))) < 0)
73
        return res;
74
 
75
    if ((n = find_node(d, id)) == NULL)
76
@@ -376,11 +375,6 @@
77
        d->pending_refresh = true;
78
    }
79
    n->generation = d->generation;
80
-   if (m.status != 3) {
81
-       n->errors++;
82
-       if (n->last_error_status == -1)
83
-           n->last_error_status = m.status;
84
-   }
85
    return 0;
86
 }
87
 
88
@@ -476,7 +470,8 @@
89
            print_time(buf2, active, 64, busy),
90
            print_perc(buf3, active, 64, waiting, quantum),
91
            print_perc(buf4, active, 64, busy, quantum),
92
-           i->xrun_count + n->errors,
93
+           n->measurement.xrun_count == XRUN_INVALID ?
94
+                   i->xrun_count : n->measurement.xrun_count,
95
            active ? n->format : "",
96
            n->driver == n ? "" : " + ",
97
            n->name);
98
@@ -487,8 +482,6 @@
99
    n->driver = n;
100
    spa_zero(n->measurement);
101
    spa_zero(n->info);
102
-   n->errors = 0;
103
-   n->last_error_status = 0;
104
 }
105
 
106
 static void do_refresh(struct data *d)
107
pipewire-0.3.71.tar.gz/test/test-logger.c -> pipewire-0.3.72.tar.gz/test/test-logger.c Changed
19
 
1
@@ -504,7 +504,7 @@
2
    }
3
 
4
    sd_journal_seek_tail(journal);
5
-   sd_journal_next(journal);
6
+   sd_journal_previous(journal);
7
 
8
    spa_scnprintf(token, sizeof(token), "MARK %s:%d", __func__, __LINE__);
9
    spa_logt_info(iface, &topic, "%s", token);
10
@@ -572,7 +572,7 @@
11
    }
12
 
13
    sd_journal_seek_tail(journal);
14
-   sd_journal_next(journal);
15
+   sd_journal_previous(journal);
16
 
17
    spa_scnprintf(token, sizeof(token), "MARK %s:%d", __func__, __LINE__);
18
 
19