Overview

Request 5548 (accepted)

New upstream release

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

pipewire-aptx.changes Changed
x
 
1
@@ -1,4 +1,9 @@
2
 -------------------------------------------------------------------
3
+Mon Apr  4 15:18:29 UTC 2022 - Bjørn Lie <zaitor@opensuse.org>
4
+
5
+- Update to version 0.3.49
6
+
7
+-------------------------------------------------------------------
8
 Thu Mar  3 11:50:58 UTC 2022 - Bjørn Lie <zaitor@opensuse.org>
9
 
10
 - Update to version 0.3.48
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.48
6
+Version:        0.3.49
7
 Release:        0
8
 Summary:        PipeWire Bluetooth aptX codec plugin
9
 License:        MIT
10
pipewire-0.3.48.tar.gz/.gitignore -> pipewire-0.3.49.tar.gz/.gitignore Changed
16
 
1
@@ -1,3 +1,4 @@
2
+.*
3
 .tarball-version
4
 .version
5
 .*.swp
6
@@ -6,9 +7,6 @@
7
 *.tar.gz
8
 *.tar.xz
9
 *.o
10
-build/
11
-builddir/
12
-config.h.meson
13
 cscope.out
14
 cscope.in.out
15
 cscope.po.out
16
pipewire-0.3.48.tar.gz/.gitlab-ci.yml -> pipewire-0.3.49.tar.gz/.gitlab-ci.yml Changed
19
 
1
@@ -25,7 +25,7 @@
2
 .fedora:
3
   variables:
4
     # Update this tag when you want to trigger a rebuild
5
-    FDO_DISTRIBUTION_TAG: '2022-02-16.0'
6
+    FDO_DISTRIBUTION_TAG: '2022-03-05.0'
7
     FDO_DISTRIBUTION_VERSION: '35'
8
     FDO_DISTRIBUTION_PACKAGES: >-
9
       alsa-lib-devel
10
@@ -66,6 +66,8 @@
11
       ninja-build
12
       pkgconf
13
       python3-pip
14
+      pulseaudio-utils
15
+      openal-soft
16
     FDO_DISTRIBUTION_EXEC: >-
17
       pip3 install meson
18
 
19
pipewire-0.3.48.tar.gz/Makefile.in -> pipewire-0.3.49.tar.gz/Makefile.in Changed
31
 
1
@@ -17,6 +17,7 @@
2
 
3
 run: all
4
    SPA_PLUGIN_DIR=$(BUILD_ROOT)/spa/plugins \
5
+   SPA_DATA_DIR=$(SOURCE_ROOT)/spa/plugins \
6
    PIPEWIRE_MODULE_DIR=$(BUILD_ROOT)/src/modules \
7
    PATH=$(BUILD_ROOT)/src/examples:$(PATH) \
8
    PIPEWIRE_CONFIG_DIR=$(BUILD_ROOT)/src/daemon \
9
@@ -26,6 +27,7 @@
10
 
11
 run-pulse: all
12
    SPA_PLUGIN_DIR=$(BUILD_ROOT)/spa/plugins \
13
+   SPA_DATA_DIR=$(SOURCE_ROOT)/spa/plugins \
14
    PIPEWIRE_MODULE_DIR=$(BUILD_ROOT)/src/modules \
15
    PIPEWIRE_CONFIG_DIR=$(BUILD_ROOT)/src/daemon \
16
    ACP_PATHS_DIR=$(SOURCE_ROOT)/spa/plugins/alsa/mixer/paths \
17
@@ -46,11 +48,13 @@
18
 
19
 monitor: all
20
    SPA_PLUGIN_DIR=$(BUILD_ROOT)/spa/plugins \
21
+   SPA_DATA_DIR=$(SOURCE_ROOT)/spa/plugins \
22
    PIPEWIRE_MODULE_DIR=$(BUILD_ROOT)/src/modules/ \
23
    $(BUILD_ROOT)/src/tools/pw-mon
24
 
25
 cli: all
26
    SPA_PLUGIN_DIR=$(BUILD_ROOT)/spa/plugins \
27
+   SPA_DATA_DIR=$(SOURCE_ROOT)/spa/plugins \
28
    PIPEWIRE_MODULE_DIR=$(BUILD_ROOT)/src/modules/ \
29
    $(BUILD_ROOT)/src/tools/pw-cli
30
 
31
pipewire-0.3.48.tar.gz/NEWS -> pipewire-0.3.49.tar.gz/NEWS Changed
89
 
1
@@ -1,3 +1,77 @@
2
+# PipeWire 0.3.49 (2022-03-31)
3
+
4
+This is a bugfix release that is API and ABI compatible with previous
5
+0.3.x releases.
6
+
7
+## Highlights
8
+  - Sample rate switching should work again.
9
+  - pw-dot can now use the output of pw-dump to render a graph.
10
+  - Bluetooth A2DP streaming was improved that would reduce stuttering on
11
+    some devices.
12
+  - A JACK bug was fixed that would sometimes make it impossible to add more
13
+    tracks in Ardour. (#1714)
14
+  - Many bugfixes and improvements.
15
+
16
+## PipeWire
17
+  - Fix a potential crash when NULL params were configured.
18
+  - Add some simple functional tests to avoid some recent regressions. Improve
19
+    the test framework for this as well.
20
+  - Improvements to the poll loop to avoid some use-after-free scenarios.
21
+  - Fix samplerate switching again.
22
+  - setlocale is not called anymore from the pipewire library. This should be
23
+    called by the application. (#2223)
24
+  - pw_init() and pw_deinit() can now be nested and called multiple times.
25
+  - pw_stream will now report the resampler delay in the pw_time.queued field.
26
+
27
+## modules
28
+  - module-filter-chain now supports arbitrary many properties and will use
29
+    property hints to assign them the right type.
30
+  - The ROC modules now accept a sink/source_properties parameter.
31
+  - The module-rt can now also be built without RT-Kit support.
32
+  - module-echo-cancel can now use a fraction to specify the delay for more
33
+    precise control.
34
+
35
+## SPA
36
+  - The channelmixer will now do upmixing by default and will not use
37
+    normalization. It will also use a simple upmixing algorithm that duplicates
38
+    channels by default. A more interesting upmix method is also available (PSD)
39
+    but needs to be enabled manually. (#861)
40
+  - Add SSE optimized (de)interleave functions for 32 bits samples with and
41
+    without byteswap.
42
+  - JSON parsing of empty strings will now give an invalid number instead of
43
+    0.
44
+  - JSON numbers are now parsed and serialized in a locale independent way so
45
+    that , and . are not mixed up.
46
+  - The resampler will now report the resample delay and queued samples as the
47
+    extra delay.
48
+
49
+## tools
50
+  - pw-cat will read more dsf files correctly and will not crash at the end.
51
+  - pw-top now has a man page.
52
+  - pw-dot can now use the output of pw-dump to render a graph.
53
+
54
+## bluetooth
55
+  - Improve interactions with oFono.
56
+  - Fix recovery with slow connections.
57
+  - Improve frame size of AptX-ll.
58
+  - A2DP can now use any quantum and will flush packets in smaller chunks
59
+    when needed to adapt. This improves stuttering in some cases.
60
+
61
+## pulse-server
62
+  - The server configuration can now be placed in pulse.properties section,
63
+    which also makes it possible to have custom overrides.
64
+  - Implement FIX_ flags for capture as well.
65
+  - Small fixes and improvements in module loading.
66
+
67
+## JACK
68
+  - Clear the last error before executing a new action or else we could end up
69
+    with error from a previous action. This causes some problems in Ardour where
70
+    adding a track would fail after some time. (#1714)
71
+
72
+
73
+Older versions:
74
+
75
+
76
 # PipeWire 0.3.48 (2022-03-03)
77
 
78
 This is a bugfix release that is API and ABI compatible with previous
79
@@ -73,9 +147,6 @@
80
   - Don't try to connect HSP/HFP when no backend is available.
81
 
82
 
83
-Older versions:
84
-
85
-
86
 # PipeWire 0.3.47 (2022-02-18)
87
 
88
 This is a bugfix release that is API and ABI compatible with previous
89
pipewire-0.3.48.tar.gz/README.md -> pipewire-0.3.49.tar.gz/README.md Changed
10
 
1
@@ -189,7 +189,7 @@
2
 
3
 ## Documentation
4
 
5
-Find tutorials and design documentation [here](doc/index.md).
6
+Find tutorials and design documentation [here](doc/index.dox).
7
 
8
 The (incomplete) autogenerated API docs are [here](https://docs.pipewire.org).
9
 
10
pipewire-0.3.48.tar.gz/doc/dma-buf.dox -> pipewire-0.3.49.tar.gz/doc/dma-buf.dox Changed
17
 
1
@@ -138,6 +138,15 @@
2
   using a proper graphics API (such as EGL, Vulkan or VA-API) to process the
3
   DMA-BUFs.
4
 
5
+# Size of DMA-BUFs
6
+
7
+When importing a DMA-BUF with a proper graphics API the size of a single buffer plane
8
+is no relevant property since it will be derived by the driver from the other properties.
9
+Therefore consumers should ignore the field `maxsize` of a `spa_data` and the field
10
+`size` of a `spa_chunk` struct. Producers are allowed to set both to 0.
11
+In cases where mapping a single plane is required the size should be obtained locally
12
+via the filedescriptor.
13
+
14
 # v4l2
15
 
16
 Another use case for streaming via DMA-BUFs are exporting a camera feed from v4l2
17
pipewire-0.3.48.tar.gz/man/meson.build -> pipewire-0.3.49.tar.gz/man/meson.build Changed
9
 
1
@@ -17,6 +17,7 @@
2
   'pw-mididump.1.rst.in',
3
   'pw-mon.1.rst.in',
4
   'pw-profiler.1.rst.in',
5
+  'pw-top.1.rst.in',
6
 ]
7
 
8
 if get_option('pipewire-jack').allowed()
9
pipewire-0.3.49.tar.gz/man/pw-top.1.rst.in Added
84
 
1
@@ -0,0 +1,82 @@
2
+pw-top
3
+######
4
+
5
+---------------------------
6
+The PipeWire process viewer
7
+---------------------------
8
+
9
+:Manual section: 1
10
+:Manual group: General Commands Manual
11
+
12
+SYNOPSIS
13
+========
14
+
15
+| **pw-top** [*options*]
16
+
17
+DESCRIPTION
18
+===========
19
+
20
+The *pw-top* program provides a dynamic real-time view of the pipewire
21
+node and device statistics.
22
+
23
+The columns presented are as follows:
24
+
25
+S
26
+  Measurement status.
27
+  ! representing inactive - no connections
28
+
29
+  Blank representing active
30
+
31
+ID
32
+  The ID of the pipewire node/device, as found in *pw-dump*
33
+
34
+QUANT
35
+  Current quantum at which the device/node is polled.
36
+  See https://gitlab.freedesktop.org/pipewire/pipewire/-/wikis/FAQ#pipewire-buffering-explained
37
+
38
+RATE
39
+  Sample rate used for communicating with this device/node.
40
+
41
+WAIT
42
+
43
+BUSY
44
+
45
+W/Q
46
+  Ratio of WAIT / QUANT.
47
+
48
+B/Q
49
+  Ratio of BUSY / QUANT
50
+
51
+ERR
52
+  Total of Xruns and Errors
53
+
54
+NAME
55
+  Name assigned to the device/node, as found in *pw-dump* node.name
56
+
57
+  Names are prefixed by *+* when they are linked to a driver (entry above with no +)
58
+
59
+
60
+OPTIONS
61
+=======
62
+
63
+-h | --help
64
+  Show help.
65
+
66
+-r | --remote=NAME
67
+  The name the *remote* instance to monitor. If left unspecified,
68
+  a connection is made to the default PipeWire instance.
69
+
70
+--version
71
+  Show version information.
72
+
73
+
74
+AUTHORS
75
+=======
76
+
77
+The PipeWire Developers <@PACKAGE_BUGREPORT@>; PipeWire is available from @PACKAGE_URL@
78
+
79
+SEE ALSO
80
+========
81
+
82
+``pipewire(1)``,
83
+
84
pipewire-0.3.48.tar.gz/meson.build -> pipewire-0.3.49.tar.gz/meson.build Changed
8
 
1
@@ -1,5 +1,5 @@
2
 project('pipewire', ['c' ],
3
-  version : '0.3.48',
4
+  version : '0.3.49',
5
   license : [ 'MIT', 'LGPL-2.1-or-later', 'GPL-2.0-only' ],
6
   meson_version : '>= 0.59.0',
7
   default_options : [ 'warning_level=3',
8
pipewire-0.3.48.tar.gz/pipewire-jack/src/pipewire-jack.c -> pipewire-0.3.49.tar.gz/pipewire-jack/src/pipewire-jack.c Changed
46
 
1
@@ -298,7 +298,6 @@
2
    int pending_sync;
3
    int last_sync;
4
    int last_res;
5
-   bool error;
6
 
7
    struct spa_node_info info;
8
 
9
@@ -845,7 +844,6 @@
10
            id, seq, res, spa_strerror(res), message);
11
 
12
    if (id == PW_ID_CORE) {
13
-       client->error = true;
14
        client->last_res = res;
15
        if (!client->destroyed)
16
            do_callback(client, shutdown_callback, client->shutdown_arg);
17
@@ -867,9 +865,8 @@
18
        pw_log_warn("sync requested from callback");
19
        return 0;
20
    }
21
-   if (client->error)
22
-       return client->last_res;
23
 
24
+   client->last_res = 0;
25
    client->pending_sync = pw_proxy_sync((struct pw_proxy*)client->core, client->pending_sync);
26
 
27
    while (true) {
28
@@ -881,7 +878,7 @@
29
        if (in_data_thread && client->rt_locked)
30
            pthread_mutex_lock(&client->rt_lock);
31
 
32
-       if (client->error)
33
+       if (client->last_res < 0)
34
            return client->last_res;
35
 
36
        if (client->pending_sync == client->last_sync)
37
@@ -3314,7 +3311,7 @@
38
    while (true) {
39
            pw_thread_loop_wait(client->context.loop);
40
 
41
-       if (client->error)
42
+       if (client->last_res < 0)
43
            goto init_failed;
44
 
45
        if (client->has_transport)
46
pipewire-0.3.48.tar.gz/po/pl.po -> pipewire-0.3.49.tar.gz/po/pl.po Changed
247
 
1
@@ -1,15 +1,15 @@
2
 # Polish translation for pipewire.
3
-# Copyright © 2008-2021 the pipewire authors.
4
+# Copyright © 2008-2022 the pipewire authors.
5
 # This file is distributed under the same license as the pipewire package.
6
-# Piotr Drąg <piotrdrag@gmail.com>, 2008, 2012-2021.
7
+# Piotr Drąg <piotrdrag@gmail.com>, 2008, 2012-2022.
8
 #
9
 msgid ""
10
 msgstr ""
11
 "Project-Id-Version: pipewire\n"
12
 "Report-Msgid-Bugs-To: https://gitlab.freedesktop.org/pipewire/pipewire/-/"
13
 "issues\n"
14
-"POT-Creation-Date: 2021-09-21 15:31+0000\n"
15
-"PO-Revision-Date: 2021-10-09 15:35+0200\n"
16
+"POT-Creation-Date: 2022-02-13 15:33+0000\n"
17
+"PO-Revision-Date: 2022-03-13 12:05+0100\n"
18
 "Last-Translator: Piotr Drąg <piotrdrag@gmail.com>\n"
19
 "Language-Team: Polish <community-poland@mozilla.org>\n"
20
 "Language: pl\n"
21
@@ -41,43 +41,36 @@
22
 msgid "Start the PipeWire Media System"
23
 msgstr "Uruchomienie systemu multimediów PipeWire"
24
 
25
-#: src/examples/media-session/alsa-monitor.c:656
26
-#: spa/plugins/alsa/acp/compat.c:189
27
-msgid "Built-in Audio"
28
-msgstr "Wbudowany dźwięk"
29
-
30
-#: src/examples/media-session/alsa-monitor.c:660
31
-#: spa/plugins/alsa/acp/compat.c:194
32
-msgid "Modem"
33
-msgstr "Modem"
34
-
35
-#: src/examples/media-session/alsa-monitor.c:669
36
-#: src/modules/module-zeroconf-discover.c:296
37
-msgid "Unknown device"
38
-msgstr "Nieznane urządzenie"
39
-
40
-#: src/modules/module-protocol-pulse/modules/module-tunnel-sink.c:173
41
-#: src/modules/module-protocol-pulse/modules/module-tunnel-source.c:173
42
+#: src/modules/module-protocol-pulse/modules/module-tunnel-sink.c:188
43
+#: src/modules/module-protocol-pulse/modules/module-tunnel-source.c:188
44
 #, c-format
45
 msgid "Tunnel to %s/%s"
46
 msgstr "Tunel do %s/%s"
47
 
48
-#: src/modules/module-pulse-tunnel.c:534
49
+#: src/modules/module-fallback-sink.c:51
50
+msgid "Dummy Output"
51
+msgstr "Głuche wyjście"
52
+
53
+#: src/modules/module-pulse-tunnel.c:536
54
 #, c-format
55
 msgid "Tunnel for %s@%s"
56
 msgstr "Tunel dla %s@%s"
57
 
58
-#: src/modules/module-zeroconf-discover.c:308
59
+#: src/modules/module-zeroconf-discover.c:332
60
+msgid "Unknown device"
61
+msgstr "Nieznane urządzenie"
62
+
63
+#: src/modules/module-zeroconf-discover.c:344
64
 #, c-format
65
 msgid "%s on %s@%s"
66
 msgstr "%s na %s@%s"
67
 
68
-#: src/modules/module-zeroconf-discover.c:312
69
+#: src/modules/module-zeroconf-discover.c:348
70
 #, c-format
71
 msgid "%s on %s"
72
 msgstr "%s na %s"
73
 
74
-#: src/tools/pw-cat.c:1055
75
+#: src/tools/pw-cat.c:1075
76
 #, c-format
77
 msgid ""
78
 "%s [options] <file>\n"
79
@@ -92,7 +85,7 @@
80
 "  -v, --verbose                         Wyświetla więcej komunikatów\n"
81
 "\n"
82
 
83
-#: src/tools/pw-cat.c:1062
84
+#: src/tools/pw-cat.c:1082
85
 #, c-format
86
 msgid ""
87
 "  -R, --remote                          Remote daemon name\n"
88
@@ -130,7 +123,7 @@
89
 "docelowych dla --target\n"
90
 "\n"
91
 
92
-#: src/tools/pw-cat.c:1080
93
+#: src/tools/pw-cat.c:1100
94
 #, c-format
95
 msgid ""
96
 "      --rate                            Sample rate (req. for rec) (default "
97
@@ -166,7 +159,7 @@
98
 "(domyślnie %d)\n"
99
 "\n"
100
 
101
-#: src/tools/pw-cat.c:1097
102
+#: src/tools/pw-cat.c:1117
103
 msgid ""
104
 "  -p, --playback                        Playback mode\n"
105
 "  -r, --record                          Recording mode\n"
106
@@ -180,7 +173,7 @@
107
 "  -d, --dsd                             Tryb DSD\n"
108
 "\n"
109
 
110
-#: src/tools/pw-cli.c:2954
111
+#: src/tools/pw-cli.c:3050
112
 #, c-format
113
 msgid ""
114
 "%s [options] [command]\n"
115
@@ -198,12 +191,12 @@
116
 "  -r, --remote                          Nazwa zdalnej usługi\n"
117
 "\n"
118
 
119
-#: spa/plugins/alsa/acp/acp.c:310
120
+#: spa/plugins/alsa/acp/acp.c:321
121
 msgid "Pro Audio"
122
 msgstr "Dźwięk w zastosowaniach profesjonalnych"
123
 
124
-#: spa/plugins/alsa/acp/acp.c:433 spa/plugins/alsa/acp/alsa-mixer.c:4648
125
-#: spa/plugins/bluez5/bluez5-device.c:1135
126
+#: spa/plugins/alsa/acp/acp.c:444 spa/plugins/alsa/acp/alsa-mixer.c:4648
127
+#: spa/plugins/bluez5/bluez5-device.c:1159
128
 msgid "Off"
129
 msgstr "Wyłączone"
130
 
131
@@ -230,7 +223,7 @@
132
 
133
 #: spa/plugins/alsa/acp/alsa-mixer.c:2657
134
 #: spa/plugins/alsa/acp/alsa-mixer.c:2741
135
-#: spa/plugins/bluez5/bluez5-device.c:1292
136
+#: spa/plugins/bluez5/bluez5-device.c:1328
137
 msgid "Microphone"
138
 msgstr "Mikrofon"
139
 
140
@@ -296,7 +289,7 @@
141
 msgstr "Brak podbicia basów"
142
 
143
 #: spa/plugins/alsa/acp/alsa-mixer.c:2672
144
-#: spa/plugins/bluez5/bluez5-device.c:1297
145
+#: spa/plugins/bluez5/bluez5-device.c:1333
146
 msgid "Speaker"
147
 msgstr "Głośnik"
148
 
149
@@ -411,7 +404,7 @@
150
 
151
 #: spa/plugins/alsa/acp/alsa-mixer.c:4484
152
 #: spa/plugins/alsa/acp/alsa-mixer.c:4642
153
-#: spa/plugins/bluez5/bluez5-device.c:1282
154
+#: spa/plugins/bluez5/bluez5-device.c:1318
155
 msgid "Headset"
156
 msgstr "Słuchawki z mikrofonem"
157
 
158
@@ -623,65 +616,73 @@
159
 "Prawdopodobnie jest to błąd sterownika ALSA „%s”. Proszę zgłosić ten problem "
160
 "programistom usługi ALSA."
161
 
162
-#: spa/plugins/alsa/acp/channelmap.h:466
163
+#: spa/plugins/alsa/acp/channelmap.h:464
164
 msgid "(invalid)"
165
 msgstr "(nieprawidłowe)"
166
 
167
-#: spa/plugins/bluez5/bluez5-device.c:1145
168
+#: spa/plugins/alsa/acp/compat.c:189
169
+msgid "Built-in Audio"
170
+msgstr "Wbudowany dźwięk"
171
+
172
+#: spa/plugins/alsa/acp/compat.c:194
173
+msgid "Modem"
174
+msgstr "Modem"
175
+
176
+#: spa/plugins/bluez5/bluez5-device.c:1170
177
 msgid "Audio Gateway (A2DP Source & HSP/HFP AG)"
178
 msgstr "Bramka dźwięku (źródło A2DP i AG HSP/HFP)"
179
 
180
-#: spa/plugins/bluez5/bluez5-device.c:1168
181
+#: spa/plugins/bluez5/bluez5-device.c:1195
182
 #, c-format
183
 msgid "High Fidelity Playback (A2DP Sink, codec %s)"
184
 msgstr "Odtwarzanie o wysokiej dokładności (odpływ A2DP, kodek %s)"
185
 
186
-#: spa/plugins/bluez5/bluez5-device.c:1171
187
+#: spa/plugins/bluez5/bluez5-device.c:1198
188
 #, c-format
189
 msgid "High Fidelity Duplex (A2DP Source/Sink, codec %s)"
190
 msgstr "Dupleks o wysokiej dokładności (źródło/odpływ A2DP, kodek %s)"
191
 
192
-#: spa/plugins/bluez5/bluez5-device.c:1178
193
+#: spa/plugins/bluez5/bluez5-device.c:1206
194
 msgid "High Fidelity Playback (A2DP Sink)"
195
 msgstr "Odtwarzanie o wysokiej dokładności (odpływ A2DP)"
196
 
197
-#: spa/plugins/bluez5/bluez5-device.c:1180
198
+#: spa/plugins/bluez5/bluez5-device.c:1208
199
 msgid "High Fidelity Duplex (A2DP Source/Sink)"
200
 msgstr "Dupleks o wysokiej dokładności (źródło/odpływ A2DP)"
201
 
202
-#: spa/plugins/bluez5/bluez5-device.c:1207
203
+#: spa/plugins/bluez5/bluez5-device.c:1236
204
 #, c-format
205
 msgid "Headset Head Unit (HSP/HFP, codec %s)"
206
 msgstr "Jednostka główna słuchawek z mikrofonem (HSP/HFP, kodek %s)"
207
 
208
-#: spa/plugins/bluez5/bluez5-device.c:1211
209
+#: spa/plugins/bluez5/bluez5-device.c:1241
210
 msgid "Headset Head Unit (HSP/HFP)"
211
 msgstr "Jednostka główna słuchawek z mikrofonem (HSP/HFP)"
212
 
213
-#: spa/plugins/bluez5/bluez5-device.c:1287
214
+#: spa/plugins/bluez5/bluez5-device.c:1323
215
 msgid "Handsfree"
216
 msgstr "Zestaw głośnomówiący"
217
 
218
-#: spa/plugins/bluez5/bluez5-device.c:1302
219
+#: spa/plugins/bluez5/bluez5-device.c:1338
220
 msgid "Headphone"
221
 msgstr "Słuchawki"
222
 
223
-#: spa/plugins/bluez5/bluez5-device.c:1307
224
+#: spa/plugins/bluez5/bluez5-device.c:1343
225
 msgid "Portable"
226
 msgstr "Przenośne"
227
 
228
-#: spa/plugins/bluez5/bluez5-device.c:1312
229
+#: spa/plugins/bluez5/bluez5-device.c:1348
230
 msgid "Car"
231
 msgstr "Samochód"
232
 
233
-#: spa/plugins/bluez5/bluez5-device.c:1317
234
+#: spa/plugins/bluez5/bluez5-device.c:1353
235
 msgid "HiFi"
236
 msgstr "HiFi"
237
 
238
-#: spa/plugins/bluez5/bluez5-device.c:1322
239
+#: spa/plugins/bluez5/bluez5-device.c:1358
240
 msgid "Phone"
241
 msgstr "Telefon"
242
 
243
-#: spa/plugins/bluez5/bluez5-device.c:1328
244
+#: spa/plugins/bluez5/bluez5-device.c:1364
245
 msgid "Bluetooth"
246
 msgstr "Bluetooth"
247
pipewire-0.3.48.tar.gz/spa/include/spa/debug/format.h -> pipewire-0.3.49.tar.gz/spa/include/spa/debug/format.h Changed
10
 
1
@@ -69,7 +69,7 @@
2
        spa_debugn("%f", *(float *) body);
3
        break;
4
    case SPA_TYPE_Double:
5
-       spa_debugn("%g", *(double *) body);
6
+       spa_debugn("%f", *(double *) body);
7
        break;
8
    case SPA_TYPE_String:
9
        spa_debugn("%s", (char *) body);
10
pipewire-0.3.48.tar.gz/spa/include/spa/utils/hook.h -> pipewire-0.3.49.tar.gz/spa/include/spa/utils/hook.h Changed
61
 
1
@@ -261,8 +261,10 @@
2
  * The below (pseudo)code is a minimal example outlining the use of hooks:
3
  * \code{.c}
4
  * // the public interface
5
+ * #define VERSION_BAR_EVENTS 0 // version of the vtable
6
  * struct bar_events {
7
- *    uint32_t version;
8
+ *    uint32_t version; // NOTE: an integral member named `version`
9
+ *                      //       must be present in the vtable
10
  *    void (*boom)(void *data, const char *msg);
11
  * };
12
  *
13
@@ -272,17 +274,23 @@
14
  * };
15
  *
16
  * void party_add_event_listener(struct party *p, struct spa_hook *listener,
17
- *                               struct bar_events *events, void *data)
18
+ *                               const struct bar_events *events, void *data)
19
  * {
20
  *    spa_hook_list_append(&p->bar_list, listener, events, data);
21
  * }
22
  *
23
  * static void party_on(struct party *p)
24
  * {
25
- *     spa_hook_list_call(&p->list, struct bar_events,
26
- *                        boom, // function name
27
- *                        0 // hardcoded version,
28
- *                        "party on, wayne");
29
+ *     // NOTE: this is a macro, it evaluates to an integer,
30
+ *     //       which is the number of hooks called
31
+ *     spa_hook_list_call(&p->list,
32
+ *                        struct bar_events, // vtable type
33
+ *                        boom,              // function name
34
+ *                        0,                 // hardcoded version,
35
+ *                                           //     usually the version in which `boom`
36
+ *                                           //     has been added to the vtable
37
+ *                        "party on, wayne"  // function argument(s)
38
+ *                        );
39
  * }
40
  * \endcode
41
  *
42
@@ -293,7 +301,8 @@
43
  *      printf("%s", msg);
44
  * }
45
  *
46
- * static const struct bar_events {
47
+ * static const struct bar_events events = {
48
+ *    .version = VERSION_BAR_EVENTS, // version of the implemented interface
49
  *    .boom = boom_cb,
50
  * };
51
  *
52
@@ -302,7 +311,7 @@
53
  *      struct spa_hook hook;
54
  *      struct party *p = start_the_party();
55
  *
56
- *      party_add_event_listener(p, &hook, boom_cb, userdata);
57
+ *      party_add_event_listener(p, &hook, &events, userdata);
58
  *
59
  *      mainloop();
60
  *      return 0;
61
pipewire-0.3.48.tar.gz/spa/include/spa/utils/json.h -> pipewire-0.3.49.tar.gz/spa/include/spa/utils/json.h Changed
53
 
1
@@ -34,8 +34,11 @@
2
 #include <stdlib.h>
3
 #include <stdint.h>
4
 #include <string.h>
5
+#include <math.h>
6
+#include <float.h>
7
 
8
 #include <spa/utils/defs.h>
9
+#include <spa/utils/string.h>
10
 
11
 /** \defgroup spa_json JSON
12
  * Relaxed JSON variant parsing
13
@@ -237,9 +240,10 @@
14
 static inline int spa_json_parse_float(const char *val, int len, float *result)
15
 {
16
    char *end;
17
-   *result = strtof(val, &end);
18
-   return end == val + len;
19
+   *result = spa_strtof(val, &end);
20
+   return len > 0 && end == val + len;
21
 }
22
+
23
 static inline bool spa_json_is_float(const char *val, int len)
24
 {
25
    float dummy;
26
@@ -254,12 +258,25 @@
27
    return spa_json_parse_float(value, len, res);
28
 }
29
 
30
+static inline char *spa_json_format_float(char *str, int size, float val)
31
+{
32
+   if (SPA_UNLIKELY(!isnormal(val))) {
33
+       if (val == INFINITY)
34
+           val = FLT_MAX;
35
+       else if (val == -INFINITY)
36
+           val = FLT_MIN;
37
+       else
38
+           val = 0.0f;
39
+   }
40
+   return spa_dtoa(str, size, val);
41
+}
42
+
43
 /* int */
44
 static inline int spa_json_parse_int(const char *val, int len, int *result)
45
 {
46
    char *end;
47
    *result = strtol(val, &end, 0);
48
-   return end == val + len;
49
+   return len > 0 && end == val + len;
50
 }
51
 static inline bool spa_json_is_int(const char *val, int len)
52
 {
53
pipewire-0.3.48.tar.gz/spa/include/spa/utils/string.h -> pipewire-0.3.49.tar.gz/spa/include/spa/utils/string.h Changed
103
 
1
@@ -32,6 +32,8 @@
2
 #include <stdarg.h>
3
 #include <stdbool.h>
4
 #include <errno.h>
5
+#include <stdlib.h>
6
+#include <locale.h>
7
 
8
 #include <spa/utils/defs.h>
9
 
10
@@ -264,6 +266,27 @@
11
 }
12
 
13
 /**
14
+ * Convert \a str to a float in the C locale.
15
+ *
16
+ * If \a endptr is not NULL, a pointer to the character after the last character
17
+ * used in the conversion is stored in the location referenced by endptr.
18
+ *
19
+ * \return the result float.
20
+ */
21
+static inline float spa_strtof(const char *str, char **endptr)
22
+{
23
+   static locale_t locale = NULL;
24
+   float v;
25
+   if (SPA_UNLIKELY(locale == NULL))
26
+       locale = newlocale(LC_ALL_MASK, "C", NULL);
27
+   if (locale != NULL)
28
+       v = strtof_l(str, endptr, locale);
29
+   else
30
+       v = strtof(str, endptr);
31
+   return v;
32
+}
33
+
34
+/**
35
  * Convert \a str to a float and store the result in \a val.
36
  *
37
  * On failure, the value of \a val is unmodified.
38
@@ -277,9 +300,8 @@
39
 
40
    if (!str || *str =='\0')
41
        return false;
42
-
43
    errno = 0;
44
-   v = strtof(str, &endptr);
45
+   v = spa_strtof(str, &endptr);
46
    if (errno != 0 || *endptr != '\0')
47
        return false;
48
 
49
@@ -288,6 +310,27 @@
50
 }
51
 
52
 /**
53
+ * Convert \a str to a double in the C locale.
54
+ *
55
+ * If \a endptr is not NULL, a pointer to the character after the last character
56
+ * used in the conversion is stored in the location referenced by endptr.
57
+ *
58
+ * \return the result float.
59
+ */
60
+static inline double spa_strtod(const char *str, char **endptr)
61
+{
62
+   static locale_t locale = NULL;
63
+   double v;
64
+   if (SPA_UNLIKELY(locale == NULL))
65
+       locale = newlocale(LC_ALL_MASK, "C", NULL);
66
+   if (locale != NULL)
67
+       v = strtod_l(str, endptr, locale);
68
+   else
69
+       v = strtod(str, endptr);
70
+   return v;
71
+}
72
+
73
+/**
74
  * Convert \a str to a double and store the result in \a val.
75
  *
76
  * On failure, the value of \a val is unmodified.
77
@@ -303,7 +346,7 @@
78
        return false;
79
 
80
    errno = 0;
81
-   v = strtod(str, &endptr);
82
+   v = spa_strtod(str, &endptr);
83
    if (errno != 0 || *endptr != '\0')
84
        return false;
85
 
86
@@ -311,6 +354,16 @@
87
    return true;
88
 }
89
 
90
+static inline char *spa_dtoa(char *str, size_t size, double val)
91
+{
92
+   int i, l;
93
+   l = spa_scnprintf(str, size, "%f", val);
94
+   for (i = 0; i < l; i++)
95
+       if (str[i] == ',')
96
+           str[i] = '.';
97
+   return str;
98
+}
99
+
100
 /**
101
  * \}
102
  */
103
pipewire-0.3.48.tar.gz/spa/plugins/aec/aec-null.c -> pipewire-0.3.49.tar.gz/spa/plugins/aec/aec-null.c Changed
19
 
1
@@ -151,7 +151,7 @@
2
    return 1;
3
 }
4
 
5
-const struct spa_handle_factory spa_aec_exaudio_factory = {
6
+const struct spa_handle_factory spa_aec_null_factory = {
7
    SPA_VERSION_HANDLE_FACTORY,
8
    SPA_NAME_AEC,
9
    NULL,
10
@@ -169,7 +169,7 @@
11
 
12
    switch (*index) {
13
    case 0:
14
-       *factory = &spa_aec_exaudio_factory;
15
+       *factory = &spa_aec_null_factory;
16
        break;
17
    default:
18
        return 0;
19
pipewire-0.3.48.tar.gz/spa/plugins/aec/aec-webrtc.cpp -> pipewire-0.3.49.tar.gz/spa/plugins/aec/aec-webrtc.cpp Changed
19
 
1
@@ -250,7 +250,7 @@
2
    return 1;
3
 }
4
 
5
-const struct spa_handle_factory spa_aec_exaudio_factory = {
6
+const struct spa_handle_factory spa_aec_webrtc_factory = {
7
    SPA_VERSION_HANDLE_FACTORY,
8
    SPA_NAME_AEC,
9
    NULL,
10
@@ -268,7 +268,7 @@
11
 
12
    switch (*index) {
13
    case 0:
14
-       *factory = &spa_aec_exaudio_factory;
15
+       *factory = &spa_aec_webrtc_factory;
16
        break;
17
    default:
18
        return 0;
19
pipewire-0.3.48.tar.gz/spa/plugins/alsa/alsa-pcm-sink.c -> pipewire-0.3.49.tar.gz/spa/plugins/alsa/alsa-pcm-sink.c Changed
48
 
1
@@ -160,7 +160,7 @@
2
                SPA_TYPE_OBJECT_PropInfo, id,
3
                SPA_PROP_INFO_id,   SPA_POD_Id(SPA_PROP_latencyOffsetNsec),
4
                SPA_PROP_INFO_description, SPA_POD_String("Latency offset (ns)"),
5
-               SPA_PROP_INFO_type, SPA_POD_CHOICE_RANGE_Long(0LL, 0LL, INT64_MAX));
6
+               SPA_PROP_INFO_type, SPA_POD_CHOICE_RANGE_Long(0LL, 0LL, 2 * SPA_NSEC_PER_SEC));
7
            break;
8
        case 4:
9
            if (!this->is_iec958 && !this->is_hdmi)
10
@@ -310,20 +310,18 @@
11
    case SPA_PARAM_Props:
12
    {
13
        struct props *p = &this->props;
14
-       struct spa_process_latency_info info;
15
        struct spa_pod *iec958_codecs = NULL, *params = NULL;
16
+       int64_t lat_ns = -1;
17
 
18
        if (param == NULL) {
19
            reset_props(p);
20
            return 0;
21
        }
22
 
23
-       info = this->process_latency;
24
-
25
        spa_pod_parse_object(param,
26
            SPA_TYPE_OBJECT_Props, NULL,
27
            SPA_PROP_device,       SPA_POD_OPT_Stringn(p->device, sizeof(p->device)),
28
-           SPA_PROP_latencyOffsetNsec,   SPA_POD_OPT_Long(&info.ns),
29
+           SPA_PROP_latencyOffsetNsec,   SPA_POD_OPT_Long(&lat_ns),
30
            SPA_PROP_iec958Codecs, SPA_POD_OPT_Pod(&iec958_codecs),
31
            SPA_PROP_params,       SPA_POD_OPT_Pod(&params));
32
 
33
@@ -342,8 +340,12 @@
34
            this->port_params[PORT_EnumFormat].user++;
35
        }
36
        spa_alsa_parse_prop_params(this, params);
37
-       handle_process_latency(this, &info);
38
-
39
+       if (lat_ns != -1) {
40
+           struct spa_process_latency_info info;
41
+           info = this->process_latency;
42
+           info.ns = lat_ns;
43
+           handle_process_latency(this, &info);
44
+       }
45
        emit_node_info(this, false);
46
        emit_port_info(this, false);
47
        break;
48
pipewire-0.3.48.tar.gz/spa/plugins/alsa/alsa-pcm-source.c -> pipewire-0.3.49.tar.gz/spa/plugins/alsa/alsa-pcm-source.c Changed
43
 
1
@@ -160,7 +160,7 @@
2
                SPA_TYPE_OBJECT_PropInfo, id,
3
                SPA_PROP_INFO_id,   SPA_POD_Id(SPA_PROP_latencyOffsetNsec),
4
                SPA_PROP_INFO_description, SPA_POD_String("Latency offset (ns)"),
5
-               SPA_PROP_INFO_type, SPA_POD_CHOICE_RANGE_Long(0LL, 0LL, INT64_MAX));
6
+               SPA_PROP_INFO_type, SPA_POD_CHOICE_RANGE_Long(0LL, 0LL, 2 * SPA_NSEC_PER_SEC));
7
            break;
8
        default:
9
            param = spa_alsa_enum_propinfo(this, result.index - 4, &b);
10
@@ -290,24 +290,27 @@
11
    case SPA_PARAM_Props:
12
    {
13
        struct props *p = &this->props;
14
-       struct spa_process_latency_info info;
15
        struct spa_pod *params = NULL;
16
+       int64_t lat_ns = -1;
17
 
18
        if (param == NULL) {
19
            reset_props(p);
20
            return 0;
21
        }
22
 
23
-       info = this->process_latency;
24
-
25
        spa_pod_parse_object(param,
26
            SPA_TYPE_OBJECT_Props, NULL,
27
            SPA_PROP_device,       SPA_POD_OPT_Stringn(p->device, sizeof(p->device)),
28
-           SPA_PROP_latencyOffsetNsec,   SPA_POD_OPT_Long(&info.ns),
29
+           SPA_PROP_latencyOffsetNsec,   SPA_POD_OPT_Long(&lat_ns),
30
            SPA_PROP_params,       SPA_POD_OPT_Pod(&params));
31
 
32
        spa_alsa_parse_prop_params(this, params);
33
-       handle_process_latency(this, &info);
34
+       if (lat_ns != -1) {
35
+           struct spa_process_latency_info info;
36
+           info = this->process_latency;
37
+           info.ns = lat_ns;
38
+           handle_process_latency(this, &info);
39
+       }
40
 
41
        emit_node_info(this, false);
42
        emit_port_info(this, false);
43
pipewire-0.3.48.tar.gz/spa/plugins/alsa/alsa-pcm.c -> pipewire-0.3.49.tar.gz/spa/plugins/alsa/alsa-pcm.c Changed
102
 
1
@@ -237,7 +237,7 @@
2
            SPA_TYPE_OBJECT_PropInfo, SPA_PARAM_PropInfo,
3
            SPA_PROP_INFO_name, SPA_POD_String("api.alsa.period-size"),
4
            SPA_PROP_INFO_description, SPA_POD_String("Period Size"),
5
-           SPA_PROP_INFO_type, SPA_POD_Int(state->default_period_size),
6
+           SPA_PROP_INFO_type, SPA_POD_CHOICE_RANGE_Int(state->default_period_size, 0, 8192),
7
            SPA_PROP_INFO_params, SPA_POD_Bool(true));
8
        break;
9
    case 6:
10
@@ -245,7 +245,7 @@
11
            SPA_TYPE_OBJECT_PropInfo, SPA_PARAM_PropInfo,
12
            SPA_PROP_INFO_name, SPA_POD_String("api.alsa.period-num"),
13
            SPA_PROP_INFO_description, SPA_POD_String("Number of Periods"),
14
-           SPA_PROP_INFO_type, SPA_POD_Int(state->default_period_num),
15
+           SPA_PROP_INFO_type, SPA_POD_CHOICE_RANGE_Int(state->default_period_num, 0, 1024),
16
            SPA_PROP_INFO_params, SPA_POD_Bool(true));
17
        break;
18
    case 7:
19
@@ -253,7 +253,7 @@
20
            SPA_TYPE_OBJECT_PropInfo, SPA_PARAM_PropInfo,
21
            SPA_PROP_INFO_name, SPA_POD_String("api.alsa.headroom"),
22
            SPA_PROP_INFO_description, SPA_POD_String("Headroom"),
23
-           SPA_PROP_INFO_type, SPA_POD_Int(state->default_headroom),
24
+           SPA_PROP_INFO_type, SPA_POD_CHOICE_RANGE_Int(state->default_headroom, 0, 8192),
25
            SPA_PROP_INFO_params, SPA_POD_Bool(true));
26
        break;
27
    case 8:
28
@@ -261,7 +261,7 @@
29
            SPA_TYPE_OBJECT_PropInfo, SPA_PARAM_PropInfo,
30
            SPA_PROP_INFO_name, SPA_POD_String("api.alsa.start-delay"),
31
            SPA_PROP_INFO_description, SPA_POD_String("Start Delay"),
32
-           SPA_PROP_INFO_type, SPA_POD_Int(state->default_start_delay),
33
+           SPA_PROP_INFO_type, SPA_POD_CHOICE_RANGE_Int(state->default_start_delay, 0, 8192),
34
            SPA_PROP_INFO_params, SPA_POD_Bool(true));
35
        break;
36
    case 9:
37
@@ -269,7 +269,7 @@
38
            SPA_TYPE_OBJECT_PropInfo, SPA_PARAM_PropInfo,
39
            SPA_PROP_INFO_name, SPA_POD_String("api.alsa.disable-mmap"),
40
            SPA_PROP_INFO_description, SPA_POD_String("Disable MMAP"),
41
-           SPA_PROP_INFO_type, SPA_POD_Bool(state->disable_mmap),
42
+           SPA_PROP_INFO_type, SPA_POD_CHOICE_Bool(state->disable_mmap),
43
            SPA_PROP_INFO_params, SPA_POD_Bool(true));
44
        break;
45
    case 10:
46
@@ -277,7 +277,7 @@
47
            SPA_TYPE_OBJECT_PropInfo, SPA_PARAM_PropInfo,
48
            SPA_PROP_INFO_name, SPA_POD_String("api.alsa.disable-batch"),
49
            SPA_PROP_INFO_description, SPA_POD_String("Disable Batch"),
50
-           SPA_PROP_INFO_type, SPA_POD_Bool(state->disable_batch),
51
+           SPA_PROP_INFO_type, SPA_POD_CHOICE_Bool(state->disable_batch),
52
            SPA_PROP_INFO_params, SPA_POD_Bool(true));
53
        break;
54
    case 11:
55
@@ -285,7 +285,7 @@
56
            SPA_TYPE_OBJECT_PropInfo, SPA_PARAM_PropInfo,
57
            SPA_PROP_INFO_name, SPA_POD_String("api.alsa.use-chmap"),
58
            SPA_PROP_INFO_description, SPA_POD_String("Use the driver channelmap"),
59
-           SPA_PROP_INFO_type, SPA_POD_Bool(state->props.use_chmap),
60
+           SPA_PROP_INFO_type, SPA_POD_CHOICE_Bool(state->props.use_chmap),
61
            SPA_PROP_INFO_params, SPA_POD_Bool(true));
62
        break;
63
    case 12:
64
@@ -293,7 +293,7 @@
65
            SPA_TYPE_OBJECT_PropInfo, SPA_PARAM_PropInfo,
66
            SPA_PROP_INFO_name, SPA_POD_String("api.alsa.multi-rate"),
67
            SPA_PROP_INFO_description, SPA_POD_String("Support multiple rates"),
68
-           SPA_PROP_INFO_type, SPA_POD_Bool(state->multi_rate),
69
+           SPA_PROP_INFO_type, SPA_POD_CHOICE_Bool(state->multi_rate),
70
            SPA_PROP_INFO_params, SPA_POD_Bool(true));
71
        break;
72
    case 13:
73
@@ -301,7 +301,8 @@
74
            SPA_TYPE_OBJECT_PropInfo, SPA_PARAM_PropInfo,
75
            SPA_PROP_INFO_name, SPA_POD_String("latency.internal.rate"),
76
            SPA_PROP_INFO_description, SPA_POD_String("Internal latency in samples"),
77
-           SPA_PROP_INFO_type, SPA_POD_Int(state->process_latency.rate),
78
+           SPA_PROP_INFO_type, SPA_POD_CHOICE_RANGE_Int(state->process_latency.rate,
79
+               0, 65536),
80
            SPA_PROP_INFO_params, SPA_POD_Bool(true));
81
        break;
82
    case 14:
83
@@ -309,7 +310,8 @@
84
            SPA_TYPE_OBJECT_PropInfo, SPA_PARAM_PropInfo,
85
            SPA_PROP_INFO_name, SPA_POD_String("latency.internal.ns"),
86
            SPA_PROP_INFO_description, SPA_POD_String("Internal latency in nanoseconds"),
87
-           SPA_PROP_INFO_type, SPA_POD_Long(state->process_latency.ns),
88
+           SPA_PROP_INFO_type, SPA_POD_CHOICE_RANGE_Long(state->process_latency.ns,
89
+               0, 2 * SPA_NSEC_PER_SEC),
90
            SPA_PROP_INFO_params, SPA_POD_Bool(true));
91
        break;
92
    case 15:
93
@@ -429,7 +431,7 @@
94
        } else
95
            continue;
96
 
97
-       spa_log_debug(state->log, "key:'%s' val:'%s'", name, value);
98
+       spa_log_info(state->log, "key:'%s' val:'%s'", name, value);
99
        alsa_set_param(state, name, value);
100
        changed++;
101
    }
102
pipewire-0.3.48.tar.gz/spa/plugins/alsa/test-timer.c -> pipewire-0.3.49.tar.gz/spa/plugins/alsa/test-timer.c Changed
174
 
1
@@ -25,6 +25,7 @@
2
 #include <stdio.h>
3
 #include <stdbool.h>
4
 #include <limits.h>
5
+#include <getopt.h>
6
 #include <math.h>
7
 #include <sys/timerfd.h>
8
 
9
@@ -42,6 +43,8 @@
10
 #define BW_PERIOD  (NSEC_PER_SEC * 3)
11
 
12
 struct state {
13
+   const char *device;
14
+   unsigned int format;
15
    unsigned int rate;
16
    unsigned int channels;
17
    snd_pcm_uframes_t period;
18
@@ -77,27 +80,39 @@
19
    }               \
20
 }
21
 
22
+#define LOOP(type,areas,scale) {                                   \
23
+   uint32_t i, j;                                          \
24
+   type *samples, v;                                       \
25
+   samples = (type*)((uint8_t*)areas[0].addr + (areas[0].first + offset*areas[0].step) / 8);   \
26
+   for (i = 0; i < frames; i++) {                                  \
27
+       state->accumulator += M_PI_M2 * 440 / state->rate;                  \
28
+       if (state->accumulator >= M_PI_M2)                          \
29
+           state->accumulator -= M_PI_M2;                          \
30
+       v = sin(state->accumulator) * scale;                            \
31
+       for (j = 0; j < state->channels; j++)                           \
32
+           *samples++ = v;                                 \
33
+   }                                               \
34
+}
35
+
36
 static int write_period(struct state *state)
37
 {
38
    snd_pcm_uframes_t frames = state->period;
39
    snd_pcm_uframes_t offset;
40
    const snd_pcm_channel_area_t* areas;
41
-   uint32_t i, j;
42
-   int32_t *samples, v;
43
 
44
    snd_pcm_mmap_begin(state->hndl, &areas, &offset, &frames);
45
 
46
-   samples = (int32_t*)((uint8_t*)areas[0].addr + (areas[0].first + offset*areas[0].step) / 8);
47
-
48
-   for (i = 0; i < frames; i++) {
49
-       state->accumulator += M_PI_M2 * 440 / state->rate;
50
-       if (state->accumulator >= M_PI_M2)
51
-           state->accumulator -= M_PI_M2;
52
-       v = sin(state->accumulator) * 0x7fffffff;
53
-
54
-       for (j = 0; j < state->channels; j++)
55
-           *samples++ = v;
56
+   switch (state->format) {
57
+   case SND_PCM_FORMAT_S32_LE:
58
+       LOOP(int32_t, areas, 0x7fffffff);
59
+       break;
60
+   case SND_PCM_FORMAT_S16_LE:
61
+       LOOP(int16_t, areas, 0x7fff);
62
+       break;
63
+   default:
64
+       break;
65
    }
66
+
67
    snd_pcm_mmap_commit(state->hndl, offset, frames) ;
68
 
69
    return 0;
70
@@ -156,27 +171,89 @@
71
    return 0;
72
 }
73
 
74
+static unsigned int format_from_string(const char *str)
75
+{
76
+   if (strcmp(str, "S32_LE") == 0)
77
+       return SND_PCM_FORMAT_S32_LE;
78
+   else if (strcmp(str, "S32_BE") == 0)
79
+       return SND_PCM_FORMAT_S32_BE;
80
+   else if (strcmp(str, "S24_LE") == 0)
81
+       return SND_PCM_FORMAT_S24_LE;
82
+   else if (strcmp(str, "S24_BE") == 0)
83
+       return SND_PCM_FORMAT_S24_BE;
84
+   else if (strcmp(str, "S24_3LE") == 0)
85
+       return SND_PCM_FORMAT_S24_3LE;
86
+   else if (strcmp(str, "S24_3_BE") == 0)
87
+       return SND_PCM_FORMAT_S24_3BE;
88
+   else if (strcmp(str, "S16_LE") == 0)
89
+       return SND_PCM_FORMAT_S16_LE;
90
+   else if (strcmp(str, "S16_BE") == 0)
91
+       return SND_PCM_FORMAT_S16_BE;
92
+   return 0;
93
+}
94
+
95
+static void show_help(const char *name, bool error)
96
+{
97
+        fprintf(error ? stderr : stdout, "%s [options]\n"
98
+       "  -h, --help                            Show this help\n"
99
+       "  -D, --device                          device name (default %s)\n",
100
+       name, DEFAULT_DEVICE);
101
+}
102
+
103
 int main(int argc, char *argv[])
104
 {
105
    struct state state = { 0, };
106
-   const char *device = DEFAULT_DEVICE;
107
    snd_pcm_hw_params_t *hparams;
108
    snd_pcm_sw_params_t *sparams;
109
    struct timespec now;
110
-
111
-   CHECK(snd_pcm_open(&state.hndl, device, SND_PCM_STREAM_PLAYBACK, 0), "open %s failed", device);
112
-
113
+   char c;
114
+   static const struct option long_options[] = {
115
+       { "help",   no_argument,        NULL, 'h' },
116
+       { "device", required_argument,  NULL, 'D' },
117
+       { "format", required_argument,  NULL, 'f' },
118
+       { "rate",   required_argument,  NULL, 'r' },
119
+       { "channels",   required_argument,  NULL, 'c' },
120
+       { NULL, 0, NULL, 0}
121
+   };
122
+   state.device = DEFAULT_DEVICE;
123
+   state.format = SND_PCM_FORMAT_S16_LE;
124
    state.rate = 44100;
125
    state.channels = 2;
126
    state.period = 1024;
127
 
128
+   while ((c = getopt_long(argc, argv, "hD:f:r:c:", long_options, NULL)) != -1) {
129
+       switch (c) {
130
+       case 'h':
131
+           show_help(argv[0], false);
132
+           return 0;
133
+       case 'D':
134
+           state.device = optarg;
135
+           break;
136
+       case 'f':
137
+           state.format = format_from_string(optarg);
138
+           break;
139
+       case 'r':
140
+           state.rate = atoi(optarg);
141
+           break;
142
+       case 'c':
143
+           state.channels = atoi(optarg);
144
+           break;
145
+       default:
146
+           show_help(argv[0], true);
147
+           return -1;
148
+       }
149
+   }
150
+
151
+   CHECK(snd_pcm_open(&state.hndl, state.device, SND_PCM_STREAM_PLAYBACK, 0),
152
+           "open %s failed", state.device);
153
+
154
    /* hw params */
155
    snd_pcm_hw_params_alloca(&hparams);
156
    snd_pcm_hw_params_any(state.hndl, hparams);
157
    CHECK(snd_pcm_hw_params_set_access(state.hndl, hparams,
158
                SND_PCM_ACCESS_MMAP_INTERLEAVED), "set interleaved");
159
    CHECK(snd_pcm_hw_params_set_format(state.hndl, hparams,
160
-               SND_PCM_FORMAT_S32_LE), "set format");
161
+               state.format), "set format");
162
    CHECK(snd_pcm_hw_params_set_channels_near(state.hndl, hparams,
163
                &state.channels), "set channels");
164
    CHECK(snd_pcm_hw_params_set_rate_near(state.hndl, hparams,
165
@@ -186,7 +263,7 @@
166
    CHECK(snd_pcm_hw_params_get_buffer_size(hparams, &state.buffer_frames), "get_buffer_size_max");
167
 
168
    fprintf(stdout, "opened format:%s rate:%u channels:%u\n",
169
-           snd_pcm_format_name(SND_PCM_FORMAT_S32_LE),
170
+           snd_pcm_format_name(state.format),
171
            state.rate, state.channels);
172
 
173
    snd_pcm_sw_params_alloca(&sparams);
174
pipewire-0.3.48.tar.gz/spa/plugins/audioconvert/audioadapter.c -> pipewire-0.3.49.tar.gz/spa/plugins/audioconvert/audioadapter.c Changed
9
 
1
@@ -542,7 +542,6 @@
2
    res = spa_pod_parse_object(format,
3
            SPA_TYPE_OBJECT_Format, NULL,
4
            SPA_FORMAT_AUDIO_format, SPA_POD_OPT_Id(&info->format),
5
-           SPA_FORMAT_AUDIO_rate, SPA_POD_OPT_Int(&info->rate),
6
            SPA_FORMAT_AUDIO_channels, SPA_POD_OPT_Int(&info->channels),
7
            SPA_FORMAT_AUDIO_position, SPA_POD_OPT_Pod(&position));
8
    if (position == NULL ||
9
pipewire-0.3.48.tar.gz/spa/plugins/audioconvert/channelmix-ops-c.c -> pipewire-0.3.49.tar.gz/spa/plugins/audioconvert/channelmix-ops-c.c Changed
58
 
1
@@ -246,6 +246,16 @@
2
        for (i = 0; i < n_dst; i++)
3
            memset(d[i], 0, n_samples * sizeof(float));
4
    }
5
+   else if (mix->upmix == CHANNELMIX_UPMIX_SIMPLE) {
6
+       for (n = 0; n < n_samples; n++) {
7
+           float c = s[0][n] + s[1][n];
8
+           d[0][n] = s[0][n] * v0;
9
+           d[1][n] = s[1][n] * v1;
10
+           d[2][n] = c * v2;
11
+           d[3][n] = c;
12
+       }
13
+       lr4_process(&mix->lr4[3], d[3], d[3], v3, n_samples);
14
+   }
15
    else if (v0 == 1.0f && v1 == 1.0f) {
16
        for (n = 0; n < n_samples; n++) {
17
            float c = s[0][n] + s[1][n];
18
@@ -289,6 +299,18 @@
19
        for (i = 0; i < n_dst; i++)
20
            memset(d[i], 0, n_samples * sizeof(float));
21
    }
22
+   else if (mix->upmix == CHANNELMIX_UPMIX_SIMPLE) {
23
+       for (n = 0; n < n_samples; n++) {
24
+           float c = s[0][n] + s[1][n];
25
+           d[0][n] = s[0][n] * v0;
26
+           d[1][n] = s[1][n] * v1;
27
+           d[2][n] = c * v2;
28
+           d[3][n] = c;
29
+           d[4][n] = s[0][n] * v4;
30
+           d[5][n] = s[1][n] * v5;
31
+       }
32
+       lr4_process(&mix->lr4[3], d[3], d[3], v3, n_samples);
33
+   }
34
    else if (v0 == 1.0f && v1 == 1.0f) {
35
        for (n = 0; n < n_samples; n++) {
36
            float c = s[0][n] + s[1][n];
37
@@ -347,6 +369,20 @@
38
        for (i = 0; i < n_dst; i++)
39
            memset(d[i], 0, n_samples * sizeof(float));
40
    }
41
+   else if (mix->upmix == CHANNELMIX_UPMIX_SIMPLE) {
42
+       for (n = 0; n < n_samples; n++) {
43
+           float c = s[0][n] + s[1][n];
44
+           d[0][n] = s[0][n] * v0;
45
+           d[1][n] = s[1][n] * v1;
46
+           d[2][n] = c * v2;
47
+           d[3][n] = c;
48
+           d[4][n] = s[0][n] * v4;
49
+           d[5][n] = s[1][n] * v5;
50
+           d[6][n] = s[0][n] * v6;
51
+           d[7][n] = s[1][n] * v7;
52
+       }
53
+       lr4_process(&mix->lr4[3], d[3], d[3], v3, n_samples);
54
+   }
55
    else if (v0 == 1.0f && v1 == 1.0f && v4 == 1.0f && v5 == 1.0f) {
56
        for (n = 0; n < n_samples; n++) {
57
            float c = s[0][n] + s[1][n];
58
pipewire-0.3.48.tar.gz/spa/plugins/audioconvert/channelmix-ops.c -> pipewire-0.3.49.tar.gz/spa/plugins/audioconvert/channelmix-ops.c Changed
11
 
1
@@ -210,7 +210,8 @@
2
    unassigned = src_mask & ~dst_mask;
3
    keep = dst_mask & ~src_mask;
4
 
5
-   if (!SPA_FLAG_IS_SET(mix->options, CHANNELMIX_OPTION_UPMIX))
6
+   if (!SPA_FLAG_IS_SET(mix->options, CHANNELMIX_OPTION_UPMIX) ||
7
+       mix->upmix == CHANNELMIX_UPMIX_NONE)
8
        keep = 0;
9
 
10
    keep |= FRONT;
11
pipewire-0.3.48.tar.gz/spa/plugins/audioconvert/channelmix-ops.h -> pipewire-0.3.49.tar.gz/spa/plugins/audioconvert/channelmix-ops.h Changed
47
 
1
@@ -26,6 +26,7 @@
2
 #include <stdio.h>
3
 
4
 #include <spa/utils/defs.h>
5
+#include <spa/utils/string.h>
6
 #include <spa/param/audio/raw.h>
7
 
8
 #undef SPA_LOG_TOPIC_DEFAULT
9
@@ -61,6 +62,10 @@
10
 #define CHANNELMIX_OPTION_NORMALIZE    (1<<1)      /**< normalize volumes */
11
 #define CHANNELMIX_OPTION_UPMIX        (1<<2)      /**< do simple upmixing */
12
    uint32_t options;
13
+#define CHANNELMIX_UPMIX_NONE      (0)     /**< disable upmixing */
14
+#define CHANNELMIX_UPMIX_SIMPLE        (1)     /**< simple upmixing */
15
+#define CHANNELMIX_UPMIX_PSD       (2)     /**< Passive Surround Decoding upmixing */
16
+   uint32_t upmix;
17
 
18
    struct spa_log *log;
19
 
20
@@ -97,6 +102,26 @@
21
 
22
 int channelmix_init(struct channelmix *mix);
23
 
24
+static const struct channelmix_upmix_info {
25
+   const char *label;
26
+   const char *description;
27
+   uint32_t upmix;
28
+} channelmix_upmix_info[] = {
29
+   [CHANNELMIX_UPMIX_NONE] = { "none", "Disabled", CHANNELMIX_UPMIX_NONE },
30
+   [CHANNELMIX_UPMIX_SIMPLE] = { "simple", "Simple upmixing", CHANNELMIX_UPMIX_SIMPLE },
31
+   [CHANNELMIX_UPMIX_PSD] = { "psd", "Passive Surround Decoding", CHANNELMIX_UPMIX_PSD }
32
+};
33
+
34
+static inline uint32_t channelmix_upmix_from_label(const char *label)
35
+{
36
+   uint32_t i;
37
+   for (i = 0; i < SPA_N_ELEMENTS(channelmix_upmix_info); i++) {
38
+       if (spa_streq(channelmix_upmix_info[i].label, label))
39
+           return channelmix_upmix_info[i].upmix;
40
+   }
41
+   return CHANNELMIX_UPMIX_NONE;
42
+}
43
+
44
 #define channelmix_process(mix,...)    (mix)->process(mix, __VA_ARGS__)
45
 #define channelmix_set_volume(mix,...) (mix)->set_volume(mix, __VA_ARGS__)
46
 #define channelmix_free(mix)       (mix)->free(mix)
47
pipewire-0.3.48.tar.gz/spa/plugins/audioconvert/channelmix.c -> pipewire-0.3.49.tar.gz/spa/plugins/audioconvert/channelmix.c Changed
142
 
1
@@ -292,7 +292,8 @@
2
 {
3
    struct volumes *vol;
4
 
5
-   if (this->mix.set_volume == NULL)
6
+   if (this->mix.set_volume == NULL ||
7
+       this->props.disabled)
8
        return;
9
 
10
    if (this->props.have_soft_volume)
11
@@ -545,6 +546,31 @@
12
                    this->mix.hilbert_taps, 0, MAX_TAPS),
13
                SPA_PROP_INFO_params, SPA_POD_Bool(true));
14
            break;
15
+       case 17:
16
+       {
17
+           struct spa_pod_frame f[2];
18
+           uint32_t i;
19
+           spa_pod_builder_push_object(&b, &f[0],
20
+               SPA_TYPE_OBJECT_PropInfo, id);
21
+           spa_pod_builder_add(&b,
22
+               SPA_PROP_INFO_name, SPA_POD_String("channelmix.upmix-method"),
23
+               SPA_PROP_INFO_description, SPA_POD_String("Upmix Method to use"),
24
+               SPA_PROP_INFO_type, SPA_POD_String(
25
+                   channelmix_upmix_info[this->mix.upmix].label),
26
+               0);
27
+           spa_pod_builder_prop(&b, SPA_PROP_INFO_labels, 0);
28
+           spa_pod_builder_push_struct(&b, &f[1]);
29
+           for (i = 0; i < SPA_N_ELEMENTS(channelmix_upmix_info); i++) {
30
+               spa_pod_builder_string(&b, channelmix_upmix_info[i].label);
31
+               spa_pod_builder_string(&b, channelmix_upmix_info[i].description);
32
+           }
33
+           spa_pod_builder_pop(&b, &f[1]);
34
+           spa_pod_builder_add(&b,
35
+               SPA_PROP_INFO_params, SPA_POD_Bool(true),
36
+               0);
37
+           param = spa_pod_builder_pop(&b, &f[0]);
38
+           break;
39
+       }
40
        default:
41
            return 0;
42
        }
43
@@ -604,6 +630,8 @@
44
            spa_pod_builder_float(&b, this->mix.widen);
45
            spa_pod_builder_string(&b, "channelmix.hilbert-taps");
46
            spa_pod_builder_int(&b, this->mix.hilbert_taps);
47
+           spa_pod_builder_string(&b, "channelmix.upmix-method");
48
+           spa_pod_builder_string(&b, channelmix_upmix_info[this->mix.upmix].label);
49
            spa_pod_builder_pop(&b, &f[1]);
50
            param = spa_pod_builder_pop(&b, &f[0]);
51
            break;
52
@@ -647,6 +675,8 @@
53
        spa_atof(s, &this->mix.widen);
54
    else if (spa_streq(k, "channelmix.hilbert-taps"))
55
        spa_atou32(s, &this->mix.hilbert_taps, 0);
56
+   else if (spa_streq(k, "channelmix.upmix-method"))
57
+       this->mix.upmix = channelmix_upmix_from_label(s);
58
    else
59
        return 0;
60
    return 1;
61
@@ -665,7 +695,7 @@
62
    while (true) {
63
        const char *name;
64
        struct spa_pod *pod;
65
-       char value[512];
66
+       char value[512], buf[128];
67
 
68
        if (spa_pod_parser_get_string(&prs, &name) < 0)
69
            break;
70
@@ -676,8 +706,9 @@
71
        if (spa_pod_is_string(pod)) {
72
            spa_pod_copy_string(pod, sizeof(value), value);
73
        } else if (spa_pod_is_float(pod)) {
74
-           snprintf(value, sizeof(value), "%f",
75
-                   SPA_POD_VALUE(struct spa_pod_float, pod));
76
+           snprintf(value, sizeof(value), "%s",
77
+                   spa_json_format_float(buf, sizeof(buf),
78
+                       SPA_POD_VALUE(struct spa_pod_float, pod)));
79
        } else if (spa_pod_is_int(pod)) {
80
            snprintf(value, sizeof(value), "%d",
81
                    SPA_POD_VALUE(struct spa_pod_int, pod));
82
@@ -691,7 +722,7 @@
83
        spa_log_info(this->log, "key:'%s' val:'%s'", name, value);
84
        changed += channelmix_set_param(this, name, value);
85
    }
86
-   if (changed)
87
+   if (changed && !this->props.disabled)
88
        channelmix_init(&this->mix);
89
    return changed;
90
 }
91
@@ -709,9 +740,6 @@
92
    if (param == NULL)
93
        return 0;
94
 
95
-   if (this->props.disabled)
96
-       return 0;
97
-
98
    SPA_POD_OBJECT_FOREACH(obj, prop) {
99
        switch (prop->key) {
100
        case SPA_PROP_volume:
101
@@ -771,6 +799,7 @@
102
            break;
103
        }
104
    }
105
+
106
    if (changed) {
107
        struct port *port = GET_PORT(this, this->direction, 0);
108
        if (have_soft_volume)
109
@@ -780,6 +809,7 @@
110
 
111
        if (port->have_format)
112
            remap_volumes(this, &port->format);
113
+
114
        set_volume(this);
115
    }
116
    return changed;
117
@@ -794,9 +824,6 @@
118
    if (size < 3)
119
        return -EINVAL;
120
 
121
-   if (this->props.disabled)
122
-       return 0;
123
-
124
    if ((val[0] & 0xf0) != 0xb0 || val[1] != 7)
125
        return 0;
126
 
127
@@ -1627,9 +1654,13 @@
128
 
129
    props_reset(&this->props);
130
 
131
-   this->mix.options = CHANNELMIX_OPTION_NORMALIZE;
132
+   this->mix.options = CHANNELMIX_OPTION_UPMIX;
133
+   this->mix.upmix = CHANNELMIX_UPMIX_SIMPLE;
134
    this->mix.log = this->log;
135
+   this->mix.lfe_cutoff = 120.0f;
136
+   this->mix.fc_cutoff = 6000.0f;
137
    this->mix.rear_delay = 12.0f;
138
+   this->mix.widen = 0.1f;
139
 
140
    for (i = 0; info && i < info->n_items; i++) {
141
        const char *k = info->items[i].key;
142
pipewire-0.3.48.tar.gz/spa/plugins/audioconvert/fmt-ops-sse2.c -> pipewire-0.3.49.tar.gz/spa/plugins/audioconvert/fmt-ops-sse2.c Changed
356
 
1
@@ -544,6 +544,354 @@
2
 }
3
 
4
 static void
5
+conv_interleave_32_1s_sse2(void *data, void * SPA_RESTRICT dst, const void * SPA_RESTRICT src[],
6
+       uint32_t n_channels, uint32_t n_samples)
7
+{
8
+   const int32_t *s0 = src[0];
9
+   int32_t *d = dst;
10
+   uint32_t n, unrolled;
11
+   __m128i out[4];
12
+
13
+   if (SPA_IS_ALIGNED(s0, 16))
14
+       unrolled = n_samples & ~3;
15
+   else
16
+       unrolled = 0;
17
+
18
+   for(n = 0; n < unrolled; n += 4) {
19
+       out[0] = _mm_load_si128((__m128i*)&s0[n]);
20
+       out[1] = _mm_shuffle_epi32(out[0], _MM_SHUFFLE(0, 3, 2, 1));
21
+       out[2] = _mm_shuffle_epi32(out[0], _MM_SHUFFLE(1, 0, 3, 2));
22
+       out[3] = _mm_shuffle_epi32(out[0], _MM_SHUFFLE(2, 1, 0, 3));
23
+
24
+       d[0*n_channels] = _mm_cvtsi128_si32(out[0]);
25
+       d[1*n_channels] = _mm_cvtsi128_si32(out[1]);
26
+       d[2*n_channels] = _mm_cvtsi128_si32(out[2]);
27
+       d[3*n_channels] = _mm_cvtsi128_si32(out[3]);
28
+       d += 4*n_channels;
29
+   }
30
+   for(; n < n_samples; n++) {
31
+       *d = s0[n];
32
+       d += n_channels;
33
+   }
34
+}
35
+static void
36
+conv_interleave_32_4s_sse2(void *data, void * SPA_RESTRICT dst, const void * SPA_RESTRICT src[],
37
+       uint32_t n_channels, uint32_t n_samples)
38
+{
39
+   const float *s0 = src[0], *s1 = src[1], *s2 = src[2], *s3 = src[3];
40
+   float *d = dst;
41
+   uint32_t n, unrolled;
42
+   __m128 out[4];
43
+
44
+   if (SPA_IS_ALIGNED(s0, 16) &&
45
+       SPA_IS_ALIGNED(s1, 16) &&
46
+       SPA_IS_ALIGNED(s2, 16) &&
47
+       SPA_IS_ALIGNED(s3, 16))
48
+       unrolled = n_samples & ~3;
49
+   else
50
+       unrolled = 0;
51
+
52
+   for(n = 0; n < unrolled; n += 4) {
53
+       out[0] = _mm_load_ps(&s0[n]);
54
+       out[1] = _mm_load_ps(&s1[n]);
55
+       out[2] = _mm_load_ps(&s2[n]);
56
+       out[3] = _mm_load_ps(&s3[n]);
57
+
58
+       _MM_TRANSPOSE4_PS(out[0], out[1], out[2], out[3]);
59
+
60
+       _mm_storeu_ps((d + 0*n_channels), out[0]);
61
+       _mm_storeu_ps((d + 1*n_channels), out[1]);
62
+       _mm_storeu_ps((d + 2*n_channels), out[2]);
63
+       _mm_storeu_ps((d + 3*n_channels), out[3]);
64
+       d += 4*n_channels;
65
+   }
66
+   for(; n < n_samples; n++) {
67
+       out[0] = _mm_setr_ps(s0[n], s1[n], s2[n], s3[n]);
68
+       _mm_storeu_ps(d, out[0]);
69
+       d += n_channels;
70
+   }
71
+}
72
+
73
+void
74
+conv_interleave_32_sse2(struct convert *conv, void * SPA_RESTRICT dst[], const void * SPA_RESTRICT src[],
75
+       uint32_t n_samples)
76
+{
77
+   int32_t *d = dst[0];
78
+   uint32_t i = 0, n_channels = conv->n_channels;
79
+
80
+   for(; i + 3 < n_channels; i += 4)
81
+       conv_interleave_32_4s_sse2(conv, &d[i], &src[i], n_channels, n_samples);
82
+   for(; i < n_channels; i++)
83
+       conv_interleave_32_1s_sse2(conv, &d[i], &src[i], n_channels, n_samples);
84
+}
85
+
86
+#define _MM_BSWAP_EPI32(x)                     \
87
+({                                 \
88
+   __m128i a = _mm_or_si128(                   \
89
+       _mm_slli_epi16(x, 8),                   \
90
+       _mm_srli_epi16(x, 8));                  \
91
+   a = _mm_shufflelo_epi16(a, _MM_SHUFFLE(2, 3, 0, 1));        \
92
+   a = _mm_shufflehi_epi16(a, _MM_SHUFFLE(2, 3, 0, 1));        \
93
+})
94
+
95
+static void
96
+conv_interleave_32s_1s_sse2(void *data, void * SPA_RESTRICT dst, const void * SPA_RESTRICT src[],
97
+       uint32_t n_channels, uint32_t n_samples)
98
+{
99
+   const int32_t *s0 = src[0];
100
+   int32_t *d = dst;
101
+   uint32_t n, unrolled;
102
+   __m128i out[4];
103
+
104
+   if (SPA_IS_ALIGNED(s0, 16))
105
+       unrolled = n_samples & ~3;
106
+   else
107
+       unrolled = 0;
108
+
109
+   for(n = 0; n < unrolled; n += 4) {
110
+       out[0] = _mm_load_si128((__m128i*)&s0[n]);
111
+       out[0] = _MM_BSWAP_EPI32(out[0]);
112
+       out[1] = _mm_shuffle_epi32(out[0], _MM_SHUFFLE(0, 3, 2, 1));
113
+       out[2] = _mm_shuffle_epi32(out[0], _MM_SHUFFLE(1, 0, 3, 2));
114
+       out[3] = _mm_shuffle_epi32(out[0], _MM_SHUFFLE(2, 1, 0, 3));
115
+
116
+       d[0*n_channels] = _mm_cvtsi128_si32(out[0]);
117
+       d[1*n_channels] = _mm_cvtsi128_si32(out[1]);
118
+       d[2*n_channels] = _mm_cvtsi128_si32(out[2]);
119
+       d[3*n_channels] = _mm_cvtsi128_si32(out[3]);
120
+       d += 4*n_channels;
121
+   }
122
+   for(; n < n_samples; n++) {
123
+       *d = bswap_32(s0[n]);
124
+       d += n_channels;
125
+   }
126
+}
127
+static void
128
+conv_interleave_32s_4s_sse2(void *data, void * SPA_RESTRICT dst, const void * SPA_RESTRICT src[],
129
+       uint32_t n_channels, uint32_t n_samples)
130
+{
131
+   const float *s0 = src[0], *s1 = src[1], *s2 = src[2], *s3 = src[3];
132
+   float *d = dst;
133
+   uint32_t n, unrolled;
134
+   __m128 out[4];
135
+
136
+   if (SPA_IS_ALIGNED(s0, 16) &&
137
+       SPA_IS_ALIGNED(s1, 16) &&
138
+       SPA_IS_ALIGNED(s2, 16) &&
139
+       SPA_IS_ALIGNED(s3, 16))
140
+       unrolled = n_samples & ~3;
141
+   else
142
+       unrolled = 0;
143
+
144
+   for(n = 0; n < unrolled; n += 4) {
145
+       out[0] = _mm_load_ps(&s0[n]);
146
+       out[1] = _mm_load_ps(&s1[n]);
147
+       out[2] = _mm_load_ps(&s2[n]);
148
+       out[3] = _mm_load_ps(&s3[n]);
149
+
150
+       _MM_TRANSPOSE4_PS(out[0], out[1], out[2], out[3]);
151
+
152
+       out[0] = (__m128) _MM_BSWAP_EPI32((__m128i)out[0]);
153
+       out[1] = (__m128) _MM_BSWAP_EPI32((__m128i)out[1]);
154
+       out[2] = (__m128) _MM_BSWAP_EPI32((__m128i)out[2]);
155
+       out[3] = (__m128) _MM_BSWAP_EPI32((__m128i)out[3]);
156
+
157
+       _mm_storeu_ps(&d[0*n_channels], out[0]);
158
+       _mm_storeu_ps(&d[1*n_channels], out[1]);
159
+       _mm_storeu_ps(&d[2*n_channels], out[2]);
160
+       _mm_storeu_ps(&d[3*n_channels], out[3]);
161
+       d += 4*n_channels;
162
+   }
163
+   for(; n < n_samples; n++) {
164
+       out[0] = _mm_setr_ps(s0[n], s1[n], s2[n], s3[n]);
165
+       out[0] = (__m128) _MM_BSWAP_EPI32((__m128i)out[0]);
166
+       _mm_storeu_ps(d, out[0]);
167
+       d += n_channels;
168
+   }
169
+}
170
+
171
+void
172
+conv_interleave_32s_sse2(struct convert *conv, void * SPA_RESTRICT dst[], const void * SPA_RESTRICT src[],
173
+       uint32_t n_samples)
174
+{
175
+   int32_t *d = dst[0];
176
+   uint32_t i = 0, n_channels = conv->n_channels;
177
+
178
+   for(; i + 3 < n_channels; i += 4)
179
+       conv_interleave_32s_4s_sse2(conv, &d[i], &src[i], n_channels, n_samples);
180
+   for(; i < n_channels; i++)
181
+       conv_interleave_32s_1s_sse2(conv, &d[i], &src[i], n_channels, n_samples);
182
+}
183
+
184
+static void
185
+conv_deinterleave_32_1s_sse2(void *data, void * SPA_RESTRICT dst[], const void * SPA_RESTRICT src,
186
+       uint32_t n_channels, uint32_t n_samples)
187
+{
188
+   const float *s = src;
189
+   float *d0 = dst[0];
190
+   uint32_t n, unrolled;
191
+   __m128 out;
192
+
193
+   if (SPA_IS_ALIGNED(d0, 16))
194
+       unrolled = n_samples & ~3;
195
+   else
196
+       unrolled = 0;
197
+
198
+   for(n = 0; n < unrolled; n += 4) {
199
+       out = _mm_setr_ps(s[0*n_channels],
200
+                 s[1*n_channels],
201
+                 s[2*n_channels],
202
+                 s[3*n_channels]);
203
+       _mm_store_ps(&d0[n], out);
204
+       s += 4*n_channels;
205
+   }
206
+   for(; n < n_samples; n++) {
207
+       d0[n] = *s;
208
+       s += n_channels;
209
+   }
210
+}
211
+
212
+static void
213
+conv_deinterleave_32_4s_sse2(void *data, void * SPA_RESTRICT dst[], const void * SPA_RESTRICT src,
214
+       uint32_t n_channels, uint32_t n_samples)
215
+{
216
+   const float *s = src;
217
+   float *d0 = dst[0], *d1 = dst[1], *d2 = dst[2], *d3 = dst[3];
218
+   uint32_t n, unrolled;
219
+   __m128 out[4];
220
+
221
+   if (SPA_IS_ALIGNED(d0, 16) &&
222
+       SPA_IS_ALIGNED(d1, 16) &&
223
+       SPA_IS_ALIGNED(d2, 16) &&
224
+       SPA_IS_ALIGNED(d3, 16))
225
+       unrolled = n_samples & ~3;
226
+   else
227
+       unrolled = 0;
228
+
229
+   for(n = 0; n < unrolled; n += 4) {
230
+       out[0] = _mm_loadu_ps(&s[0 * n_channels]);
231
+       out[1] = _mm_loadu_ps(&s[1 * n_channels]);
232
+       out[2] = _mm_loadu_ps(&s[2 * n_channels]);
233
+       out[3] = _mm_loadu_ps(&s[3 * n_channels]);
234
+
235
+       _MM_TRANSPOSE4_PS(out[0], out[1], out[2], out[3]);
236
+
237
+       _mm_store_ps(&d0[n], out[0]);
238
+       _mm_store_ps(&d1[n], out[1]);
239
+       _mm_store_ps(&d2[n], out[2]);
240
+       _mm_store_ps(&d3[n], out[3]);
241
+       s += 4 * n_channels;
242
+   }
243
+   for(; n < n_samples; n++) {
244
+       d0[n] = s[0];
245
+       d1[n] = s[1];
246
+       d2[n] = s[2];
247
+       d3[n] = s[3];
248
+       s += n_channels;
249
+   }
250
+}
251
+
252
+void
253
+conv_deinterleave_32_sse2(struct convert *conv, void * SPA_RESTRICT dst[], const void * SPA_RESTRICT src[],
254
+       uint32_t n_samples)
255
+{
256
+   const float *s = src[0];
257
+   uint32_t i = 0, n_channels = conv->n_channels;
258
+
259
+   for(; i + 3 < n_channels; i += 4)
260
+       conv_deinterleave_32_4s_sse2(conv, &dst[i], &s[i], n_channels, n_samples);
261
+   for(; i < n_channels; i++)
262
+       conv_deinterleave_32_1s_sse2(conv, &dst[i], &s[i], n_channels, n_samples);
263
+}
264
+
265
+static void
266
+conv_deinterleave_32s_1s_sse2(void *data, void * SPA_RESTRICT dst[], const void * SPA_RESTRICT src,
267
+       uint32_t n_channels, uint32_t n_samples)
268
+{
269
+   const float *s = src;
270
+   float *d0 = dst[0];
271
+   uint32_t n, unrolled;
272
+   __m128 out;
273
+
274
+   if (SPA_IS_ALIGNED(d0, 16))
275
+       unrolled = n_samples & ~3;
276
+   else
277
+       unrolled = 0;
278
+
279
+   for(n = 0; n < unrolled; n += 4) {
280
+       out = _mm_setr_ps(s[0*n_channels],
281
+                 s[1*n_channels],
282
+                 s[2*n_channels],
283
+                 s[3*n_channels]);
284
+       out = (__m128) _MM_BSWAP_EPI32((__m128i)out);
285
+       _mm_store_ps(&d0[n], out);
286
+       s += 4*n_channels;
287
+   }
288
+   for(; n < n_samples; n++) {
289
+       d0[n] = bswap_32(*s);
290
+       s += n_channels;
291
+   }
292
+}
293
+
294
+static void
295
+conv_deinterleave_32s_4s_sse2(void *data, void * SPA_RESTRICT dst[], const void * SPA_RESTRICT src,
296
+       uint32_t n_channels, uint32_t n_samples)
297
+{
298
+   const float *s = src;
299
+   float *d0 = dst[0], *d1 = dst[1], *d2 = dst[2], *d3 = dst[3];
300
+   uint32_t n, unrolled;
301
+   __m128 out[4];
302
+
303
+   if (SPA_IS_ALIGNED(d0, 16) &&
304
+       SPA_IS_ALIGNED(d1, 16) &&
305
+       SPA_IS_ALIGNED(d2, 16) &&
306
+       SPA_IS_ALIGNED(d3, 16))
307
+       unrolled = n_samples & ~3;
308
+   else
309
+       unrolled = 0;
310
+
311
+   for(n = 0; n < unrolled; n += 4) {
312
+       out[0] = _mm_loadu_ps(&s[0 * n_channels]);
313
+       out[1] = _mm_loadu_ps(&s[1 * n_channels]);
314
+       out[2] = _mm_loadu_ps(&s[2 * n_channels]);
315
+       out[3] = _mm_loadu_ps(&s[3 * n_channels]);
316
+
317
+       _MM_TRANSPOSE4_PS(out[0], out[1], out[2], out[3]);
318
+
319
+       out[0] = (__m128) _MM_BSWAP_EPI32((__m128i)out[0]);
320
+       out[1] = (__m128) _MM_BSWAP_EPI32((__m128i)out[1]);
321
+       out[2] = (__m128) _MM_BSWAP_EPI32((__m128i)out[2]);
322
+       out[3] = (__m128) _MM_BSWAP_EPI32((__m128i)out[3]);
323
+
324
+       _mm_store_ps(&d0[n], out[0]);
325
+       _mm_store_ps(&d1[n], out[1]);
326
+       _mm_store_ps(&d2[n], out[2]);
327
+       _mm_store_ps(&d3[n], out[3]);
328
+       s += 4 * n_channels;
329
+   }
330
+   for(; n < n_samples; n++) {
331
+       d0[n] = bswap_32(s[0]);
332
+       d1[n] = bswap_32(s[1]);
333
+       d2[n] = bswap_32(s[2]);
334
+       d3[n] = bswap_32(s[3]);
335
+       s += n_channels;
336
+   }
337
+}
338
+
339
+void
340
+conv_deinterleave_32s_sse2(struct convert *conv, void * SPA_RESTRICT dst[], const void * SPA_RESTRICT src[],
341
+       uint32_t n_samples)
342
+{
343
+   const float *s = src[0];
344
+   uint32_t i = 0, n_channels = conv->n_channels;
345
+
346
+   for(; i + 3 < n_channels; i += 4)
347
+       conv_deinterleave_32s_4s_sse2(conv, &dst[i], &s[i], n_channels, n_samples);
348
+   for(; i < n_channels; i++)
349
+       conv_deinterleave_32s_1s_sse2(conv, &dst[i], &s[i], n_channels, n_samples);
350
+}
351
+
352
+static void
353
 conv_f32_to_s16_1_sse2(void *data, void * SPA_RESTRICT dst, const void * SPA_RESTRICT src,
354
        uint32_t n_samples)
355
 {
356
pipewire-0.3.48.tar.gz/spa/plugins/audioconvert/fmt-ops.c -> pipewire-0.3.49.tar.gz/spa/plugins/audioconvert/fmt-ops.c Changed
52
 
1
@@ -84,10 +84,22 @@
2
 
3
    { SPA_AUDIO_FORMAT_F32, SPA_AUDIO_FORMAT_F32, 0, 0, conv_copy32_c },
4
    { SPA_AUDIO_FORMAT_F32P, SPA_AUDIO_FORMAT_F32P, 0, 0, conv_copy32d_c },
5
+#if defined (HAVE_SSE2)
6
+   { SPA_AUDIO_FORMAT_F32, SPA_AUDIO_FORMAT_F32P, 0, 0, conv_deinterleave_32_sse2 },
7
+#endif
8
    { SPA_AUDIO_FORMAT_F32, SPA_AUDIO_FORMAT_F32P, 0, 0, conv_deinterleave_32_c },
9
+#if defined (HAVE_SSE2)
10
+   { SPA_AUDIO_FORMAT_F32P, SPA_AUDIO_FORMAT_F32, 0, 0, conv_interleave_32_sse2 },
11
+#endif
12
    { SPA_AUDIO_FORMAT_F32P, SPA_AUDIO_FORMAT_F32, 0, 0, conv_interleave_32_c },
13
 
14
+#if defined (HAVE_SSE2)
15
+   { SPA_AUDIO_FORMAT_F32_OE, SPA_AUDIO_FORMAT_F32P, 0, 0, conv_deinterleave_32s_sse2 },
16
+#endif
17
    { SPA_AUDIO_FORMAT_F32_OE, SPA_AUDIO_FORMAT_F32P, 0, 0, conv_deinterleave_32s_c },
18
+#if defined (HAVE_SSE2)
19
+   { SPA_AUDIO_FORMAT_F32P, SPA_AUDIO_FORMAT_F32_OE, 0, 0, conv_interleave_32s_sse2 },
20
+#endif
21
    { SPA_AUDIO_FORMAT_F32P, SPA_AUDIO_FORMAT_F32_OE, 0, 0, conv_interleave_32s_c },
22
 
23
    { SPA_AUDIO_FORMAT_U32, SPA_AUDIO_FORMAT_F32, 0, 0, conv_u32_to_f32_c },
24
@@ -259,7 +271,13 @@
25
    /* s32 */
26
    { SPA_AUDIO_FORMAT_S32, SPA_AUDIO_FORMAT_S32, 0, 0, conv_copy32_c },
27
    { SPA_AUDIO_FORMAT_S32P, SPA_AUDIO_FORMAT_S32P, 0, 0, conv_copy32d_c },
28
+#if defined (HAVE_SSE2)
29
+   { SPA_AUDIO_FORMAT_S32, SPA_AUDIO_FORMAT_S32P, 0, 0, conv_deinterleave_32_sse2 },
30
+#endif
31
    { SPA_AUDIO_FORMAT_S32, SPA_AUDIO_FORMAT_S32P, 0, 0, conv_deinterleave_32_c },
32
+#if defined (HAVE_SSE2)
33
+   { SPA_AUDIO_FORMAT_S32P, SPA_AUDIO_FORMAT_S32, 0, 0, conv_interleave_32_sse2 },
34
+#endif
35
    { SPA_AUDIO_FORMAT_S32P, SPA_AUDIO_FORMAT_S32, 0, 0, conv_interleave_32_c },
36
 
37
    /* s24 */
38
@@ -271,7 +289,13 @@
39
    /* s24_32 */
40
    { SPA_AUDIO_FORMAT_S24_32, SPA_AUDIO_FORMAT_S24_32, 0, 0, conv_copy32_c },
41
    { SPA_AUDIO_FORMAT_S24_32P, SPA_AUDIO_FORMAT_S24_32P, 0, 0, conv_copy32d_c },
42
+#if defined (HAVE_SSE2)
43
+   { SPA_AUDIO_FORMAT_S24_32, SPA_AUDIO_FORMAT_S24_32P, 0, 0, conv_deinterleave_32_sse2 },
44
+#endif
45
    { SPA_AUDIO_FORMAT_S24_32, SPA_AUDIO_FORMAT_S24_32P, 0, 0, conv_deinterleave_32_c },
46
+#if defined (HAVE_SSE2)
47
+   { SPA_AUDIO_FORMAT_S24_32P, SPA_AUDIO_FORMAT_S24_32, 0, 0, conv_interleave_32_sse2 },
48
+#endif
49
    { SPA_AUDIO_FORMAT_S24_32P, SPA_AUDIO_FORMAT_S24_32, 0, 0, conv_interleave_32_c },
50
 
51
    /* F64 */
52
pipewire-0.3.48.tar.gz/spa/plugins/audioconvert/fmt-ops.h -> pipewire-0.3.49.tar.gz/spa/plugins/audioconvert/fmt-ops.h Changed
12
 
1
@@ -328,6 +328,10 @@
2
 DEFINE_FUNCTION(f32d_to_s16_2, sse2);
3
 DEFINE_FUNCTION(f32d_to_s16, sse2);
4
 DEFINE_FUNCTION(f32d_to_s16d, sse2);
5
+DEFINE_FUNCTION(deinterleave_32, sse2);
6
+DEFINE_FUNCTION(deinterleave_32s, sse2);
7
+DEFINE_FUNCTION(interleave_32, sse2);
8
+DEFINE_FUNCTION(interleave_32s, sse2);
9
 #endif
10
 #if defined(HAVE_SSSE3)
11
 DEFINE_FUNCTION(s24_to_f32d, ssse3);
12
pipewire-0.3.48.tar.gz/spa/plugins/audioconvert/merger.c -> pipewire-0.3.49.tar.gz/spa/plugins/audioconvert/merger.c Changed
127
 
1
@@ -306,78 +306,7 @@
2
 
3
    case SPA_PARAM_PropInfo:
4
    {
5
-       struct props *p = &this->props;
6
-
7
        switch (result.index) {
8
-       case 0:
9
-           param = spa_pod_builder_add_object(&b,
10
-               SPA_TYPE_OBJECT_PropInfo, id,
11
-               SPA_PROP_INFO_id,   SPA_POD_Id(SPA_PROP_volume),
12
-               SPA_PROP_INFO_name, SPA_POD_String("Volume"),
13
-               SPA_PROP_INFO_type, SPA_POD_CHOICE_RANGE_Float(p->volume, 0.0, 10.0));
14
-           break;
15
-       case 1:
16
-           param = spa_pod_builder_add_object(&b,
17
-               SPA_TYPE_OBJECT_PropInfo, id,
18
-               SPA_PROP_INFO_id,   SPA_POD_Id(SPA_PROP_mute),
19
-               SPA_PROP_INFO_name, SPA_POD_String("Mute"),
20
-               SPA_PROP_INFO_type, SPA_POD_CHOICE_Bool(p->channel.mute));
21
-           break;
22
-       case 2:
23
-           param = spa_pod_builder_add_object(&b,
24
-               SPA_TYPE_OBJECT_PropInfo, id,
25
-               SPA_PROP_INFO_id,   SPA_POD_Id(SPA_PROP_channelVolumes),
26
-               SPA_PROP_INFO_name, SPA_POD_String("Channel Volumes"),
27
-               SPA_PROP_INFO_type, SPA_POD_CHOICE_RANGE_Float(p->volume, 0.0, 10.0),
28
-               SPA_PROP_INFO_container, SPA_POD_Id(SPA_TYPE_Array));
29
-           break;
30
-       case 3:
31
-           param = spa_pod_builder_add_object(&b,
32
-               SPA_TYPE_OBJECT_PropInfo, id,
33
-               SPA_PROP_INFO_id,   SPA_POD_Id(SPA_PROP_channelMap),
34
-               SPA_PROP_INFO_name, SPA_POD_String("Channel Map"),
35
-               SPA_PROP_INFO_type, SPA_POD_Id(SPA_AUDIO_CHANNEL_UNKNOWN),
36
-               SPA_PROP_INFO_container, SPA_POD_Id(SPA_TYPE_Array));
37
-           break;
38
-       case 4:
39
-           param = spa_pod_builder_add_object(&b,
40
-               SPA_TYPE_OBJECT_PropInfo, id,
41
-               SPA_PROP_INFO_id,   SPA_POD_Id(SPA_PROP_monitorMute),
42
-               SPA_PROP_INFO_name, SPA_POD_String("Monitor Mute"),
43
-               SPA_PROP_INFO_type, SPA_POD_CHOICE_Bool(p->monitor.mute));
44
-           break;
45
-       case 5:
46
-           param = spa_pod_builder_add_object(&b,
47
-               SPA_TYPE_OBJECT_PropInfo, id,
48
-               SPA_PROP_INFO_id,   SPA_POD_Id(SPA_PROP_monitorVolumes),
49
-               SPA_PROP_INFO_name, SPA_POD_String("Monitor Volumes"),
50
-               SPA_PROP_INFO_type, SPA_POD_CHOICE_RANGE_Float(p->volume, 0.0, 10.0),
51
-               SPA_PROP_INFO_container, SPA_POD_Id(SPA_TYPE_Array));
52
-           break;
53
-       case 6:
54
-           param = spa_pod_builder_add_object(&b,
55
-               SPA_TYPE_OBJECT_PropInfo, id,
56
-               SPA_PROP_INFO_id,   SPA_POD_Id(SPA_PROP_softMute),
57
-               SPA_PROP_INFO_name, SPA_POD_String("Soft Mute"),
58
-               SPA_PROP_INFO_type, SPA_POD_CHOICE_Bool(p->soft.mute));
59
-           break;
60
-       case 7:
61
-           param = spa_pod_builder_add_object(&b,
62
-               SPA_TYPE_OBJECT_PropInfo, id,
63
-               SPA_PROP_INFO_id,   SPA_POD_Id(SPA_PROP_softVolumes),
64
-               SPA_PROP_INFO_name, SPA_POD_String("Soft Volumes"),
65
-               SPA_PROP_INFO_type, SPA_POD_CHOICE_RANGE_Float(p->volume, 0.0, 10.0),
66
-               SPA_PROP_INFO_container, SPA_POD_Id(SPA_TYPE_Array));
67
-           break;
68
-       case 8:
69
-           param = spa_pod_builder_add_object(&b,
70
-               SPA_TYPE_OBJECT_PropInfo, id,
71
-               SPA_PROP_INFO_name, SPA_POD_String("monitor.channel-volumes"),
72
-               SPA_PROP_INFO_description, SPA_POD_String("Monitor channel volume"),
73
-               SPA_PROP_INFO_type, SPA_POD_CHOICE_Bool(
74
-                   this->monitor_channel_volumes),
75
-               SPA_PROP_INFO_params, SPA_POD_Bool(true));
76
-           break;
77
        default:
78
            return 0;
79
        }
80
@@ -386,46 +315,7 @@
81
 
82
    case SPA_PARAM_Props:
83
    {
84
-#if 0
85
-       struct props *p = &this->props;
86
-       struct spa_pod_frame f[2];
87
-#endif
88
-
89
        switch (result.index) {
90
-#if 0
91
-       case 0:
92
-           spa_pod_builder_push_object(&b, &f[0],
93
-                                SPA_TYPE_OBJECT_Props, id);
94
-           spa_pod_builder_add(&b,
95
-               SPA_PROP_volume,        SPA_POD_Float(p->volume),
96
-               SPA_PROP_mute,          SPA_POD_Bool(p->channel.mute),
97
-               SPA_PROP_channelVolumes,    SPA_POD_Array(sizeof(float),
98
-                                   SPA_TYPE_Float,
99
-                                   p->channel.n_volumes,
100
-                                   p->channel.volumes),
101
-               SPA_PROP_channelMap,        SPA_POD_Array(sizeof(uint32_t),
102
-                                   SPA_TYPE_Id,
103
-                                   p->n_channels,
104
-                                   p->channel_map),
105
-               SPA_PROP_softMute,      SPA_POD_Bool(p->soft.mute),
106
-               SPA_PROP_softVolumes,       SPA_POD_Array(sizeof(float),
107
-                                   SPA_TYPE_Float,
108
-                                   p->soft.n_volumes,
109
-                                   p->soft.volumes),
110
-               SPA_PROP_monitorMute,       SPA_POD_Bool(p->monitor.mute),
111
-               SPA_PROP_monitorVolumes,    SPA_POD_Array(sizeof(float),
112
-                                   SPA_TYPE_Float,
113
-                                   p->monitor.n_volumes,
114
-                                   p->monitor.volumes),
115
-               0);
116
-           spa_pod_builder_prop(&b, SPA_PROP_params, 0);
117
-           spa_pod_builder_push_struct(&b, &f[1]);
118
-           spa_pod_builder_string(&b, "monitor.channel-volumes");
119
-           spa_pod_builder_bool(&b, this->monitor_channel_volumes);
120
-           spa_pod_builder_pop(&b, &f[1]);
121
-           param = spa_pod_builder_pop(&b, &f[0]);
122
-           break;
123
-#endif
124
        default:
125
            return 0;
126
        }
127
pipewire-0.3.48.tar.gz/spa/plugins/audioconvert/resample.c -> pipewire-0.3.49.tar.gz/spa/plugins/audioconvert/resample.c Changed
77
 
1
@@ -307,9 +307,6 @@
2
 
3
        if (spa_pod_is_string(pod)) {
4
            spa_pod_copy_string(pod, sizeof(value), value);
5
-       } else if (spa_pod_is_float(pod)) {
6
-           snprintf(value, sizeof(value), "%f",
7
-                   SPA_POD_VALUE(struct spa_pod_float, pod));
8
        } else if (spa_pod_is_int(pod)) {
9
            snprintf(value, sizeof(value), "%d",
10
                    SPA_POD_VALUE(struct spa_pod_int, pod));
11
@@ -395,10 +392,10 @@
12
 static void update_rate_match(struct impl *this, bool passthrough, uint32_t out_size, uint32_t in_queued)
13
 {
14
    if (this->io_rate_match) {
15
-       uint32_t match_size;
16
+       uint32_t delay, match_size;
17
 
18
        if (passthrough) {
19
-           this->io_rate_match->delay = 0;
20
+           delay = in_queued;
21
            match_size = out_size;
22
        } else {
23
            if (SPA_FLAG_IS_SET(this->io_rate_match->flags, SPA_IO_RATE_MATCH_FLAG_ACTIVE))
24
@@ -406,12 +403,14 @@
25
            else
26
                resample_update_rate(&this->resample, this->rate_scale);
27
 
28
-           this->io_rate_match->delay = resample_delay(&this->resample);
29
+           delay = resample_delay(&this->resample) + in_queued;
30
            match_size = resample_in_len(&this->resample, out_size);
31
        }
32
        match_size -= SPA_MIN(match_size, in_queued);
33
        this->io_rate_match->size = match_size;
34
-       spa_log_trace_fp(this->log, "%p: next match %u", this, match_size);
35
+       this->io_rate_match->delay = delay;
36
+       spa_log_trace_fp(this->log, "%p: next match:%u queued:%u delay:%u", this, match_size,
37
+               in_queued, delay);
38
    } else {
39
        resample_update_rate(&this->resample, this->rate_scale * this->props.rate);
40
    }
41
@@ -997,7 +996,7 @@
42
    if (SPA_LIKELY(this->io_position)) {
43
        double r =  this->rate_scale;
44
 
45
-       max = this->io_position->clock.duration;
46
+       max = this->io_position->clock.duration * sizeof(float);
47
        if (this->mode == MODE_SPLIT) {
48
            if (this->io_position->clock.rate.denom != this->resample.o_rate)
49
                r = (double) this->io_position->clock.rate.denom / this->resample.o_rate;
50
@@ -1015,13 +1014,13 @@
51
        }
52
    }
53
    else
54
-       max = maxsize / sizeof(float);
55
+       max = maxsize;
56
 
57
    switch (this->mode) {
58
    case MODE_SPLIT:
59
        /* in split mode we need to output exactly the size of the
60
         * duration so we don't try to flush early */
61
-       maxsize = SPA_MIN(maxsize, max * sizeof(float));
62
+       maxsize = SPA_MIN(maxsize, max);
63
        flush_out = false;
64
        break;
65
    case MODE_MERGE:
66
@@ -1112,8 +1111,8 @@
67
        spa_log_trace_fp(this->log, "%p: no output buffer", this);
68
    }
69
 
70
-   update_rate_match(this, passthrough, max - outport->offset / sizeof(float),
71
-           size - inport->offset / sizeof(float));
72
+   update_rate_match(this, passthrough, (max - outport->offset) / sizeof(float),
73
+           (size - inport->offset) / sizeof(float));
74
    return res;
75
 }
76
 
77
pipewire-0.3.48.tar.gz/spa/plugins/audiotestsrc/audiotestsrc.c -> pipewire-0.3.49.tar.gz/spa/plugins/audiotestsrc/audiotestsrc.c Changed
24
 
1
@@ -984,6 +984,13 @@
2
    return 0;
3
 }
4
 
5
+static int do_remove_timer(struct spa_loop *loop, bool async, uint32_t seq, const void *data, size_t size, void *user_data)
6
+{
7
+   struct impl *this = user_data;
8
+   spa_loop_remove_source(this->data_loop, &this->timer_source);
9
+   return 0;
10
+}
11
+
12
 static int impl_clear(struct spa_handle *handle)
13
 {
14
    struct impl *this;
15
@@ -993,7 +1000,7 @@
16
    this = (struct impl *) handle;
17
 
18
    if (this->data_loop)
19
-       spa_loop_remove_source(this->data_loop, &this->timer_source);
20
+       spa_loop_invoke(this->data_loop, do_remove_timer, 0, NULL, 0, true, this);
21
    spa_system_close(this->data_system, this->timer_source.fd);
22
 
23
    return 0;
24
pipewire-0.3.48.tar.gz/spa/plugins/bluez5/a2dp-codec-aptx.c -> pipewire-0.3.49.tar.gz/spa/plugins/bluez5/a2dp-codec-aptx.c Changed
10
 
1
@@ -343,6 +343,8 @@
2
 
3
    if (this->hd)
4
        this->max_frames = (this->mtu - sizeof(struct rtp_header)) / this->frame_length;
5
+   else if (codec_is_ll(codec))
6
+       this->max_frames = SPA_MIN(256u, this->mtu) / this->frame_length;
7
    else
8
        this->max_frames = this->mtu / this->frame_length;
9
 
10
pipewire-0.3.48.tar.gz/spa/plugins/bluez5/a2dp-codecs.h -> pipewire-0.3.49.tar.gz/spa/plugins/bluez5/a2dp-codecs.h Changed
10
 
1
@@ -43,7 +43,7 @@
2
 
3
 #define SPA_TYPE_INTERFACE_Bluez5CodecA2DP SPA_TYPE_INFO_INTERFACE_BASE "Bluez5:Codec:A2DP:Private"
4
 
5
-#define SPA_VERSION_BLUEZ5_CODEC_A2DP      0
6
+#define SPA_VERSION_BLUEZ5_CODEC_A2DP      1
7
 
8
 struct spa_bluez5_codec_a2dp {
9
    struct spa_interface iface;
10
pipewire-0.3.48.tar.gz/spa/plugins/bluez5/a2dp-sink.c -> pipewire-0.3.49.tar.gz/spa/plugins/bluez5/a2dp-sink.c Changed
310
 
1
@@ -70,6 +70,9 @@
2
 
3
 #define FILL_FRAMES 2
4
 #define MAX_BUFFERS 32
5
+#define MIN_LATENCY    128
6
+#define MAX_LATENCY    8192
7
+#define BUFFER_SIZE    (MAX_LATENCY*8)
8
 
9
 struct buffer {
10
    uint32_t id;
11
@@ -137,6 +140,8 @@
12
    struct spa_source source;
13
    int timerfd;
14
    struct spa_source flush_source;
15
+   struct spa_source flush_timer_source;
16
+   int flush_timerfd;
17
 
18
    struct spa_io_clock *clock;
19
    struct spa_io_position *position;
20
@@ -153,14 +158,14 @@
21
 
22
    int need_flush;
23
    uint32_t block_size;
24
-   uint8_t buffer[4096];
25
+   uint8_t buffer[BUFFER_SIZE];
26
    uint32_t buffer_used;
27
    uint32_t header_size;
28
    uint32_t frame_count;
29
    uint16_t seqnum;
30
    uint32_t timestamp;
31
    uint64_t sample_count;
32
-   uint8_t tmp_buffer[4096];
33
+   uint8_t tmp_buffer[BUFFER_SIZE];
34
    uint32_t tmp_buffer_used;
35
    uint32_t fd_buffer_size;
36
 };
37
@@ -169,11 +174,7 @@
38
 
39
 static void reset_props(struct impl *this, struct props *props)
40
 {
41
-   if (this->codec->id == SPA_BLUETOOTH_AUDIO_CODEC_APTX_LL) {
42
-       props->min_latency = 256;
43
-   } else {
44
-       props->min_latency = MIN_LATENCY;
45
-   }
46
+   props->min_latency = MIN_LATENCY;
47
    props->max_latency = MAX_LATENCY;
48
    props->latency_offset = 0;
49
    strncpy(props->clock_name, DEFAULT_CLOCK_NAME, sizeof(props->clock_name));
50
@@ -481,11 +482,6 @@
51
    return written;
52
 }
53
 
54
-static bool want_flush(struct impl *this)
55
-{
56
-   return (this->frame_count * this->block_size / this->port.frame_size >= this->props.min_latency);
57
-}
58
-
59
 static int encode_buffer(struct impl *this, const void *data, uint32_t size)
60
 {
61
    int processed;
62
@@ -542,7 +538,7 @@
63
    spa_log_trace(this->log, "%p: used:%d block_size:%d", this,
64
            this->buffer_used, this->block_size);
65
 
66
-   if (this->need_flush || want_flush(this))
67
+   if (this->need_flush)
68
        return send_buffer(this);
69
 
70
    return 0;
71
@@ -565,12 +561,25 @@
72
    return total;
73
 }
74
 
75
-static void enable_flush(struct impl *this, bool enabled)
76
+static void enable_flush(struct impl *this, bool enabled, uint64_t timeout)
77
 {
78
-   if (SPA_FLAG_IS_SET(this->flush_source.mask, SPA_IO_OUT) != enabled) {
79
-       SPA_FLAG_UPDATE(this->flush_source.mask, SPA_IO_OUT, enabled);
80
+   bool flush_enabled = enabled && (timeout == 0);
81
+   struct itimerspec ts;
82
+
83
+   if (SPA_FLAG_IS_SET(this->flush_source.mask, SPA_IO_OUT) != flush_enabled) {
84
+       SPA_FLAG_UPDATE(this->flush_source.mask, SPA_IO_OUT, flush_enabled);
85
        spa_loop_update_source(this->data_loop, &this->flush_source);
86
    }
87
+
88
+   if (!enabled)
89
+       timeout = 0;
90
+
91
+   ts.it_value.tv_sec = timeout / SPA_NSEC_PER_SEC;
92
+   ts.it_value.tv_nsec = timeout % SPA_NSEC_PER_SEC;
93
+   ts.it_interval.tv_sec = 0;
94
+   ts.it_interval.tv_nsec = 0;
95
+   spa_system_timerfd_settime(this->data_system,
96
+           this->flush_timerfd, 0, &ts, NULL);
97
 }
98
 
99
 static int flush_data(struct impl *this, uint64_t now_time)
100
@@ -579,6 +588,11 @@
101
    uint32_t total_frames;
102
    struct port *port = &this->port;
103
 
104
+   if (!this->flush_source.loop) {
105
+       /* I/O in error state */
106
+       return -EIO;
107
+   }
108
+
109
    total_frames = 0;
110
 again:
111
    written = 0;
112
@@ -640,42 +654,80 @@
113
    }
114
 
115
    if (written > 0 && this->buffer_used == this->header_size) {
116
-       enable_flush(this, false);
117
+       enable_flush(this, false, 0);
118
        return 0;
119
    }
120
 
121
    written = flush_buffer(this);
122
+
123
    if (written == -EAGAIN) {
124
-       spa_log_trace(this->log, "%p: delay flush", this);
125
+       spa_log_trace(this->log, "%p: fail flush", this);
126
        if (now_time - this->last_error > SPA_NSEC_PER_SEC / 2) {
127
+           spa_log_trace(this->log, "%p: reduce bitpool", this);
128
            this->codec->reduce_bitpool(this->codec_data);
129
            this->last_error = now_time;
130
        }
131
-       this->need_flush = true;
132
-       enable_flush(this, true);
133
+
134
+       /*
135
+        * The socket buffer is full, and the device is not processing data
136
+        * fast enough, so should just skip this packet. There will be a sound
137
+        * glitch in any case.
138
+        */
139
+       written = this->buffer_used;
140
+       reset_buffer(this);
141
    }
142
-   else if (written < 0) {
143
+
144
+   if (written < 0) {
145
        spa_log_trace(this->log, "%p: error flushing %s", this,
146
                spa_strerror(written));
147
        reset_buffer(this);
148
-       enable_flush(this, false);
149
+       enable_flush(this, false, 0);
150
        return written;
151
    }
152
    else if (written > 0) {
153
+       /*
154
+        * We cannot write all data we have at once, since this can exceed
155
+        * device buffers.  We'll want a limited number of "excess"
156
+        * samples. This is an issue for the "low-latency" A2DP codecs.
157
+        *
158
+        * Flushing the rest of the data (if any) is delayed after a timeout,
159
+        * selected on an average-rate basis:
160
+        *
161
+        * npackets = quantum / packet_samples
162
+        * write_end_time = npackets * timeout
163
+        * max_excess = quantum - sample_rate * write_end_time
164
+        * packet_time = packet_samples / sample_rate
165
+        * => timeout = (quantum - max_excess)/quantum * packet_time
166
+        */
167
+       uint64_t max_excess = 2*256;
168
+       uint64_t packet_samples = this->frame_count * this->block_size / port->frame_size;
169
+       uint64_t packet_time = packet_samples * SPA_NSEC_PER_SEC / port->current_format.info.raw.rate;
170
+       uint64_t quantum = SPA_LIKELY(this->clock) ? this->clock->duration : 0;
171
+       uint64_t timeout = (quantum > max_excess) ?
172
+           (packet_time * (quantum - max_excess) / quantum) : 0;
173
+
174
        reset_buffer(this);
175
        if (now_time - this->last_error > SPA_NSEC_PER_SEC) {
176
-           this->codec->increase_bitpool(this->codec_data);
177
+           if (get_transport_unused_size(this) == (int)this->fd_buffer_size) {
178
+               spa_log_trace(this->log, "%p: increase bitpool", this);
179
+               this->codec->increase_bitpool(this->codec_data);
180
+           }
181
            this->last_error = now_time;
182
        }
183
-       if (!spa_list_is_empty(&port->ready))
184
-           goto again;
185
-
186
-       enable_flush(this, false);
187
+       if (!spa_list_is_empty(&port->ready)) {
188
+           spa_log_trace(this->log, "%p: flush after %d ns", this, (int)timeout);
189
+           if (timeout == 0)
190
+               goto again;
191
+           else
192
+               enable_flush(this, true, timeout);
193
+       } else {
194
+           enable_flush(this, false, 0);
195
+       }
196
    }
197
    else {
198
        /* Don't want to flush yet, or failed to write anything */
199
        spa_log_trace(this->log, "%p: skip flush", this);
200
-       enable_flush(this, false);
201
+       enable_flush(this, false, 0);
202
    }
203
    return 0;
204
 }
205
@@ -692,6 +744,30 @@
206
            spa_loop_remove_source(this->data_loop, &this->flush_source);
207
        return;
208
    }
209
+
210
+   if (this->transport == NULL) {
211
+       enable_flush(this, false, 0);
212
+       return;
213
+   }
214
+
215
+   flush_data(this, this->current_time);
216
+}
217
+
218
+static void a2dp_on_flush_timeout(struct spa_source *source)
219
+{
220
+   struct impl *this = source->data;
221
+   uint64_t exp;
222
+
223
+   spa_log_trace(this->log, "%p: flush on timeout", this);
224
+
225
+   if (spa_system_timerfd_read(this->data_system, this->flush_timerfd, &exp) < 0)
226
+       spa_log_warn(this->log, "error reading timerfd: %s", strerror(errno));
227
+
228
+   if (this->transport == NULL) {
229
+       enable_flush(this, false, 0);
230
+       return;
231
+   }
232
+
233
    flush_data(this, this->current_time);
234
 }
235
 
236
@@ -835,6 +911,13 @@
237
    this->source.rmask = 0;
238
    spa_loop_add_source(this->data_loop, &this->source);
239
 
240
+   this->flush_timer_source.data = this;
241
+   this->flush_timer_source.fd = this->flush_timerfd;
242
+   this->flush_timer_source.func = a2dp_on_flush_timeout;
243
+   this->flush_timer_source.mask = SPA_IO_IN;
244
+   this->flush_timer_source.rmask = 0;
245
+   spa_loop_add_source(this->data_loop, &this->flush_timer_source);
246
+
247
    this->flush_source.data = this;
248
    this->flush_source.fd = this->transport->fd;
249
    this->flush_source.func = a2dp_on_flush;
250
@@ -865,9 +948,18 @@
251
    ts.it_interval.tv_sec = 0;
252
    ts.it_interval.tv_nsec = 0;
253
    spa_system_timerfd_settime(this->data_system, this->timerfd, 0, &ts, NULL);
254
+
255
    if (this->flush_source.loop)
256
        spa_loop_remove_source(this->data_loop, &this->flush_source);
257
 
258
+   if (this->flush_timer_source.loop)
259
+       spa_loop_remove_source(this->data_loop, &this->flush_timer_source);
260
+   ts.it_value.tv_sec = 0;
261
+   ts.it_value.tv_nsec = 0;
262
+   ts.it_interval.tv_sec = 0;
263
+   ts.it_interval.tv_nsec = 0;
264
+   spa_system_timerfd_settime(this->data_system, this->flush_timerfd, 0, &ts, NULL);
265
+
266
    return 0;
267
 }
268
 
269
@@ -928,22 +1020,15 @@
270
 
271
 static void emit_node_info(struct impl *this, bool full)
272
 {
273
-   char latency[64] = SPA_STRINGIFY(MIN_LATENCY)"/48000";
274
    struct spa_dict_item node_info_items[] = {
275
        { SPA_KEY_DEVICE_API, "bluez5" },
276
        { SPA_KEY_MEDIA_CLASS, "Audio/Sink" },
277
        { SPA_KEY_NODE_DRIVER, "true" },
278
-       { SPA_KEY_NODE_LATENCY, latency },
279
    };
280
    uint64_t old = full ? this->info.change_mask : 0;
281
    if (full)
282
        this->info.change_mask = this->info_all;
283
    if (this->info.change_mask) {
284
-       if (this->transport && this->port.have_format)
285
-           snprintf(latency, sizeof(latency), "%d/%d", (int)this->props.min_latency,
286
-                   (int)this->port.current_format.info.raw.rate);
287
-       else
288
-           snprintf(latency, sizeof(latency), "%d/48000", (int)this->props.min_latency);
289
        this->info.props = &SPA_DICT_INIT_ARRAY(node_info_items);
290
        spa_node_emit_info(&this->hooks, &this->info);
291
        this->info.change_mask = old;
292
@@ -1426,6 +1511,7 @@
293
    if (this->transport)
294
        spa_hook_remove(&this->transport_listener);
295
    spa_system_close(this->data_system, this->timerfd);
296
+   spa_system_close(this->data_system, this->flush_timerfd);
297
    return 0;
298
 }
299
 
300
@@ -1533,6 +1619,9 @@
301
    this->timerfd = spa_system_timerfd_create(this->data_system,
302
            CLOCK_MONOTONIC, SPA_FD_CLOEXEC | SPA_FD_NONBLOCK);
303
 
304
+   this->flush_timerfd = spa_system_timerfd_create(this->data_system,
305
+           CLOCK_MONOTONIC, SPA_FD_CLOEXEC | SPA_FD_NONBLOCK);
306
+
307
    return 0;
308
 }
309
 
310
pipewire-0.3.48.tar.gz/spa/plugins/bluez5/a2dp-source.c -> pipewire-0.3.49.tar.gz/spa/plugins/bluez5/a2dp-source.c Changed
10
 
1
@@ -70,6 +70,8 @@
2
 
3
 #define FILL_FRAMES 2
4
 #define MAX_BUFFERS 32
5
+#define MIN_LATENCY    512
6
+#define MAX_LATENCY    1024
7
 
8
 struct buffer {
9
    uint32_t id;
10
pipewire-0.3.48.tar.gz/spa/plugins/bluez5/backend-ofono.c -> pipewire-0.3.49.tar.gz/spa/plugins/bluez5/backend-ofono.c Changed
281
 
1
@@ -38,10 +38,14 @@
2
 #include <spa/support/plugin.h>
3
 #include <spa/utils/string.h>
4
 #include <spa/utils/type.h>
5
+#include <spa/utils/result.h>
6
 #include <spa/param/audio/raw.h>
7
 
8
 #include "defs.h"
9
 
10
+#define INITIAL_INTERVAL_NSEC  (500 * SPA_NSEC_PER_MSEC)
11
+#define ACTION_INTERVAL_NSEC   (3000 * SPA_NSEC_PER_MSEC)
12
+
13
 static struct spa_log_topic log_topic = SPA_LOG_TOPIC(0, "spa.bluez5.ofono");
14
 #undef SPA_LOG_TOPIC_DEFAULT
15
 #define SPA_LOG_TOPIC_DEFAULT &log_topic
16
@@ -53,17 +57,23 @@
17
 
18
    struct spa_log *log;
19
    struct spa_loop *main_loop;
20
+   struct spa_system *main_system;
21
    struct spa_dbus *dbus;
22
+   struct spa_loop_utils *loop_utils;
23
    DBusConnection *conn;
24
 
25
    const struct spa_bt_quirks *quirks;
26
 
27
+   struct spa_source *timer;
28
+
29
    unsigned int filters_added:1;
30
    unsigned int msbc_supported:1;
31
 };
32
 
33
 struct transport_data {
34
    struct spa_source sco;
35
+   unsigned int broken:1;
36
+   unsigned int activated:1;
37
 };
38
 
39
 #define OFONO_HF_AUDIO_MANAGER_INTERFACE OFONO_SERVICE ".HandsfreeAudioManager"
40
@@ -161,6 +171,13 @@
41
 
42
    dbus_error_init(&err);
43
 
44
+   /*
45
+    * XXX: We assume here oFono replies. It however can happen that the headset does
46
+    * XXX: not properly respond to the codec negotiation RFCOMM commands.
47
+    * XXX: oFono (1.34) fails to handle this condition, and will not send DBus reply
48
+    * XXX: in this case. The transport acquire API is synchronous, so we can't
49
+    * XXX: do better here right now.
50
+    */
51
    r = dbus_connection_send_with_reply_and_block(backend->conn, m, -1, &err);
52
    dbus_message_unref(m);
53
    m = NULL;
54
@@ -196,12 +213,19 @@
55
 static int ofono_audio_acquire(void *data, bool optional)
56
 {
57
    struct spa_bt_transport *transport = data;
58
+   struct transport_data *td = transport->user_data;
59
    struct impl *backend = SPA_CONTAINER_OF(transport->backend, struct impl, this);
60
    uint8_t codec;
61
    int ret = 0;
62
 
63
    if (transport->fd >= 0)
64
        goto finish;
65
+   if (td->broken) {
66
+       ret = -EIO;
67
+       goto finish;
68
+   }
69
+
70
+   spa_bt_device_update_last_bluez_action_time(transport->device);
71
 
72
    ret = _audio_acquire(backend, transport->path, &codec);
73
    if (ret < 0)
74
@@ -210,27 +234,28 @@
75
    transport->fd = ret;
76
 
77
    if (transport->codec != codec) {
78
-       struct spa_bt_transport *t = NULL;
79
+       struct timespec ts;
80
 
81
-       spa_log_warn(backend->log, "Acquired codec (%d) differs from transport one (%d)",
82
-                    codec, transport->codec);
83
+       spa_log_info(backend->log, "transport %p: acquired codec (%d) differs from transport one (%d)",
84
+               transport, codec, transport->codec);
85
 
86
        /* shutdown to make sure connection is dropped immediately */
87
        shutdown(transport->fd, SHUT_RDWR);
88
        close(transport->fd);
89
        transport->fd = -1;
90
 
91
-       /* Create a new transport which differs only for codec */
92
-       t = _transport_create(backend, transport->path, transport->device,
93
-                             transport->profile, codec, &transport->impl);
94
-
95
-       spa_bt_transport_free(transport);
96
-       spa_bt_device_connect_profile(t->device, t->profile);
97
-
98
-       ret = -EIO;
99
-       goto finish;
100
+       /* schedule immediate profile update, from main loop */
101
+       transport->codec = codec;
102
+       td->broken = true;
103
+       ts.tv_sec = 0;
104
+       ts.tv_nsec = 1;
105
+       spa_loop_utils_update_timer(backend->loop_utils, backend->timer,
106
+               &ts, NULL, false);
107
+       return -EIO;
108
    }
109
 
110
+   td->broken = false;
111
+
112
    spa_log_debug(backend->log, "transport %p: Acquire %s, fd %d codec %d", transport,
113
            transport->path, transport->fd, transport->codec);
114
 
115
@@ -293,14 +318,74 @@
116
    .release = ofono_audio_release,
117
 };
118
 
119
+bool activate_transport(struct spa_bt_transport *t, const void *data)
120
+{
121
+   struct impl *backend = (void *)data;
122
+   struct transport_data *td = t->user_data;
123
+   struct timespec ts;
124
+   uint64_t now, threshold;
125
+
126
+   if (t->backend != &backend->this)
127
+       return false;
128
+
129
+   /* Check device-specific rate limit */
130
+   spa_system_clock_gettime(backend->main_system, CLOCK_MONOTONIC, &ts);
131
+   now = SPA_TIMESPEC_TO_NSEC(&ts);
132
+   threshold = t->device->last_bluez_action_time + ACTION_INTERVAL_NSEC;
133
+   if (now < threshold) {
134
+       ts.tv_sec = (threshold - now) / SPA_NSEC_PER_SEC;
135
+       ts.tv_nsec = (threshold - now) % SPA_NSEC_PER_SEC;
136
+       spa_loop_utils_update_timer(backend->loop_utils, backend->timer,
137
+               &ts, NULL, false);
138
+       return false;
139
+   }
140
+
141
+   if (!td->activated) {
142
+       /* Connect profile */
143
+       spa_log_debug(backend->log, "Transport %s activated", t->path);
144
+       td->activated = true;
145
+       spa_bt_device_connect_profile(t->device, t->profile);
146
+   }
147
+
148
+   if (td->broken) {
149
+       /* Recreate the transport */
150
+       struct spa_bt_transport *t_copy;
151
+
152
+       t_copy = _transport_create(backend, t->path, t->device,
153
+               t->profile, t->codec, (struct spa_callbacks *)&ofono_transport_impl);
154
+       spa_bt_transport_free(t);
155
+
156
+       if (t_copy)
157
+           spa_bt_device_connect_profile(t_copy->device, t_copy->profile);
158
+
159
+       return true;
160
+   }
161
+
162
+   return false;
163
+}
164
+
165
+static void activate_transports(struct impl *backend)
166
+{
167
+   while (spa_bt_transport_find_full(backend->monitor, activate_transport, backend));
168
+}
169
+
170
+static void activate_timer_event(void *userdata, uint64_t expirations)
171
+{
172
+   struct impl *backend = userdata;
173
+   spa_loop_utils_update_timer(backend->loop_utils, backend->timer, NULL, NULL, false);
174
+   activate_transports(backend);
175
+}
176
+
177
 static DBusHandlerResult ofono_audio_card_found(struct impl *backend, char *path, DBusMessageIter *props_i)
178
 {
179
    const char *remote_address = NULL;
180
    const char *local_address = NULL;
181
    struct spa_bt_device *d;
182
    struct spa_bt_transport *t;
183
+   struct transport_data *td;
184
    enum spa_bt_profile profile = SPA_BT_PROFILE_HFP_AG;
185
-   uint8_t codec = HFP_AUDIO_CODEC_CVSD;
186
+   uint8_t codec = backend->msbc_supported ?
187
+       HFP_AUDIO_CODEC_MSBC : HFP_AUDIO_CODEC_CVSD;
188
 
189
    spa_assert(backend);
190
    spa_assert(path);
191
@@ -340,24 +425,6 @@
192
        dbus_message_iter_next(props_i);
193
    }
194
 
195
-   /*
196
-    * Acquire and close immediately to figure out the codec.
197
-    * This is necessary if we are in HF mode, because we need to emit
198
-    * nodes and the advertised sample rate of the node depends on the codec.
199
-    * For AG mode, we delay the emission of the nodes, so it is not necessary
200
-    * to know the codec in advance
201
-    */
202
-   if (profile == SPA_BT_PROFILE_HFP_HF) {
203
-       int fd = _audio_acquire(backend, path, &codec);
204
-       if (fd < 0) {
205
-           spa_log_error(backend->log, "Failed to retrieve codec for %s", path);
206
-           return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
207
-       }
208
-       /* shutdown to make sure connection is dropped immediately */
209
-       shutdown(fd, SHUT_RDWR);
210
-       close(fd);
211
-   }
212
-
213
    if (!remote_address || !local_address) {
214
        spa_log_error(backend->log, "Missing addresses for %s", path);
215
        return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
216
@@ -371,8 +438,31 @@
217
    spa_bt_device_add_profile(d, profile);
218
 
219
    t = _transport_create(backend, path, d, profile, codec, (struct spa_callbacks *)&ofono_transport_impl);
220
+   if (t == NULL) {
221
+       spa_log_error(backend->log, "failed to create transport: %s", spa_strerror(-errno));
222
+       return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
223
+   }
224
 
225
-   spa_bt_device_connect_profile(t->device, profile);
226
+   td = t->user_data;
227
+
228
+   /*
229
+    * For HF profile, delay profile connect, so that we likely don't do it at the
230
+    * same time as the device is busy with A2DP connect. This avoids some oFono
231
+    * misbehavior (see comment in _audio_acquire above).
232
+    *
233
+    * For AG mode, we delay the emission of the nodes, so it is not necessary
234
+    * to know the codec in advance.
235
+    */
236
+   if (profile == SPA_BT_PROFILE_HFP_HF) {
237
+       struct timespec ts;
238
+       ts.tv_sec = INITIAL_INTERVAL_NSEC / SPA_NSEC_PER_SEC;
239
+       ts.tv_nsec = INITIAL_INTERVAL_NSEC % SPA_NSEC_PER_SEC;
240
+       spa_loop_utils_update_timer(backend->loop_utils, backend->timer,
241
+               &ts, NULL, false);
242
+   } else {
243
+       td->activated = true;
244
+       spa_bt_device_connect_profile(t->device, t->profile);
245
+   }
246
 
247
    spa_log_debug(backend->log, "Transport %s available, codec %d", t->path, t->codec);
248
 
249
@@ -754,6 +844,9 @@
250
        backend->filters_added = false;
251
    }
252
 
253
+   if (backend->timer)
254
+       spa_loop_utils_destroy_source(backend->loop_utils, backend->timer);
255
+
256
    dbus_connection_unregister_object_path(backend->conn, OFONO_AUDIO_CLIENT);
257
 
258
    free(backend);
259
@@ -819,6 +912,8 @@
260
    backend->log = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_Log);
261
    backend->dbus = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_DBus);
262
    backend->main_loop = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_Loop);
263
+   backend->main_system = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_System);
264
+   backend->loop_utils = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_LoopUtils);
265
    backend->conn = dbus_connection;
266
    if (info && (str = spa_dict_lookup(info, "bluez5.enable-msbc")))
267
        backend->msbc_supported = spa_atob(str);
268
@@ -827,6 +922,12 @@
269
 
270
    spa_log_topic_init(backend->log, &log_topic);
271
 
272
+   backend->timer = spa_loop_utils_add_timer(backend->loop_utils, activate_timer_event, backend);
273
+   if (backend->timer == NULL) {
274
+       free(backend);
275
+       return NULL;
276
+   }
277
+
278
    if (!dbus_connection_register_object_path(backend->conn,
279
                          OFONO_AUDIO_CLIENT,
280
                          &vtable_profile, backend)) {
281
pipewire-0.3.48.tar.gz/spa/plugins/bluez5/bluez5-dbus.c -> pipewire-0.3.49.tar.gz/spa/plugins/bluez5/bluez5-dbus.c Changed
46
 
1
@@ -837,7 +837,7 @@
2
    return NULL;
3
 }
4
 
5
-static void device_update_last_bluez_action_time(struct spa_bt_device *device)
6
+void spa_bt_device_update_last_bluez_action_time(struct spa_bt_device *device)
7
 {
8
    struct timespec ts;
9
    spa_system_clock_gettime(device->monitor->main_system, CLOCK_MONOTONIC, &ts);
10
@@ -867,7 +867,7 @@
11
 
12
    spa_list_prepend(&monitor->device_list, &d->link);
13
 
14
-   device_update_last_bluez_action_time(d);
15
+   spa_bt_device_update_last_bluez_action_time(d);
16
 
17
    return d;
18
 }
19
@@ -2595,7 +2595,7 @@
20
        goto next;
21
    }
22
 
23
-   device_update_last_bluez_action_time(sw->device);
24
+   spa_bt_device_update_last_bluez_action_time(sw->device);
25
 
26
    spa_log_info(sw->device->monitor->log, "a2dp codec switch %p: trying codec %s for endpoint %s, local endpoint %s",
27
                 sw, codec->name, ep->path, local_endpoint);
28
@@ -2727,7 +2727,7 @@
29
    dbus_pending_call_unref(pending);
30
    sw->pending = NULL;
31
 
32
-   device_update_last_bluez_action_time(device);
33
+   spa_bt_device_update_last_bluez_action_time(device);
34
 
35
    if (!a2dp_codec_switch_goto_active(sw)) {
36
        if (r != NULL)
37
@@ -3030,7 +3030,7 @@
38
        return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
39
    }
40
 
41
-   device_update_last_bluez_action_time(transport->device);
42
+   spa_bt_device_update_last_bluez_action_time(transport->device);
43
 
44
    if (profile & SPA_BT_PROFILE_A2DP_SOURCE) {
45
        /* PW is the rendering device so it's responsible for reporting hardware volume. */
46
pipewire-0.3.48.tar.gz/spa/plugins/bluez5/codec-loader.c -> pipewire-0.3.49.tar.gz/spa/plugins/bluez5/codec-loader.c Changed
19
 
1
@@ -103,7 +103,7 @@
2
    spa_log_debug(impl->log, "loading codecs from %s", factory_name);
3
 
4
    if ((res = spa_handle_get_interface(handle, SPA_TYPE_INTERFACE_Bluez5CodecA2DP, &iface)) < 0) {
5
-       spa_log_info(impl->log, "Bluetooth codec plugin %s has no codec interface",
6
+       spa_log_warn(impl->log, "Bluetooth codec plugin %s has no codec interface",
7
                factory_name);
8
        goto fail;
9
    }
10
@@ -111,7 +111,7 @@
11
    bluez5_codec_a2dp = iface;
12
 
13
    if (bluez5_codec_a2dp->iface.version != SPA_VERSION_BLUEZ5_CODEC_A2DP) {
14
-       spa_log_info(impl->log, "codec plugin %s has incompatible ABI version (%d != %d)",
15
+       spa_log_warn(impl->log, "codec plugin %s has incompatible ABI version (%d != %d)",
16
                factory_name, bluez5_codec_a2dp->iface.version, SPA_VERSION_BLUEZ5_CODEC_A2DP);
17
        res = -ENOENT;
18
        goto fail;
19
pipewire-0.3.48.tar.gz/spa/plugins/bluez5/defs.h -> pipewire-0.3.49.tar.gz/spa/plugins/bluez5/defs.h Changed
19
 
1
@@ -60,9 +60,6 @@
2
 
3
 #define PIPEWIRE_BATTERY_PROVIDER "/org/freedesktop/pipewire/battery"
4
 
5
-#define MIN_LATENCY    512
6
-#define MAX_LATENCY    1024
7
-
8
 #define OBJECT_MANAGER_INTROSPECT_XML                                          \
9
    DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE                                  \
10
    "<node>\n"                                                                 \
11
@@ -499,6 +496,7 @@
12
 int spa_bt_device_supports_hfp_codec(struct spa_bt_device *device, unsigned int codec);
13
 int spa_bt_device_release_transports(struct spa_bt_device *device);
14
 int spa_bt_device_report_battery_level(struct spa_bt_device *device, uint8_t percentage);
15
+void spa_bt_device_update_last_bluez_action_time(struct spa_bt_device *device);
16
 
17
 #define spa_bt_device_emit(d,m,v,...)          spa_hook_list_call(&(d)->listener_list, \
18
                                struct spa_bt_device_events,    \
19
pipewire-0.3.48.tar.gz/spa/plugins/bluez5/quirks.c -> pipewire-0.3.49.tar.gz/spa/plugins/bluez5/quirks.c Changed
20
 
1
@@ -262,14 +262,16 @@
2
    } else {
3
        char path[PATH_MAX];
4
        const char *dir = getenv("SPA_DATA_DIR");
5
+       int res;
6
 
7
        if (dir == NULL)
8
            dir = SPADATADIR;
9
 
10
        if (spa_scnprintf(path, sizeof(path), "%s/bluez5/bluez-hardware.conf", dir) >= 0)
11
-           load_conf(this, path);
12
+           if ((res = load_conf(this, path)) < 0)
13
+               spa_log_warn(this->log, "failed to load '%s': %s", path,
14
+                       spa_strerror(res));
15
    }
16
-
17
    if (!(this->kernel_rules && this->adapter_rules && this->device_rules))
18
        spa_log_warn(this->log, "failed to load bluez-hardware.conf");
19
 
20
pipewire-0.3.48.tar.gz/spa/plugins/bluez5/sco-sink.c -> pipewire-0.3.49.tar.gz/spa/plugins/bluez5/sco-sink.c Changed
10
 
1
@@ -66,6 +66,8 @@
2
 };
3
 
4
 #define MAX_BUFFERS 32
5
+#define MIN_LATENCY    512
6
+#define MAX_LATENCY    1024
7
 
8
 struct buffer {
9
    uint32_t id;
10
pipewire-0.3.48.tar.gz/spa/plugins/support/loop.c -> pipewire-0.3.49.tar.gz/spa/plugins/support/loop.c Changed
447
 
1
@@ -90,6 +90,7 @@
2
    uint8_t buffer_mem[DATAS_SIZE + MAX_ALIGN];
3
 
4
    unsigned int flushing:1;
5
+   unsigned int polling:1;
6
 };
7
 
8
 struct source_impl {
9
@@ -98,7 +99,6 @@
10
    struct impl *impl;
11
    struct spa_list link;
12
 
13
-   bool close;
14
    union {
15
        spa_source_io_func_t io;
16
        spa_source_idle_func_t idle;
17
@@ -106,8 +106,11 @@
18
        spa_source_timer_func_t timer;
19
        spa_source_signal_func_t signal;
20
    } func;
21
-   bool enabled;
22
+
23
    struct spa_source *fallback;
24
+
25
+   bool close;
26
+   bool enabled;
27
 };
28
 /** \endcond */
29
 
30
@@ -116,28 +119,51 @@
31
    struct impl *impl = object;
32
    source->loop = &impl->loop;
33
    source->priv = NULL;
34
+   source->rmask = 0;
35
    return spa_system_pollfd_add(impl->system, impl->poll_fd, source->fd, source->mask, source);
36
 }
37
 
38
 static int loop_update_source(void *object, struct spa_source *source)
39
 {
40
    struct impl *impl = object;
41
+
42
+   spa_assert(source->loop == &impl->loop);
43
+
44
    return spa_system_pollfd_mod(impl->system, impl->poll_fd, source->fd, source->mask, source);
45
 }
46
 
47
-static int loop_remove_source(void *object, struct spa_source *source)
48
+static void detach_source(struct spa_source *source)
49
 {
50
-   struct impl *impl = object;
51
    struct spa_poll_event *e;
52
+
53
+   source->loop = NULL;
54
+   source->rmask = 0;
55
+
56
    if ((e = source->priv)) {
57
        /* active in an iteration of the loop, remove it from there */
58
        e->data = NULL;
59
        source->priv = NULL;
60
    }
61
-   source->loop = NULL;
62
+}
63
+
64
+static int remove_from_poll(struct impl *impl, struct spa_source *source)
65
+{
66
+   spa_assert(source->loop == &impl->loop);
67
+
68
    return spa_system_pollfd_del(impl->system, impl->poll_fd, source->fd);
69
 }
70
 
71
+static int loop_remove_source(void *object, struct spa_source *source)
72
+{
73
+   struct impl *impl = object;
74
+   spa_assert(!impl->polling);
75
+
76
+   int res = remove_from_poll(impl, source);
77
+   detach_source(source);
78
+
79
+   return res;
80
+}
81
+
82
 static void flush_items(struct impl *impl)
83
 {
84
    uint32_t index;
85
@@ -151,7 +177,7 @@
86
        item = SPA_PTROFF(impl->buffer_data, index & (DATAS_SIZE - 1), struct invoke_item);
87
        block = item->block;
88
 
89
-       spa_log_trace(impl->log, "%p: flush item %p", impl, item);
90
+       spa_log_trace_fp(impl->log, "%p: flush item %p", impl, item);
91
        item->res = item->func ? item->func(&impl->loop,
92
                true, item->seq, item->data, item->size,
93
               item->user_data) : 0;
94
@@ -223,7 +249,7 @@
95
    item->user_data = user_data;
96
    item->item_size = SPA_ROUND_UP_N(sizeof(struct invoke_item) + size, ITEM_ALIGN);
97
 
98
-   spa_log_trace(impl->log, "%p: add item %p filled:%d", impl, item, filled);
99
+   spa_log_trace_fp(impl->log, "%p: add item %p filled:%d", impl, item, filled);
100
 
101
    if (l0 >= item->item_size) {
102
        /* item + size fit in current ringbuffer idx */
103
@@ -322,15 +348,25 @@
104
 
105
    spa_log_trace(impl->log, "%p: leave %lu", impl, impl->thread);
106
 
107
-   if (--impl->enter_count == 0)
108
+   if (--impl->enter_count == 0) {
109
        impl->thread = 0;
110
+       impl->polling = false;
111
+   }
112
+}
113
+
114
+static inline void free_source(struct source_impl *s)
115
+{
116
+   detach_source(&s->source);
117
+   free(s);
118
 }
119
 
120
 static inline void process_destroy(struct impl *impl)
121
 {
122
    struct source_impl *source, *tmp;
123
+
124
    spa_list_for_each_safe(source, tmp, &impl->destroy_list, link)
125
-       free(source);
126
+       free_source(source);
127
+
128
    spa_list_init(&impl->destroy_list);
129
 }
130
 
131
@@ -340,20 +376,22 @@
132
    struct spa_poll_event ep[MAX_EP], *e;
133
    int i, nfds;
134
 
135
+   impl->polling = true;
136
    spa_loop_control_hook_before(&impl->hooks_list);
137
 
138
-   nfds = spa_system_pollfd_wait(impl->system, impl->poll_fd, ep, MAX_EP, timeout);
139
+   nfds = spa_system_pollfd_wait(impl->system, impl->poll_fd, ep, SPA_N_ELEMENTS(ep), timeout);
140
 
141
    spa_loop_control_hook_after(&impl->hooks_list);
142
-
143
-   if (SPA_UNLIKELY(nfds < 0))
144
-       return nfds;
145
+   impl->polling = false;
146
 
147
    /* first we set all the rmasks, then call the callbacks. The reason is that
148
     * some callback might also want to look at other sources it manages and
149
     * can then reset the rmask to suppress the callback */
150
    for (i = 0; i < nfds; i++) {
151
        struct spa_source *s = ep[i].data;
152
+
153
+       spa_assert(s->loop == &impl->loop);
154
+
155
        s->rmask = ep[i].events;
156
        /* already active in another iteration of the loop,
157
         * remove it from that iteration */
158
@@ -361,24 +399,32 @@
159
            e->data = NULL;
160
        s->priv = &ep[i];
161
    }
162
+
163
+   if (SPA_UNLIKELY(!spa_list_is_empty(&impl->destroy_list)))
164
+       process_destroy(impl);
165
+
166
    for (i = 0; i < nfds; i++) {
167
        struct spa_source *s = ep[i].data;
168
-       if (SPA_LIKELY(s && s->rmask && s->loop)) {
169
-           s->priv = NULL;
170
+       if (SPA_LIKELY(s && s->rmask))
171
            s->func(s);
172
+   }
173
+
174
+   for (i = 0; i < nfds; i++) {
175
+       struct spa_source *s = ep[i].data;
176
+       if (SPA_LIKELY(s)) {
177
+           s->rmask = 0;
178
+           s->priv = NULL;
179
        }
180
    }
181
-   if (SPA_UNLIKELY(!spa_list_is_empty(&impl->destroy_list)))
182
-       process_destroy(impl);
183
 
184
    return nfds;
185
 }
186
 
187
 static void source_io_func(struct spa_source *source)
188
 {
189
-   struct source_impl *impl = SPA_CONTAINER_OF(source, struct source_impl, source);
190
-   spa_log_trace_fp(impl->impl->log, "%p: io %08x", impl, source->rmask);
191
-   impl->func.io(source->data, source->fd, source->rmask);
192
+   struct source_impl *s = SPA_CONTAINER_OF(source, struct source_impl, source);
193
+   spa_log_trace_fp(s->impl->log, "%p: io %08x", s, source->rmask);
194
+   s->func.io(source->data, source->fd, source->rmask);
195
 }
196
 
197
 static struct spa_source *loop_add_io(void *object,
198
@@ -394,7 +440,6 @@
199
    if (source == NULL)
200
        goto error_exit;
201
 
202
-   source->source.loop = &impl->loop;
203
    source->source.func = source_io_func;
204
    source->source.data = data;
205
    source->source.fd = fd;
206
@@ -434,8 +479,13 @@
207
    struct impl *impl = object;
208
    struct source_impl *s = SPA_CONTAINER_OF(source, struct source_impl, source);
209
    int res;
210
+
211
+   spa_assert(s->impl == object);
212
+   spa_assert(source->func == source_io_func);
213
+
214
+   spa_log_trace(impl->log, "%p: update %08x -> %08x", s, source->mask, mask);
215
    source->mask = mask;
216
-   spa_log_trace(impl->log, "%p: update %08x", s, mask);
217
+
218
    if (s->fallback)
219
        res = spa_loop_utils_enable_idle(&impl->utils, s->fallback,
220
                mask & (SPA_IO_IN | SPA_IO_OUT) ? true : false);
221
@@ -446,26 +496,29 @@
222
 
223
 static void source_idle_func(struct spa_source *source)
224
 {
225
-   struct source_impl *impl = SPA_CONTAINER_OF(source, struct source_impl, source);
226
-   impl->func.idle(source->data);
227
+   struct source_impl *s = SPA_CONTAINER_OF(source, struct source_impl, source);
228
+   s->func.idle(source->data);
229
 }
230
 
231
 static int loop_enable_idle(void *object, struct spa_source *source, bool enabled)
232
 {
233
-   struct source_impl *impl = SPA_CONTAINER_OF(source, struct source_impl, source);
234
+   struct source_impl *s = SPA_CONTAINER_OF(source, struct source_impl, source);
235
    int res = 0;
236
 
237
-   if (enabled && !impl->enabled) {
238
-       if ((res = spa_system_eventfd_write(impl->impl->system, source->fd, 1)) < 0)
239
-           spa_log_warn(impl->impl->log, "%p: failed to write idle fd %d: %s",
240
+   spa_assert(s->impl == object);
241
+   spa_assert(source->func == source_idle_func);
242
+
243
+   if (enabled && !s->enabled) {
244
+       if ((res = spa_system_eventfd_write(s->impl->system, source->fd, 1)) < 0)
245
+           spa_log_warn(s->impl->log, "%p: failed to write idle fd %d: %s",
246
                    source, source->fd, spa_strerror(res));
247
-   } else if (!enabled && impl->enabled) {
248
+   } else if (!enabled && s->enabled) {
249
        uint64_t count;
250
-       if ((res = spa_system_eventfd_read(impl->impl->system, source->fd, &count)) < 0)
251
-           spa_log_warn(impl->impl->log, "%p: failed to read idle fd %d: %s",
252
+       if ((res = spa_system_eventfd_read(s->impl->system, source->fd, &count)) < 0)
253
+           spa_log_warn(s->impl->log, "%p: failed to read idle fd %d: %s",
254
                    source, source->fd, spa_strerror(res));
255
    }
256
-   impl->enabled = enabled;
257
+   s->enabled = enabled;
258
    return res;
259
 }
260
 
261
@@ -483,7 +536,6 @@
262
    if ((res = spa_system_eventfd_create(impl->system, SPA_FD_CLOEXEC | SPA_FD_NONBLOCK)) < 0)
263
        goto error_exit_free;
264
 
265
-   source->source.loop = &impl->loop;
266
    source->source.func = source_idle_func;
267
    source->source.data = data;
268
    source->source.fd = res;
269
@@ -513,15 +565,15 @@
270
 
271
 static void source_event_func(struct spa_source *source)
272
 {
273
-   struct source_impl *impl = SPA_CONTAINER_OF(source, struct source_impl, source);
274
+   struct source_impl *s = SPA_CONTAINER_OF(source, struct source_impl, source);
275
    uint64_t count = 0;
276
    int res;
277
 
278
-   if ((res = spa_system_eventfd_read(impl->impl->system, source->fd, &count)) < 0)
279
-       spa_log_warn(impl->impl->log, "%p: failed to read event fd %d: %s",
280
+   if ((res = spa_system_eventfd_read(s->impl->system, source->fd, &count)) < 0)
281
+       spa_log_warn(s->impl->log, "%p: failed to read event fd %d: %s",
282
                source, source->fd, spa_strerror(res));
283
 
284
-   impl->func.event(source->data, count);
285
+   s->func.event(source->data, count);
286
 }
287
 
288
 static struct spa_source *loop_add_event(void *object,
289
@@ -538,7 +590,6 @@
290
    if ((res = spa_system_eventfd_create(impl->system, SPA_FD_CLOEXEC | SPA_FD_NONBLOCK)) < 0)
291
        goto error_exit_free;
292
 
293
-   source->source.loop = &impl->loop;
294
    source->source.func = source_event_func;
295
    source->source.data = data;
296
    source->source.fd = res;
297
@@ -565,27 +616,30 @@
298
 
299
 static int loop_signal_event(void *object, struct spa_source *source)
300
 {
301
-   struct source_impl *impl = SPA_CONTAINER_OF(source, struct source_impl, source);
302
+   struct source_impl *s = SPA_CONTAINER_OF(source, struct source_impl, source);
303
    int res;
304
 
305
-   if (SPA_UNLIKELY((res = spa_system_eventfd_write(impl->impl->system, source->fd, 1)) < 0))
306
-       spa_log_warn(impl->impl->log, "%p: failed to write event fd %d: %s",
307
+   spa_assert(s->impl == object);
308
+   spa_assert(source->func == source_event_func);
309
+
310
+   if (SPA_UNLIKELY((res = spa_system_eventfd_write(s->impl->system, source->fd, 1)) < 0))
311
+       spa_log_warn(s->impl->log, "%p: failed to write event fd %d: %s",
312
                source, source->fd, spa_strerror(res));
313
    return res;
314
 }
315
 
316
 static void source_timer_func(struct spa_source *source)
317
 {
318
-   struct source_impl *impl = SPA_CONTAINER_OF(source, struct source_impl, source);
319
+   struct source_impl *s = SPA_CONTAINER_OF(source, struct source_impl, source);
320
    uint64_t expirations = 0;
321
    int res;
322
 
323
-   if (SPA_UNLIKELY((res = spa_system_timerfd_read(impl->impl->system,
324
+   if (SPA_UNLIKELY((res = spa_system_timerfd_read(s->impl->system,
325
                source->fd, &expirations)) < 0))
326
-       spa_log_warn(impl->impl->log, "%p: failed to read timer fd %d: %s",
327
+       spa_log_warn(s->impl->log, "%p: failed to read timer fd %d: %s",
328
                source, source->fd, spa_strerror(res));
329
 
330
-   impl->func.timer(source->data, expirations);
331
+   s->func.timer(source->data, expirations);
332
 }
333
 
334
 static struct spa_source *loop_add_timer(void *object,
335
@@ -603,7 +657,6 @@
336
            SPA_FD_CLOEXEC | SPA_FD_NONBLOCK)) < 0)
337
        goto error_exit_free;
338
 
339
-   source->source.loop = &impl->loop;
340
    source->source.func = source_timer_func;
341
    source->source.data = data;
342
    source->source.fd = res;
343
@@ -632,10 +685,13 @@
344
 loop_update_timer(void *object, struct spa_source *source,
345
          struct timespec *value, struct timespec *interval, bool absolute)
346
 {
347
-   struct impl *impl = object;
348
+   struct source_impl *s = SPA_CONTAINER_OF(source, struct source_impl, source);
349
    struct itimerspec its;
350
    int flags = 0, res;
351
 
352
+   spa_assert(s->impl == object);
353
+   spa_assert(source->func == source_timer_func);
354
+
355
    spa_zero(its);
356
    if (SPA_LIKELY(value)) {
357
        its.it_value = *value;
358
@@ -648,7 +704,7 @@
359
    if (SPA_LIKELY(absolute))
360
        flags |= SPA_FD_TIMER_ABSTIME;
361
 
362
-   if (SPA_UNLIKELY((res = spa_system_timerfd_settime(impl->system, source->fd, flags, &its, NULL)) < 0))
363
+   if (SPA_UNLIKELY((res = spa_system_timerfd_settime(s->impl->system, source->fd, flags, &its, NULL)) < 0))
364
        return res;
365
 
366
    return 0;
367
@@ -656,14 +712,14 @@
368
 
369
 static void source_signal_func(struct spa_source *source)
370
 {
371
-   struct source_impl *impl = SPA_CONTAINER_OF(source, struct source_impl, source);
372
+   struct source_impl *s = SPA_CONTAINER_OF(source, struct source_impl, source);
373
    int res, signal_number = 0;
374
 
375
-   if ((res = spa_system_signalfd_read(impl->impl->system, source->fd, &signal_number)) < 0)
376
-       spa_log_warn(impl->impl->log, "%p: failed to read signal fd %d: %s",
377
+   if ((res = spa_system_signalfd_read(s->impl->system, source->fd, &signal_number)) < 0)
378
+       spa_log_warn(s->impl->log, "%p: failed to read signal fd %d: %s",
379
                source, source->fd, spa_strerror(res));
380
 
381
-   impl->func.signal(source->data, signal_number);
382
+   s->func.signal(source->data, signal_number);
383
 }
384
 
385
 static struct spa_source *loop_add_signal(void *object,
386
@@ -682,7 +738,6 @@
387
            signal_number, SPA_FD_CLOEXEC | SPA_FD_NONBLOCK)) < 0)
388
        goto error_exit_free;
389
 
390
-   source->source.loop = &impl->loop;
391
    source->source.func = source_signal_func;
392
    source->source.data = data;
393
    source->source.fd = res;
394
@@ -709,22 +764,28 @@
395
 
396
 static void loop_destroy_source(void *object, struct spa_source *source)
397
 {
398
-   struct source_impl *impl = SPA_CONTAINER_OF(source, struct source_impl, source);
399
+   struct source_impl *s = SPA_CONTAINER_OF(source, struct source_impl, source);
400
+
401
+   spa_assert(s->impl == object);
402
 
403
-   spa_log_trace(impl->impl->log, "%p ", impl);
404
+   spa_log_trace(s->impl->log, "%p ", s);
405
 
406
-   spa_list_remove(&impl->link);
407
+   spa_list_remove(&s->link);
408
 
409
-   if (impl->fallback)
410
-       loop_destroy_source(impl->impl, impl->fallback);
411
-   else if (source->loop)
412
-       loop_remove_source(impl->impl, source);
413
+   if (s->fallback)
414
+       loop_destroy_source(s->impl, s->fallback);
415
+   else
416
+       remove_from_poll(s->impl, source);
417
 
418
-   if (source->fd != -1 && impl->close) {
419
-       spa_system_close(impl->impl->system, source->fd);
420
+   if (source->fd != -1 && s->close) {
421
+       spa_system_close(s->impl->system, source->fd);
422
        source->fd = -1;
423
    }
424
-   spa_list_insert(&impl->impl->destroy_list, &impl->link);
425
+
426
+   if (!s->impl->polling)
427
+       free_source(s);
428
+   else
429
+       spa_list_insert(&s->impl->destroy_list, &s->link);
430
 }
431
 
432
 static const struct spa_loop_methods impl_loop = {
433
@@ -792,11 +853,11 @@
434
        spa_log_warn(impl->log, "%p: loop is entered %d times",
435
                impl, impl->enter_count);
436
 
437
+   spa_assert(!impl->polling);
438
+
439
    spa_list_consume(source, &impl->source_list, link)
440
        loop_destroy_source(impl, &source->source);
441
 
442
-   process_destroy(impl);
443
-
444
    spa_system_close(impl->system, impl->ack_fd);
445
    spa_system_close(impl->system, impl->poll_fd);
446
 
447
pipewire-0.3.48.tar.gz/spa/plugins/support/node-driver.c -> pipewire-0.3.49.tar.gz/spa/plugins/support/node-driver.c Changed
24
 
1
@@ -350,6 +350,13 @@
2
    return 0;
3
 }
4
 
5
+static int do_remove_timer(struct spa_loop *loop, bool async, uint32_t seq, const void *data, size_t size, void *user_data)
6
+{
7
+   struct impl *this = user_data;
8
+   spa_loop_remove_source(this->data_loop, &this->timer_source);
9
+   return 0;
10
+}
11
+
12
 static int impl_clear(struct spa_handle *handle)
13
 {
14
    struct impl *this;
15
@@ -358,7 +365,7 @@
16
 
17
    this = (struct impl *) handle;
18
 
19
-   spa_loop_remove_source(this->data_loop, &this->timer_source);
20
+   spa_loop_invoke(this->data_loop, do_remove_timer, 0, NULL, 0, true, this);
21
    spa_system_close(this->data_system, this->timer_source.fd);
22
 
23
    return 0;
24
pipewire-0.3.48.tar.gz/spa/plugins/support/null-audio-sink.c -> pipewire-0.3.49.tar.gz/spa/plugins/support/null-audio-sink.c Changed
24
 
1
@@ -779,6 +779,13 @@
2
    return 0;
3
 }
4
 
5
+static int do_remove_timer(struct spa_loop *loop, bool async, uint32_t seq, const void *data, size_t size, void *user_data)
6
+{
7
+   struct impl *this = user_data;
8
+   spa_loop_remove_source(this->data_loop, &this->timer_source);
9
+   return 0;
10
+}
11
+
12
 static int impl_clear(struct spa_handle *handle)
13
 {
14
    struct impl *this;
15
@@ -787,7 +794,7 @@
16
 
17
    this = (struct impl *) handle;
18
 
19
-   spa_loop_remove_source(this->data_loop, &this->timer_source);
20
+   spa_loop_invoke(this->data_loop, do_remove_timer, 0, NULL, 0, true, this);
21
    spa_system_close(this->data_system, this->timer_source.fd);
22
 
23
    return 0;
24
pipewire-0.3.48.tar.gz/spa/plugins/test/fakesink.c -> pipewire-0.3.49.tar.gz/spa/plugins/test/fakesink.c Changed
24
 
1
@@ -698,6 +698,13 @@
2
    return 0;
3
 }
4
 
5
+static int do_remove_timer(struct spa_loop *loop, bool async, uint32_t seq, const void *data, size_t size, void *user_data)
6
+{
7
+   struct impl *this = user_data;
8
+   spa_loop_remove_source(this->data_loop, &this->timer_source);
9
+   return 0;
10
+}
11
+
12
 static int impl_clear(struct spa_handle *handle)
13
 {
14
    struct impl *this;
15
@@ -707,7 +714,7 @@
16
    this = (struct impl *) handle;
17
 
18
    if (this->data_loop)
19
-       spa_loop_remove_source(this->data_loop, &this->timer_source);
20
+       spa_loop_invoke(this->data_loop, do_remove_timer, 0, NULL, 0, true, this);
21
    spa_system_close(this->data_system, this->timer_source.fd);
22
 
23
    return 0;
24
pipewire-0.3.48.tar.gz/spa/plugins/test/fakesrc.c -> pipewire-0.3.49.tar.gz/spa/plugins/test/fakesrc.c Changed
24
 
1
@@ -731,6 +731,13 @@
2
    return 0;
3
 }
4
 
5
+static int do_remove_timer(struct spa_loop *loop, bool async, uint32_t seq, const void *data, size_t size, void *user_data)
6
+{
7
+   struct impl *this = user_data;
8
+   spa_loop_remove_source(this->data_loop, &this->timer_source);
9
+   return 0;
10
+}
11
+
12
 static int impl_clear(struct spa_handle *handle)
13
 {
14
    struct impl *this;
15
@@ -740,7 +747,7 @@
16
    this = (struct impl *) handle;
17
 
18
    if (this->data_loop)
19
-       spa_loop_remove_source(this->data_loop, &this->timer_source);
20
+       spa_loop_invoke(this->data_loop, do_remove_timer, 0, NULL, 0, true, this);
21
    spa_system_close(this->data_system, this->timer_source.fd);
22
 
23
    return 0;
24
pipewire-0.3.48.tar.gz/spa/plugins/videotestsrc/videotestsrc.c -> pipewire-0.3.49.tar.gz/spa/plugins/videotestsrc/videotestsrc.c Changed
24
 
1
@@ -838,6 +838,13 @@
2
    return 0;
3
 }
4
 
5
+static int do_remove_timer(struct spa_loop *loop, bool async, uint32_t seq, const void *data, size_t size, void *user_data)
6
+{
7
+   struct impl *this = user_data;
8
+   spa_loop_remove_source(this->data_loop, &this->timer_source);
9
+   return 0;
10
+}
11
+
12
 static int impl_clear(struct spa_handle *handle)
13
 {
14
    struct impl *this;
15
@@ -847,7 +854,7 @@
16
    this = (struct impl *) handle;
17
 
18
    if (this->data_loop)
19
-       spa_loop_remove_source(this->data_loop, &this->timer_source);
20
+       spa_loop_invoke(this->data_loop, do_remove_timer, 0, NULL, 0, true, this);
21
    spa_system_close(this->data_system, this->timer_source.fd);
22
 
23
    return 0;
24
pipewire-0.3.48.tar.gz/spa/plugins/vulkan/vulkan-compute-source.c -> pipewire-0.3.49.tar.gz/spa/plugins/vulkan/vulkan-compute-source.c Changed
24
 
1
@@ -854,6 +854,13 @@
2
    return 0;
3
 }
4
 
5
+static int do_remove_timer(struct spa_loop *loop, bool async, uint32_t seq, const void *data, size_t size, void *user_data)
6
+{
7
+   struct impl *this = user_data;
8
+   spa_loop_remove_source(this->data_loop, &this->timer_source);
9
+   return 0;
10
+}
11
+
12
 static int impl_clear(struct spa_handle *handle)
13
 {
14
    struct impl *this;
15
@@ -863,7 +870,7 @@
16
    this = (struct impl *) handle;
17
 
18
    if (this->data_loop)
19
-       spa_loop_remove_source(this->data_loop, &this->timer_source);
20
+       spa_loop_invoke(this->data_loop, do_remove_timer, 0, NULL, 0, true, this);
21
    spa_system_close(this->data_system, this->timer_source.fd);
22
 
23
    return 0;
24
pipewire-0.3.48.tar.gz/src/daemon/client-rt.conf.in -> pipewire-0.3.49.tar.gz/src/daemon/client-rt.conf.in Changed
20
 
1
@@ -81,12 +81,13 @@
2
     #node.latency          = 1024/48000
3
     #node.autoconnect      = true
4
     #resample.quality      = 4
5
-    #channelmix.normalize  = true
6
+    #channelmix.normalize  = false
7
     #channelmix.mix-lfe    = true
8
-    #channelmix.upmix      = false
9
-    #channelmix.lfe-cutoff = 0
10
-    #channelmix.fc-cutoff  = 0
11
+    #channelmix.upmix      = true
12
+    #channelmix.upmix-method = simple  # none, psd
13
+    #channelmix.lfe-cutoff = 120
14
+    #channelmix.fc-cutoff  = 6000
15
     #channelmix.rear-delay = 12.0
16
-    #channelmix.stereo-widen = 0.0
17
+    #channelmix.stereo-widen = 0.1
18
     #channelmix.hilbert-taps = 0
19
 }
20
pipewire-0.3.48.tar.gz/src/daemon/client.conf.in -> pipewire-0.3.49.tar.gz/src/daemon/client.conf.in Changed
20
 
1
@@ -71,12 +71,13 @@
2
     #node.latency          = 1024/48000
3
     #node.autoconnect      = true
4
     #resample.quality      = 4
5
-    #channelmix.normalize  = true
6
+    #channelmix.normalize  = false
7
     #channelmix.mix-lfe    = false
8
-    #channelmix.upmix      = false
9
-    #channelmix.lfe-cutoff = 0
10
-    #channelmix.fc-cutoff  = 0
11
+    #channelmix.upmix      = true
12
+    #channelmix.upmix-method = simple  # none, psd
13
+    #channelmix.lfe-cutoff = 120
14
+    #channelmix.fc-cutoff  = 6000
15
     #channelmix.rear-delay = 12.0
16
-    #channelmix.stereo-widen = 0.0
17
+    #channelmix.stereo-widen = 0.1
18
     #channelmix.hilbert-taps = 0
19
 }
20
pipewire-0.3.48.tar.gz/src/daemon/minimal.conf.in -> pipewire-0.3.49.tar.gz/src/daemon/minimal.conf.in Changed
41
 
1
@@ -202,13 +202,14 @@
2
             #resample.quality       = 4
3
             resample.disable       = true
4
             #monitor.channel-volumes = false
5
-            #channelmix.normalize   = true
6
+            #channelmix.normalize   = false
7
             #channelmix.mix-lfe     = false
8
-            #channelmix.upmix       = false
9
-            #channelmix.lfe-cutoff  = 0
10
-            #channelmix.fc-cutoff  = 0
11
+            #channelmix.upmix       = true
12
+            #channelmix.upmix-method = simple  # none, psd
13
+            #channelmix.lfe-cutoff  = 120
14
+            #channelmix.fc-cutoff   = 6000
15
             #channelmix.rear-delay  = 12.0
16
-            #channelmix.stereo-widen = 0.0
17
+            #channelmix.stereo-widen = 0.1
18
             #channelmix.hilbert-taps = 0
19
             channelmix.disable     = true
20
             #node.param.Props      = {
21
@@ -261,13 +262,14 @@
22
             #audio.position         = "FL,FR"
23
             #resample.quality      = 4
24
             resample.disable      = true
25
-            #channelmix.normalize  = true
26
+            #channelmix.normalize  = false
27
             #channelmix.mix-lfe    = false
28
-            #channelmix.upmix      = false
29
-            #channelmix.lfe-cutoff = 0
30
-            #channelmix.fc-cutoff  = 0
31
+            #channelmix.upmix      = true
32
+            #channelmix.upmix-method = simple  # none, psd
33
+            #channelmix.lfe-cutoff = 120
34
+            #channelmix.fc-cutoff  = 6000
35
             #channelmix.rear-delay = 12.0
36
-            #channelmix.stereo-widen = 0.0
37
+            #channelmix.stereo-widen = 0.1
38
             #channelmix.hilbert-taps = 0
39
             channelmix.disable     = true
40
             #node.param.Props      = {
41
pipewire-0.3.48.tar.gz/src/daemon/pipewire-pulse.conf.in -> pipewire-0.3.49.tar.gz/src/daemon/pipewire-pulse.conf.in Changed
88
 
1
@@ -40,32 +40,8 @@
2
 
3
     { name = libpipewire-module-protocol-pulse
4
         args = {
5
-            # the addresses this server listens on
6
-            server.address = [
7
-                "unix:native"
8
-                #"unix:/tmp/something"              # absolute paths may be used
9
-                #"tcp:4713"                         # IPv4 and IPv6 on all addresses
10
-                #"tcp:[::]:9999"                    # IPv6 on all addresses
11
-                #"tcp:127.0.0.1:8888"               # IPv4 on a single address
12
-                #
13
-                #{ address = "tcp:4713"             # address
14
-                #  max-clients = 64                 # maximum number of clients
15
-                #  listen-backlog = 32              # backlog in the server listen queue
16
-                #  client.access = "restricted"     # permissions for clients
17
-                #}
18
-            ]
19
-            #pulse.min.req          = 256/48000     # 5ms
20
-            #pulse.default.req      = 960/48000     # 20 milliseconds
21
-            #pulse.min.frag         = 256/48000     # 5ms
22
-            #pulse.default.frag     = 96000/48000   # 2 seconds
23
-            #pulse.default.tlength  = 96000/48000   # 2 seconds
24
-            #pulse.min.quantum      = 256/48000     # 5ms
25
-            #pulse.default.format   = F32
26
-            #pulse.default.position = [ FL FR ]
27
-            # These overrides are only applied when running in a vm.
28
-            vm.overrides = {
29
-                pulse.min.quantum = 1024/48000      # 22ms
30
-            }
31
+       # contents of pulse.properties can also be placed here
32
+       # to have config per server.
33
         }
34
     }
35
 ]
36
@@ -81,16 +57,46 @@
37
     #node.latency          = 1024/48000
38
     #node.autoconnect      = true
39
     #resample.quality      = 4
40
-    #channelmix.normalize  = true
41
+    #channelmix.normalize  = false
42
     #channelmix.mix-lfe    = false
43
-    #channelmix.upmix      = false
44
-    #channelmix.lfe-cutoff = 0
45
-    #channelmix.fc-cutoff  = 0
46
+    #channelmix.upmix      = true
47
+    #channelmix.upmix-method = simple  # none, psd
48
+    #channelmix.lfe-cutoff = 120
49
+    #channelmix.fc-cutoff  = 6000
50
     #channelmix.rear-delay = 12.0
51
-    #channelmix.stereo-widen = 0.0
52
+    #channelmix.stereo-widen = 0.1
53
     #channelmix.hilbert-taps = 0
54
 }
55
 
56
+pulse.properties = {
57
+    # the addresses this server listens on
58
+    server.address = [
59
+        "unix:native"
60
+        #"unix:/tmp/something"              # absolute paths may be used
61
+        #"tcp:4713"                         # IPv4 and IPv6 on all addresses
62
+        #"tcp:[::]:9999"                    # IPv6 on all addresses
63
+        #"tcp:127.0.0.1:8888"               # IPv4 on a single address
64
+        #
65
+        #{ address = "tcp:4713"             # address
66
+        #  max-clients = 64                 # maximum number of clients
67
+        #  listen-backlog = 32              # backlog in the server listen queue
68
+        #  client.access = "restricted"     # permissions for clients
69
+        #}
70
+    ]
71
+    #pulse.min.req          = 256/48000     # 5ms
72
+    #pulse.default.req      = 960/48000     # 20 milliseconds
73
+    #pulse.min.frag         = 256/48000     # 5ms
74
+    #pulse.default.frag     = 96000/48000   # 2 seconds
75
+    #pulse.default.tlength  = 96000/48000   # 2 seconds
76
+    #pulse.min.quantum      = 256/48000     # 5ms
77
+    #pulse.default.format   = F32
78
+    #pulse.default.position = [ FL FR ]
79
+    # These overrides are only applied when running in a vm.
80
+    vm.overrides = {
81
+        pulse.min.quantum = 1024/48000      # 22ms
82
+    }
83
+}
84
+
85
 # client/stream specific properties
86
 pulse.rules = [
87
     {
88
pipewire-0.3.48.tar.gz/src/daemon/pipewire.c -> pipewire-0.3.49.tar.gz/src/daemon/pipewire.c Changed
17
 
1
@@ -26,6 +26,7 @@
2
 #include <signal.h>
3
 #include <getopt.h>
4
 #include <libgen.h>
5
+#include <locale.h>
6
 
7
 #include <spa/utils/result.h>
8
 #include <pipewire/pipewire.h>
9
@@ -74,6 +75,7 @@
10
    snprintf(path, sizeof(path), "%s.conf", argv[0]);
11
    config_name = basename(path);
12
 
13
+   setlocale(LC_ALL, "");
14
    pw_init(&argc, &argv);
15
 
16
    while ((c = getopt_long(argc, argv, "hVc:v", long_options, NULL)) != -1) {
17
pipewire-0.3.48.tar.gz/src/gst/gstpipewiredeviceprovider.c -> pipewire-0.3.49.tar.gz/src/gst/gstpipewiredeviceprovider.c Changed
133
 
1
@@ -44,6 +44,7 @@
2
 enum
3
 {
4
   PROP_ID = 1,
5
+  PROP_SERIAL,
6
 };
7
 
8
 static GstElement *
9
@@ -51,12 +52,16 @@
10
 {
11
   GstPipeWireDevice *pipewire_dev = GST_PIPEWIRE_DEVICE (device);
12
   GstElement *elem;
13
-  gchar *str;
14
+  gchar *id_str, *serial_str;
15
 
16
   elem = gst_element_factory_make (pipewire_dev->element, name);
17
-  str = g_strdup_printf ("%u", pipewire_dev->id);
18
-  g_object_set (elem, "path", str, NULL);
19
-  g_free (str);
20
+
21
+  /* XXX: eventually only add target-object here */
22
+  id_str = g_strdup_printf ("%u", pipewire_dev->id);
23
+  serial_str = g_strdup_printf ("%"PRIu64, pipewire_dev->serial);
24
+  g_object_set (elem, "path", id_str, "target-object", serial_str, NULL);
25
+  g_free (id_str);
26
+  g_free (serial_str);
27
 
28
   return elem;
29
 }
30
@@ -65,7 +70,7 @@
31
 gst_pipewire_device_reconfigure_element (GstDevice * device, GstElement * element)
32
 {
33
   GstPipeWireDevice *pipewire_dev = GST_PIPEWIRE_DEVICE (device);
34
-  gchar *str;
35
+  gchar *id_str, *serial_str;
36
 
37
   if (spa_streq(pipewire_dev->element, "pipewiresrc")) {
38
     if (!GST_IS_PIPEWIRE_SRC (element))
39
@@ -77,9 +82,12 @@
40
     g_assert_not_reached ();
41
   }
42
 
43
-  str = g_strdup_printf ("%u", pipewire_dev->id);
44
-  g_object_set (element, "path", str, NULL);
45
-  g_free (str);
46
+  /* XXX: eventually only add target-object here */
47
+  id_str = g_strdup_printf ("%u", pipewire_dev->id);
48
+  serial_str = g_strdup_printf ("%"PRIu64, pipewire_dev->serial);
49
+  g_object_set (element, "path", id_str, "target-object", serial_str, NULL);
50
+  g_free (id_str);
51
+  g_free (serial_str);
52
 
53
   return TRUE;
54
 }
55
@@ -97,6 +105,9 @@
56
     case PROP_ID:
57
       g_value_set_uint (value, device->id);
58
       break;
59
+    case PROP_SERIAL:
60
+      g_value_set_uint64 (value, device->serial);
61
+      break;
62
     default:
63
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
64
       break;
65
@@ -115,6 +126,9 @@
66
     case PROP_ID:
67
       device->id = g_value_get_uint (value);
68
       break;
69
+    case PROP_SERIAL:
70
+      device->serial = g_value_get_uint64 (value);
71
+      break;
72
     default:
73
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
74
       break;
75
@@ -144,6 +158,11 @@
76
       g_param_spec_uint ("id", "Id",
77
           "The internal id of the PipeWire device", 0, G_MAXUINT32, SPA_ID_INVALID,
78
           G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
79
+
80
+  g_object_class_install_property (object_class, PROP_SERIAL,
81
+      g_param_spec_uint64 ("serial", "Serial",
82
+          "The internal serial of the PipeWire device", 0, G_MAXUINT64, SPA_ID_INVALID,
83
+          G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
84
 }
85
 
86
 static void
87
@@ -176,6 +195,7 @@
88
   struct pw_node *proxy;
89
   struct spa_hook proxy_listener;
90
   uint32_t id;
91
+  uint64_t serial;
92
   struct spa_hook node_listener;
93
   struct pw_node_info *info;
94
   GstCaps *caps;
95
@@ -187,6 +207,7 @@
96
   struct pw_port *proxy;
97
   struct spa_hook proxy_listener;
98
   uint32_t id;
99
+  uint64_t serial;
100
   struct spa_hook port_listener;
101
 };
102
 
103
@@ -236,9 +257,10 @@
104
 
105
   gstdev = g_object_new (GST_TYPE_PIPEWIRE_DEVICE,
106
       "display-name", name, "caps", data->caps, "device-class", klass,
107
-      "id", data->id, "properties", props, NULL);
108
+      "id", data->id, "serial", data->serial, "properties", props, NULL);
109
 
110
   gstdev->id = data->id;
111
+  gstdev->serial = data->serial;
112
   gstdev->type = type;
113
   gstdev->element = element;
114
   if (props)
115
@@ -476,6 +498,8 @@
116
     nd->self = self;
117
     nd->proxy = node;
118
     nd->id = id;
119
+    if (!props || !spa_atou64(spa_dict_lookup(props, PW_KEY_OBJECT_SERIAL), &nd->serial, 0))
120
+      nd->serial = SPA_ID_INVALID;
121
     spa_list_append(&rd->nodes, &nd->link);
122
     pw_node_add_listener(node, &nd->node_listener, &node_events, nd);
123
     pw_proxy_add_listener((struct pw_proxy*)node, &nd->proxy_listener, &proxy_node_events, nd);
124
@@ -500,6 +524,8 @@
125
     pd->node_data = nd;
126
     pd->proxy = port;
127
     pd->id = id;
128
+    if (!props || !spa_atou64(spa_dict_lookup(props, PW_KEY_OBJECT_SERIAL), &pd->serial, 0))
129
+      pd->serial = SPA_ID_INVALID;
130
     pw_port_add_listener(port, &pd->port_listener, &port_events, pd);
131
     pw_proxy_add_listener((struct pw_proxy*)port, &pd->proxy_listener, &proxy_port_events, pd);
132
     resync(self);
133
pipewire-0.3.48.tar.gz/src/gst/gstpipewiredeviceprovider.h -> pipewire-0.3.49.tar.gz/src/gst/gstpipewiredeviceprovider.h Changed
9
 
1
@@ -56,6 +56,7 @@
2
 
3
   GstPipeWireDeviceType  type;
4
   uint32_t            id;
5
+  uint64_t            serial;
6
   const gchar        *element;
7
 };
8
 
9
pipewire-0.3.48.tar.gz/src/gst/gstpipewiresink.c -> pipewire-0.3.49.tar.gz/src/gst/gstpipewiresink.c Changed
96
 
1
@@ -60,6 +60,7 @@
2
 {
3
   PROP_0,
4
   PROP_PATH,
5
+  PROP_TARGET_OBJECT,
6
   PROP_CLIENT_NAME,
7
   PROP_STREAM_PROPERTIES,
8
   PROP_MODE,
9
@@ -124,6 +125,7 @@
10
   if (pwsink->properties)
11
     gst_structure_free (pwsink->properties);
12
   g_free (pwsink->path);
13
+  g_free (pwsink->target_object);
14
   g_free (pwsink->client_name);
15
 
16
   G_OBJECT_CLASS (parent_class)->finalize (object);
17
@@ -160,6 +162,16 @@
18
                                                         "The sink path to connect to (NULL = default)",
19
                                                         NULL,
20
                                                         G_PARAM_READWRITE |
21
+                                                        G_PARAM_STATIC_STRINGS |
22
+                                                        G_PARAM_DEPRECATED));
23
+
24
+  g_object_class_install_property (gobject_class,
25
+                                   PROP_TARGET_OBJECT,
26
+                                   g_param_spec_string ("target-object",
27
+                                                        "Target object",
28
+                                                        "The sink name/serial to connect to (NULL = default)",
29
+                                                        NULL,
30
+                                                        G_PARAM_READWRITE |
31
                                                         G_PARAM_STATIC_STRINGS));
32
 
33
   g_object_class_install_property (gobject_class,
34
@@ -339,6 +351,11 @@
35
       pwsink->path = g_value_dup_string (value);
36
       break;
37
 
38
+    case PROP_TARGET_OBJECT:
39
+      g_free (pwsink->target_object);
40
+      pwsink->target_object = g_value_dup_string (value);
41
+      break;
42
+
43
     case PROP_CLIENT_NAME:
44
       g_free (pwsink->client_name);
45
       pwsink->client_name = g_value_dup_string (value);
46
@@ -376,6 +393,10 @@
47
       g_value_set_string (value, pwsink->path);
48
       break;
49
 
50
+    case PROP_TARGET_OBJECT:
51
+      g_value_set_string (value, pwsink->target_object);
52
+      break;
53
+
54
     case PROP_CLIENT_NAME:
55
       g_value_set_string (value, pwsink->client_name);
56
       break;
57
@@ -522,15 +543,37 @@
58
 
59
   if (state == PW_STREAM_STATE_UNCONNECTED) {
60
     enum pw_stream_flags flags = 0;
61
+    uint32_t target_id;
62
 
63
     if (pwsink->mode != GST_PIPEWIRE_SINK_MODE_PROVIDE)
64
       flags |= PW_STREAM_FLAG_AUTOCONNECT;
65
     else
66
       flags |= PW_STREAM_FLAG_DRIVER;
67
 
68
+    target_id = pwsink->path ? (uint32_t)atoi(pwsink->path) : PW_ID_ANY;
69
+
70
+    if (pwsink->target_object) {
71
+      struct spa_dict_item items[2] = {
72
+        SPA_DICT_ITEM_INIT(PW_KEY_TARGET_OBJECT, pwsink->target_object),
73
+        SPA_DICT_ITEM_INIT(PW_KEY_NODE_TARGET, NULL),
74
+      };
75
+      struct spa_dict dict = SPA_DICT_INIT_ARRAY(items);
76
+      uint64_t serial;
77
+
78
+      /* If target.object is a name, set it also to node.target */
79
+      if (spa_atou64(pwsink->target_object, &serial, 0)) {
80
+        dict.n_items = 1;
81
+      } else {
82
+        target_id = PW_ID_ANY;
83
+        items[1].value = pwsink->target_object;
84
+      }
85
+
86
+      pw_stream_update_properties (pwsink->stream, &dict);
87
+    }
88
+
89
     pw_stream_connect (pwsink->stream,
90
                           PW_DIRECTION_OUTPUT,
91
-                          pwsink->path ? (uint32_t)atoi(pwsink->path) : PW_ID_ANY,
92
+                          target_id,
93
                           flags,
94
                           (const struct spa_pod **) possible->pdata,
95
                           possible->len);
96
pipewire-0.3.48.tar.gz/src/gst/gstpipewiresink.h -> pipewire-0.3.49.tar.gz/src/gst/gstpipewiresink.h Changed
9
 
1
@@ -78,6 +78,7 @@
2
 
3
   /*< private >*/
4
   gchar *path;
5
+  gchar *target_object;
6
   gchar *client_name;
7
   int fd;
8
 
9
pipewire-0.3.48.tar.gz/src/gst/gstpipewiresrc.c -> pipewire-0.3.49.tar.gz/src/gst/gstpipewiresrc.c Changed
101
 
1
@@ -67,6 +67,7 @@
2
 {
3
   PROP_0,
4
   PROP_PATH,
5
+  PROP_TARGET_OBJECT,
6
   PROP_CLIENT_NAME,
7
   PROP_STREAM_PROPERTIES,
8
   PROP_ALWAYS_COPY,
9
@@ -116,6 +117,11 @@
10
       pwsrc->path = g_value_dup_string (value);
11
       break;
12
 
13
+    case PROP_TARGET_OBJECT:
14
+      g_free (pwsrc->target_object);
15
+      pwsrc->target_object = g_value_dup_string (value);
16
+      break;
17
+
18
     case PROP_CLIENT_NAME:
19
       g_free (pwsrc->client_name);
20
       pwsrc->client_name = g_value_dup_string (value);
21
@@ -169,6 +175,10 @@
22
       g_value_set_string (value, pwsrc->path);
23
       break;
24
 
25
+    case PROP_TARGET_OBJECT:
26
+      g_value_set_string (value, pwsrc->target_object);
27
+      break;
28
+
29
     case PROP_CLIENT_NAME:
30
       g_value_set_string (value, pwsrc->client_name);
31
       break;
32
@@ -244,6 +254,7 @@
33
   if (pwsrc->clock)
34
     gst_object_unref (pwsrc->clock);
35
   g_free (pwsrc->path);
36
+  g_free (pwsrc->target_object);
37
   g_free (pwsrc->client_name);
38
   g_object_unref(pwsrc->pool);
39
 
40
@@ -274,6 +285,16 @@
41
                                                         "The source path to connect to (NULL = default)",
42
                                                         NULL,
43
                                                         G_PARAM_READWRITE |
44
+                                                        G_PARAM_STATIC_STRINGS |
45
+                                                        G_PARAM_DEPRECATED));
46
+
47
+  g_object_class_install_property (gobject_class,
48
+                                   PROP_TARGET_OBJECT,
49
+                                   g_param_spec_string ("target-object",
50
+                                                        "Target object",
51
+                                                        "The source name/serial to connect to (NULL = default)",
52
+                                                        NULL,
53
+                                                        G_PARAM_READWRITE |
54
                                                         G_PARAM_STATIC_STRINGS));
55
 
56
   g_object_class_install_property (gobject_class,
57
@@ -675,6 +696,7 @@
58
   GPtrArray *possible;
59
   const char *error = NULL;
60
   struct timespec abstime;
61
+  uint32_t target_id;
62
 
63
   /* first see what is possible on our source pad */
64
   thiscaps = gst_pad_query_caps (GST_BASE_SRC_PAD (basesrc), NULL);
65
@@ -727,11 +749,33 @@
66
     }
67
   }
68
 
69
-  GST_DEBUG_OBJECT (basesrc, "connect capture with path %s", pwsrc->path);
70
+  target_id = pwsrc->path ? (uint32_t)atoi(pwsrc->path) : PW_ID_ANY;
71
+
72
+  if (pwsrc->target_object) {
73
+      struct spa_dict_item items[2] = {
74
+        SPA_DICT_ITEM_INIT(PW_KEY_TARGET_OBJECT, pwsrc->target_object),
75
+        SPA_DICT_ITEM_INIT(PW_KEY_NODE_TARGET, NULL),
76
+      };
77
+      struct spa_dict dict = SPA_DICT_INIT_ARRAY(items);
78
+      uint64_t serial;
79
+
80
+      /* If target.object is a name, set it also to node.target */
81
+      if (spa_atou64(pwsrc->target_object, &serial, 0)) {
82
+        dict.n_items = 1;
83
+      } else {
84
+        target_id = PW_ID_ANY;
85
+        items[1].value = pwsrc->target_object;
86
+      }
87
+
88
+      pw_stream_update_properties (pwsrc->stream, &dict);
89
+  }
90
+
91
+  GST_DEBUG_OBJECT (basesrc, "connect capture with path %s, target-object %s",
92
+                    pwsrc->path, pwsrc->target_object);
93
   pwsrc->negotiated = FALSE;
94
   pw_stream_connect (pwsrc->stream,
95
                      PW_DIRECTION_INPUT,
96
-                     pwsrc->path ? (uint32_t)atoi(pwsrc->path) : PW_ID_ANY,
97
+                     target_id,
98
                      PW_STREAM_FLAG_AUTOCONNECT | PW_STREAM_FLAG_DONT_RECONNECT,
99
                      (const struct spa_pod **)possible->pdata,
100
                      possible->len);
101
pipewire-0.3.48.tar.gz/src/gst/gstpipewiresrc.h -> pipewire-0.3.49.tar.gz/src/gst/gstpipewiresrc.h Changed
9
 
1
@@ -60,6 +60,7 @@
2
 
3
   /*< private >*/
4
   gchar *path;
5
+  gchar *target_object;
6
   gchar *client_name;
7
   gboolean always_copy;
8
   gint min_buffers;
9
pipewire-0.3.48.tar.gz/src/modules/meson.build -> pipewire-0.3.49.tar.gz/src/modules/meson.build Changed
29
 
1
@@ -131,8 +131,6 @@
2
   dependencies : [spa_dep, mathlib, dl_lib, pipewire_dep],
3
 )
4
 
5
-build_module_rt = dbus_dep.found()
6
-if build_module_rt
7
 pipewire_module_rt = shared_library('pipewire-module-rt', [ 'module-rt.c' ],
8
   include_directories : [configinc],
9
   install : true,
10
@@ -140,6 +138,9 @@
11
   install_rpath: modules_install_dir,
12
   dependencies : [dbus_dep, mathlib, dl_lib, pipewire_dep],
13
 )
14
+
15
+build_module_rtkit = dbus_dep.found()
16
+if build_module_rtkit
17
 # TODO: This serves as a temporary alias to prevent breaking existing setups
18
 #       while `module-rtkit` is being migrated to `module-rt`
19
 pipewire_module_rtkit = shared_library('pipewire-module-rtkit', [ 'module-rt.c' ],
20
@@ -150,7 +151,7 @@
21
   dependencies : [dbus_dep, mathlib, dl_lib, pipewire_dep],
22
 )
23
 endif
24
-summary({'rt': build_module_rt}, bool_yn: true, section: 'Optional Modules')
25
+summary({'rt': '@0@ RTKit'.format(build_module_rtkit ? 'with' : 'without')}, section: 'Optional Modules')
26
 
27
 build_module_portal = dbus_dep.found()
28
 if build_module_portal
29
pipewire-0.3.48.tar.gz/src/modules/module-client-node/remote-node.c -> pipewire-0.3.49.tar.gz/src/modules/module-client-node/remote-node.c Changed
13
 
1
@@ -843,9 +843,9 @@
2
 {
3
    struct link *link = user_data;
4
    struct spa_system *data_system = link->data->context->data_system;
5
-   struct timespec ts;
6
+   struct timespec ts = { 0, 0 };
7
 
8
-   pw_log_trace("link %p: signal", link);
9
+   pw_log_trace_fp("link %p: signal", link);
10
 
11
    spa_system_clock_gettime(data_system, CLOCK_MONOTONIC, &ts);
12
    link->target.activation->status = PW_NODE_ACTIVATION_TRIGGERED;
13
pipewire-0.3.48.tar.gz/src/modules/module-echo-cancel.c -> pipewire-0.3.49.tar.gz/src/modules/module-echo-cancel.c Changed
74
 
1
@@ -71,7 +71,7 @@
2
  * - `source.props = {}`: properties to be passed to the source stream
3
  * - `sink.props = {}`: properties to be passed to the sink stream
4
  * - `library.name = <str>`: the echo cancellation library  Currently supported:
5
- * `aec/libspa-aec-exaudio`. Leave unset to use the default method (`aec/libspa-aec-exaudio`).
6
+ * `aec/libspa-aec-webrtc`. Leave unset to use the default method (`aec/libspa-aec-webrtc`).
7
  * - `aec.args = <str>`: arguments to pass to the echo cancellation method
8
  *
9
  * ## General options
10
@@ -96,7 +96,7 @@
11
  * context.modules = [
12
  *  {   name = libpipewire-module-echo-cancel
13
  *      args = {
14
- *          # library.name  = aec/libspa-aec-exaudio
15
+ *          # library.name  = aec/libspa-aec-webrtc
16
  *          # node.latency = 1024/48000
17
  *          source.props = {
18
  *             node.name = "Echo Cancellation Source"
19
@@ -139,7 +139,7 @@
20
                "[ audio.channels=<number of channels> ] "
21
                "[ audio.position=<channel map> ] "
22
                "[ buffer.max_size=<max buffer size in ms> ] "
23
-               "[ buffer.play_delay=<play delay in ms> ] "
24
+               "[ buffer.play_delay=<delay as fraction> ] "
25
                "[ library.name =<library name> ] "
26
                "[ aec.args=<aec arguments> ] "
27
                "[ source.props=<properties> ] "
28
@@ -731,7 +731,7 @@
29
        return res;
30
 
31
    impl->rec_ringsize = sizeof(float) * impl->max_buffer_size * impl->info.rate / 1000;
32
-   impl->play_ringsize = sizeof(float) * (impl->max_buffer_size + impl->buffer_delay) * impl->info.rate / 1000;
33
+   impl->play_ringsize = sizeof(float) * ((impl->max_buffer_size * impl->info.rate / 1000) + impl->buffer_delay);
34
    impl->out_ringsize = sizeof(float) * impl->max_buffer_size * impl->info.rate / 1000;
35
    for (i = 0; i < impl->info.channels; i++) {
36
        impl->rec_buffer[i] = malloc(impl->rec_ringsize);
37
@@ -744,9 +744,9 @@
38
    spa_ringbuffer_init(&impl->out_ring);
39
 
40
    spa_ringbuffer_get_write_index(&impl->play_ring, &index);
41
-   spa_ringbuffer_write_update(&impl->play_ring, index + (sizeof(float) * (impl->buffer_delay) * impl->info.rate / 1000));
42
+   spa_ringbuffer_write_update(&impl->play_ring, index + (sizeof(float) * (impl->buffer_delay)));
43
    spa_ringbuffer_get_read_index(&impl->play_ring, &index);
44
-   spa_ringbuffer_read_update(&impl->play_ring, index + (sizeof(float) * (impl->buffer_delay) * impl->info.rate / 1000));
45
+   spa_ringbuffer_read_update(&impl->play_ring, index + (sizeof(float) * (impl->buffer_delay)));
46
 
47
    return 0;
48
 }
49
@@ -1054,7 +1054,23 @@
50
    copy_props(impl, props, PW_KEY_NODE_LATENCY);
51
 
52
    impl->max_buffer_size = pw_properties_get_uint32(props,"buffer.max_size", MAX_BUFSIZE_MS);
53
-   impl->buffer_delay = pw_properties_get_uint32(props,"buffer.play_delay", DELAY_MS);
54
+
55
+   if ((str = pw_properties_get(props, "buffer.play_delay")) != NULL) {
56
+       int req_num, req_denom;
57
+       if (sscanf(str, "%u/%u", &req_num, &req_denom) == 2) {
58
+           if (req_denom != 0) {
59
+               impl->buffer_delay = (impl->info.rate*req_num)/req_denom;
60
+           } else {
61
+               impl->buffer_delay = DELAY_MS * impl->info.rate / 1000;
62
+               pw_log_warn("Sample rate for buffer.play_delay is 0 using default");
63
+           }
64
+       } else {
65
+           impl->buffer_delay = DELAY_MS * impl->info.rate / 1000;
66
+           pw_log_warn("Wrong value/format for buffer.play_delay using default");
67
+       }
68
+   } else {
69
+       impl->buffer_delay = DELAY_MS * impl->info.rate / 1000;
70
+   }
71
 
72
    pw_properties_free(props);
73
 
74
pipewire-0.3.48.tar.gz/src/modules/module-filter-chain.c -> pipewire-0.3.49.tar.gz/src/modules/module-filter-chain.c Changed
104
 
1
@@ -408,14 +408,36 @@
2
            SPA_PROP_INFO_name, SPA_POD_String(name),
3
            0);
4
    spa_pod_builder_prop(b, SPA_PROP_INFO_type, 0);
5
-   if (min == max) {
6
-       spa_pod_builder_float(b, def);
7
+   if (p->hint & FC_HINT_BOOLEAN) {
8
+       if (min == max) {
9
+           spa_pod_builder_bool(b, def <= 0.0 ? false : true);
10
+       } else  {
11
+           spa_pod_builder_push_choice(b, &f[1], SPA_CHOICE_Enum, 0);
12
+           spa_pod_builder_bool(b, def <= 0.0 ? false : true);
13
+           spa_pod_builder_bool(b, false);
14
+           spa_pod_builder_bool(b, true);
15
+           spa_pod_builder_pop(b, &f[1]);
16
+       }
17
+   } else if (p->hint & FC_HINT_INTEGER) {
18
+       if (min == max) {
19
+           spa_pod_builder_int(b, def);
20
+       } else {
21
+           spa_pod_builder_push_choice(b, &f[1], SPA_CHOICE_Range, 0);
22
+           spa_pod_builder_int(b, def);
23
+           spa_pod_builder_int(b, min);
24
+           spa_pod_builder_int(b, max);
25
+           spa_pod_builder_pop(b, &f[1]);
26
+       }
27
    } else {
28
-       spa_pod_builder_push_choice(b, &f[1], SPA_CHOICE_Range, 0);
29
-       spa_pod_builder_float(b, def);
30
-       spa_pod_builder_float(b, min);
31
-       spa_pod_builder_float(b, max);
32
-       spa_pod_builder_pop(b, &f[1]);
33
+       if (min == max) {
34
+           spa_pod_builder_float(b, def);
35
+       } else {
36
+           spa_pod_builder_push_choice(b, &f[1], SPA_CHOICE_Range, 0);
37
+           spa_pod_builder_float(b, def);
38
+           spa_pod_builder_float(b, min);
39
+           spa_pod_builder_float(b, max);
40
+           spa_pod_builder_pop(b, &f[1]);
41
+       }
42
    }
43
    spa_pod_builder_prop(b, SPA_PROP_INFO_params, 0);
44
    spa_pod_builder_bool(b, true);
45
@@ -446,7 +468,13 @@
46
            snprintf(name, sizeof(name), "%s", p->name);
47
 
48
        spa_pod_builder_string(b, name);
49
-       spa_pod_builder_float(b, port->control_data);
50
+       if (p->hint & FC_HINT_BOOLEAN) {
51
+           spa_pod_builder_bool(b, port->control_data <= 0.0 ? false : true);
52
+       } else if (p->hint & FC_HINT_INTEGER) {
53
+           spa_pod_builder_int(b, port->control_data);
54
+       } else {
55
+           spa_pod_builder_float(b, port->control_data);
56
+       }
57
    }
58
    spa_pod_builder_pop(b, &f[1]);
59
    return spa_pod_builder_pop(b, &f[0]);
60
@@ -487,12 +515,23 @@
61
    while (true) {
62
        const char *name;
63
        float value, *val = NULL;
64
+       bool bool_val;
65
+       int32_t int_val;
66
 
67
        if (spa_pod_parser_get_string(&prs, &name) < 0)
68
            break;
69
-       if (spa_pod_parser_get_float(&prs, &value) >= 0)
70
+       if (spa_pod_parser_get_float(&prs, &value) >= 0) {
71
            val = &value;
72
-
73
+       } else if (spa_pod_parser_get_int(&prs, &int_val) >= 0) {
74
+           value = int_val;
75
+           val = &value;
76
+       } else if (spa_pod_parser_get_bool(&prs, &bool_val) >= 0) {
77
+           value = bool_val ? 1.0f : 0.0f;
78
+           val = &value;
79
+       } else {
80
+           struct spa_pod *pod;
81
+           spa_pod_parser_get_pod(&prs, &pod);
82
+       }
83
        changed += set_control_value(def_node, name, val);
84
    }
85
    return changed;
86
@@ -524,13 +563,14 @@
87
    }
88
    if (changed > 0) {
89
        uint8_t buffer[1024];
90
-       struct spa_pod_builder b;
91
+       struct spa_pod_dynamic_builder b;
92
        const struct spa_pod *params[1];
93
 
94
-       spa_pod_builder_init(&b, buffer, sizeof(buffer));
95
-       params[0] = get_props_param(graph, &b);
96
+       spa_pod_dynamic_builder_init(&b, buffer, sizeof(buffer), 4096);
97
+       params[0] = get_props_param(graph, &b.b);
98
 
99
        pw_stream_update_params(impl->capture, params, 1);
100
+       spa_pod_dynamic_builder_clean(&b);
101
    }
102
 }
103
 
104
pipewire-0.3.48.tar.gz/src/modules/module-filter-chain/plugin.h -> pipewire-0.3.49.tar.gz/src/modules/module-filter-chain/plugin.h Changed
11
 
1
@@ -47,7 +47,9 @@
2
 #define FC_PORT_AUDIO  (1ULL << 3)
3
    uint64_t flags;
4
 
5
+#define FC_HINT_BOOLEAN        (1ULL << 2)
6
 #define FC_HINT_SAMPLE_RATE    (1ULL << 3)
7
+#define FC_HINT_INTEGER        (1ULL << 5)
8
    uint64_t hint;
9
    float def;
10
    float min;
11
pipewire-0.3.48.tar.gz/src/modules/module-protocol-pulse/extensions/ext-stream-restore.c -> pipewire-0.3.49.tar.gz/src/modules/module-protocol-pulse/extensions/ext-stream-restore.c Changed
20
 
1
@@ -235,7 +235,7 @@
2
        FILE *f;
3
        char *ptr;
4
        size_t size;
5
-       char key[1024];
6
+       char key[1024], buf[128];
7
 
8
        spa_zero(map);
9
        spa_zero(vol);
10
@@ -260,7 +260,8 @@
11
        if (vol.channels > 0) {
12
            fprintf(f, ", \"volumes\": [");
13
            for (i = 0; i < vol.channels; i++)
14
-               fprintf(f, "%s%f", (i == 0 ? " ":", "), vol.values[i]);
15
+               fprintf(f, "%s%s", (i == 0 ? " ":", "),
16
+                       spa_json_format_float(buf, sizeof(buf), vol.values[i]));
17
            fprintf(f, " ]");
18
        }
19
        if (map.channels > 0) {
20
pipewire-0.3.48.tar.gz/src/modules/module-protocol-pulse/manager.c -> pipewire-0.3.49.tar.gz/src/modules/module-protocol-pulse/manager.c Changed
10
 
1
@@ -812,6 +812,8 @@
2
    struct manager *m = SPA_CONTAINER_OF(manager, struct manager, this);
3
    struct object *o;
4
 
5
+   spa_hook_list_clean(&m->hooks);
6
+
7
    spa_hook_remove(&m->core_listener);
8
 
9
    spa_list_consume(o, &m->this.object_list, this.link)
10
pipewire-0.3.48.tar.gz/src/modules/module-protocol-pulse/module.c -> pipewire-0.3.49.tar.gz/src/modules/module-protocol-pulse/module.c Changed
21
 
1
@@ -67,6 +67,7 @@
2
    if (module == NULL)
3
        return NULL;
4
 
5
+   module->index = SPA_ID_INVALID;
6
    module->impl = impl;
7
    module->methods = methods;
8
    spa_hook_list_init(&module->listener_list);
9
@@ -102,8 +103,10 @@
10
    if (module->index != SPA_ID_INVALID)
11
        pw_map_remove(&impl->modules, module->index & MODULE_INDEX_MASK);
12
 
13
+   if (module->unloading)
14
+       pw_work_queue_cancel(impl->work_queue, module, SPA_ID_INVALID);
15
+
16
    spa_hook_list_clean(&module->listener_list);
17
-   pw_work_queue_cancel(impl->work_queue, module, SPA_ID_INVALID);
18
    pw_properties_free(module->props);
19
 
20
    free((char*)module->name);
21
pipewire-0.3.48.tar.gz/src/modules/module-protocol-pulse/modules/module-combine-sink.c -> pipewire-0.3.49.tar.gz/src/modules/module-protocol-pulse/modules/module-combine-sink.c Changed
11
 
1
@@ -166,8 +166,7 @@
2
        pw_stream_queue_buffer(data->streams[i].stream, out);
3
    }
4
 
5
-   if (in != NULL)
6
-       pw_stream_queue_buffer(data->sink, in);
7
+   pw_stream_queue_buffer(data->sink, in);
8
 }
9
 
10
 static void check_initialized(struct module_combine_sink_data *data)
11
pipewire-0.3.48.tar.gz/src/modules/module-protocol-pulse/modules/module-roc-sink.c -> pipewire-0.3.49.tar.gz/src/modules/module-protocol-pulse/modules/module-roc-sink.c Changed
29
 
1
@@ -78,7 +78,7 @@
2
 
3
    fprintf(f, "{");
4
    pw_properties_serialize_dict(f, &data->roc_props->dict, 0);
5
-   fprintf(f, " } sink.props = {");
6
+   fprintf(f, " sink.props = {");
7
    pw_properties_serialize_dict(f, &data->sink_props->dict, 0);
8
    fprintf(f, " } }");
9
    fclose(f);
10
@@ -125,6 +125,7 @@
11
    { PW_KEY_MODULE_AUTHOR, "Sanchayan Maity <sanchayan@asymptotic.io>" },
12
    { PW_KEY_MODULE_DESCRIPTION, "roc sink" },
13
    { PW_KEY_MODULE_USAGE, "sink_name=<name for the sink> "
14
+               "sink_properties=<properties for the sink> "
15
                "local_ip=<local sender ip> "
16
                "remote_ip=<remote receiver ip> "
17
                "remote_source_port=<remote receiver port for source packets> "
18
@@ -156,6 +157,10 @@
19
        pw_properties_set(sink_props, PW_KEY_NODE_NAME, str);
20
        pw_properties_set(props, "sink_name", NULL);
21
    }
22
+   if ((str = pw_properties_get(props, "sink_properties")) != NULL) {
23
+       module_args_add_props(sink_props, str);
24
+       pw_properties_set(props, "sink_properties", NULL);
25
+   }
26
 
27
    if ((str = pw_properties_get(props, PW_KEY_MEDIA_CLASS)) == NULL) {
28
        pw_properties_set(props, PW_KEY_MEDIA_CLASS, "Audio/Sink");
29
pipewire-0.3.48.tar.gz/src/modules/module-protocol-pulse/modules/module-roc-source.c -> pipewire-0.3.49.tar.gz/src/modules/module-protocol-pulse/modules/module-roc-source.c Changed
29
 
1
@@ -78,7 +78,7 @@
2
 
3
    fprintf(f, "{");
4
    pw_properties_serialize_dict(f, &data->roc_props->dict, 0);
5
-   fprintf(f, " } source.props = {");
6
+   fprintf(f, " source.props = {");
7
    pw_properties_serialize_dict(f, &data->source_props->dict, 0);
8
    fprintf(f, " } }");
9
    fclose(f);
10
@@ -125,6 +125,7 @@
11
    { PW_KEY_MODULE_AUTHOR, "Sanchayan Maity <sanchayan@asymptotic.io>" },
12
    { PW_KEY_MODULE_DESCRIPTION, "roc source" },
13
    { PW_KEY_MODULE_USAGE, "source_name=<name for the source> "
14
+               "source_properties=<properties for the source> "
15
                "resampler_profile=<empty>|disable|high|medium|low "
16
                "sess_latency_msec=<target network latency in milliseconds> "
17
                "local_ip=<local receiver ip> "
18
@@ -158,6 +159,10 @@
19
        pw_properties_set(source_props, PW_KEY_NODE_NAME, str);
20
        pw_properties_set(props, "source_name", NULL);
21
    }
22
+   if ((str = pw_properties_get(props, "source_properties")) != NULL) {
23
+       module_args_add_props(source_props, str);
24
+       pw_properties_set(props, "source_properties", NULL);
25
+   }
26
 
27
    if ((str = pw_properties_get(props, PW_KEY_MEDIA_CLASS)) == NULL) {
28
        pw_properties_set(props, PW_KEY_MEDIA_CLASS, "Audio/Source");
29
pipewire-0.3.48.tar.gz/src/modules/module-protocol-pulse/pulse-server.c -> pipewire-0.3.49.tar.gz/src/modules/module-protocol-pulse/pulse-server.c Changed
19
 
1
@@ -1822,7 +1822,7 @@
2
                n_valid_formats++;
3
            }
4
        }
5
-       if (n_params < MAX_FORMATS &&
6
+       else if (n_params < MAX_FORMATS &&
7
            (params[n_params] = format_build_param(&b,
8
                SPA_PARAM_EnumFormat, &ss,
9
                ss.channels > 0 ? &map : NULL)) != NULL) {
10
@@ -5350,6 +5350,8 @@
11
    support = pw_context_get_support(context, &n_support);
12
    cpu = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_CPU);
13
 
14
+   pw_context_conf_update_props(context, "pulse.properties", props);
15
+
16
    if ((str = pw_properties_get(props, "vm.overrides")) != NULL) {
17
        if (cpu != NULL && spa_cpu_get_vm_type(cpu) != SPA_CPU_VM_NONE)
18
            pw_properties_update_string(props, str, strlen(str));
19
pipewire-0.3.48.tar.gz/src/modules/module-protocol-pulse/sample-play.c -> pipewire-0.3.49.tar.gz/src/modules/module-protocol-pulse/sample-play.c Changed
10
 
1
@@ -211,6 +211,8 @@
2
    if (p->stream)
3
        pw_stream_destroy(p->stream);
4
 
5
+   spa_hook_list_clean(&p->hooks);
6
+
7
    free(p);
8
 }
9
 
10
pipewire-0.3.48.tar.gz/src/modules/module-rt.c -> pipewire-0.3.49.tar.gz/src/modules/module-rt.c Changed
568
 
1
@@ -1,6 +1,6 @@
2
 /* PipeWire
3
  *
4
- * Copyright © 2018 Wim Taymans
5
+ * Copyright © 2022 Wim Taymans
6
  *
7
  * Permission is hereby granted, free of charge, to any person obtaining a
8
  * copy of this software and associated documentation files (the "Software"),
9
@@ -21,6 +21,30 @@
10
  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
11
  * DEALINGS IN THE SOFTWARE.
12
  */
13
+/***
14
+  Copyright 2009 Lennart Poettering
15
+  Copyright 2010 David Henningsson <diwic@ubuntu.com>
16
+
17
+  Permission is hereby granted, free of charge, to any person
18
+  obtaining a copy of this software and associated documentation files
19
+  (the "Software"), to deal in the Software without restriction,
20
+  including without limitation the rights to use, copy, modify, merge,
21
+  publish, distribute, sublicense, and/or sell copies of the Software,
22
+  and to permit persons to whom the Software is furnished to do so,
23
+  subject to the following conditions:
24
+
25
+  The above copyright notice and this permission notice shall be
26
+  included in all copies or substantial portions of the Software.
27
+
28
+  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
29
+  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
30
+  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
31
+  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
32
+  BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
33
+  ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
34
+  CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
35
+  SOFTWARE.
36
+***/
37
 
38
 #include <stdlib.h>
39
 #include <stdbool.h>
40
@@ -35,16 +59,21 @@
41
 #include <unistd.h>
42
 #include <pthread.h>
43
 #include <sys/resource.h>
44
+#include <sys/syscall.h>
45
 
46
 #include "config.h"
47
 
48
-#include <spa/support/dbus.h>
49
 #include <spa/utils/result.h>
50
 #include <spa/utils/string.h>
51
 
52
 #include <pipewire/impl.h>
53
 #include <pipewire/thread.h>
54
 
55
+#ifdef HAVE_DBUS
56
+#include <spa/support/dbus.h>
57
+#include <dbus/dbus.h>
58
+#endif
59
+
60
 /** \page page_module_rt PipeWire Module: RT
61
  *
62
  * The `rt` module uses the operating system's scheduler to enable realtime
63
@@ -87,7 +116,15 @@
64
    { PW_KEY_MODULE_VERSION, PACKAGE_VERSION },
65
 };
66
 
67
-struct pw_rtkit_bus;
68
+#ifdef HAVE_DBUS
69
+#define RTKIT_SERVICE_NAME "org.freedesktop.RealtimeKit1"
70
+#define RTKIT_OBJECT_PATH "/org/freedesktop/RealtimeKit1"
71
+
72
+/** \cond */
73
+struct pw_rtkit_bus {
74
+   DBusConnection *bus;
75
+};
76
+/** \endcond */
77
 
78
 struct thread {
79
    struct impl *impl;
80
@@ -97,6 +134,7 @@
81
    void *(*start)(void*);
82
    void *arg;
83
 };
84
+#endif /* HAVE_DBUS */
85
 
86
 struct impl {
87
    struct pw_context *context;
88
@@ -110,6 +148,7 @@
89
 
90
    struct spa_hook module_listener;
91
 
92
+#ifdef HAVE_DBUS
93
    bool use_rtkit;
94
    struct pw_rtkit_bus *system_bus;
95
 
96
@@ -119,52 +158,29 @@
97
    pthread_mutex_t lock;
98
    pthread_cond_t cond;
99
    struct spa_list threads_list;
100
+#endif
101
 };
102
 
103
-/***
104
-  Copyright 2009 Lennart Poettering
105
-  Copyright 2010 David Henningsson <diwic@ubuntu.com>
106
-
107
-  Permission is hereby granted, free of charge, to any person
108
-  obtaining a copy of this software and associated documentation files
109
-  (the "Software"), to deal in the Software without restriction,
110
-  including without limitation the rights to use, copy, modify, merge,
111
-  publish, distribute, sublicense, and/or sell copies of the Software,
112
-  and to permit persons to whom the Software is furnished to do so,
113
-  subject to the following conditions:
114
-
115
-  The above copyright notice and this permission notice shall be
116
-  included in all copies or substantial portions of the Software.
117
-
118
-  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
119
-  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
120
-  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
121
-  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
122
-  BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
123
-  ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
124
-  CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
125
-  SOFTWARE.
126
-***/
127
-
128
-#include <dbus/dbus.h>
129
-
130
-#include "config.h"
131
-
132
-#include <sys/syscall.h>
133
-
134
-#define RTKIT_SERVICE_NAME "org.freedesktop.RealtimeKit1"
135
-#define RTKIT_OBJECT_PATH "/org/freedesktop/RealtimeKit1"
136
-
137
 #ifndef RLIMIT_RTTIME
138
 #define RLIMIT_RTTIME 15
139
 #endif
140
 
141
-/** \cond */
142
-struct pw_rtkit_bus {
143
-   DBusConnection *bus;
144
-};
145
-/** \endcond */
146
+static pid_t _gettid(void)
147
+{
148
+#if defined(HAVE_GETTID)
149
+   return (pid_t) gettid();
150
+#elif defined(__linux__)
151
+   return syscall(SYS_gettid);
152
+#elif defined(__FreeBSD__)
153
+   long pid;
154
+   thr_self(&pid);
155
+   return (pid_t)pid;
156
+#else
157
+#error "No gettid impl"
158
+#endif
159
+}
160
 
161
+#ifdef HAVE_DBUS
162
 struct pw_rtkit_bus *pw_rtkit_bus_get_system(void)
163
 {
164
    struct pw_rtkit_bus *bus;
165
@@ -204,21 +220,6 @@
166
    free(system_bus);
167
 }
168
 
169
-static pid_t _gettid(void)
170
-{
171
-#if defined(HAVE_GETTID)
172
-   return (pid_t) gettid();
173
-#elif defined(__linux__)
174
-   return syscall(SYS_gettid);
175
-#elif defined(__FreeBSD__)
176
-   long pid;
177
-   thr_self(&pid);
178
-   return (pid_t)pid;
179
-#else
180
-#error "No gettid impl"
181
-#endif
182
-}
183
-
184
 static int translate_error(const char *name)
185
 {
186
    pw_log_warn("RTKit error: %s", name);
187
@@ -458,6 +459,7 @@
188
 
189
    return ret;
190
 }
191
+#endif /* HAVE_DBUS */
192
 
193
 static void module_destroy(void *data)
194
 {
195
@@ -466,8 +468,11 @@
196
    pw_thread_utils_set(NULL);
197
    spa_hook_remove(&impl->module_listener);
198
 
199
+#ifdef HAVE_DBUS
200
    if (impl->system_bus)
201
        pw_rtkit_bus_free(impl->system_bus);
202
+#endif
203
+
204
    free(impl);
205
 }
206
 
207
@@ -480,7 +485,7 @@
208
  * Check if the current user has permissions to use realtime scheduling at the
209
  * specified priority.
210
  */
211
-static bool check_realtime_priviliges(rlim_t priority)
212
+static bool check_realtime_privileges(rlim_t priority)
213
 {
214
    int old_policy;
215
    struct sched_param old_sched_params;
216
@@ -516,28 +521,33 @@
217
    }
218
 }
219
 
220
+static int sched_set_nice(int nice_level)
221
+{
222
+   if (setpriority(PRIO_PROCESS, _gettid(), nice_level) == 0)
223
+       return 0;
224
+   else
225
+       return -errno;
226
+}
227
+
228
 static int set_nice(struct impl *impl, int nice_level)
229
 {
230
    int res = 0;
231
-   pid_t tid;
232
 
233
-   if (impl->use_rtkit) {
234
-       if ((res = pw_rtkit_make_high_priority(impl->system_bus, 0, nice_level)) < 0) {
235
-           pw_log_warn("could not set nice-level to %d: %s",
236
-                   nice_level, spa_strerror(res));
237
-       } else {
238
-           pw_log_info("main thread nice level set to %d", nice_level);
239
-       }
240
+#ifdef HAVE_DBUS
241
+   if (impl->use_rtkit)
242
+       res = pw_rtkit_make_high_priority(impl->system_bus, 0, nice_level);
243
+   else
244
+       res = sched_set_nice(nice_level);
245
+#else
246
+   res = sched_set_nice(nice_level);
247
+#endif
248
+
249
+   if (res < 0) {
250
+       pw_log_warn("could not set nice-level to %d: %s",
251
+               nice_level, spa_strerror(res));
252
    } else {
253
-       tid = _gettid();
254
-       if (setpriority(PRIO_PROCESS, tid, nice_level) == 0) {
255
-           pw_log_info("main thread nice level set to %d",
256
-                   nice_level);
257
-       } else {
258
-           res = -errno;
259
-           pw_log_warn("could not set nice-level to %d: %s",
260
-                   nice_level, spa_strerror(res));
261
-       }
262
+       pw_log_info("main thread nice level set to %d",
263
+               nice_level);
264
    }
265
 
266
    return res;
267
@@ -546,14 +556,15 @@
268
 static int set_rlimit(struct impl *impl)
269
 {
270
    struct rlimit rl;
271
-   long long rttime;
272
    int res = 0;
273
 
274
    spa_zero(rl);
275
    rl.rlim_cur = impl->rt_time_soft;
276
    rl.rlim_max = impl->rt_time_hard;
277
 
278
+#ifdef HAVE_DBUS
279
    if (impl->use_rtkit) {
280
+       long long rttime;
281
        rttime = pw_rtkit_get_rttime_usec_max(impl->system_bus);
282
        if (rttime >= 0) {
283
            if ((rlim_t)rttime < rl.rlim_cur) {
284
@@ -565,6 +576,7 @@
285
            rl.rlim_max = SPA_MIN(rl.rlim_max, (rlim_t)rttime);
286
        }
287
    }
288
+#endif
289
 
290
    if (setrlimit(RLIMIT_RTTIME, &rl) < 0)
291
        res = -errno;
292
@@ -578,6 +590,46 @@
293
    return res;
294
 }
295
 
296
+static int impl_acquire_rt_sched(struct spa_thread *thread, int priority)
297
+{
298
+   int err;
299
+   struct sched_param sp;
300
+   pthread_t pt = (pthread_t)thread;
301
+
302
+   if (priority < sched_get_priority_min(REALTIME_POLICY) ||
303
+       priority > sched_get_priority_max(REALTIME_POLICY)) {
304
+       pw_log_warn("invalid priority %d for policy %d", priority, REALTIME_POLICY);
305
+       return -EINVAL;
306
+   }
307
+
308
+   spa_zero(sp);
309
+   sp.sched_priority = priority;
310
+   if ((err = pthread_setschedparam(pt, REALTIME_POLICY | PW_SCHED_RESET_ON_FORK, &sp)) != 0) {
311
+       pw_log_warn("could not make thread %p realtime: %s", thread, strerror(err));
312
+       return -err;
313
+   }
314
+
315
+   pw_log_info("acquired realtime priority %d for thread %p", priority, thread);
316
+   return 0;
317
+}
318
+
319
+static int impl_drop_rt_generic(void *data, struct spa_thread *thread)
320
+{
321
+   struct sched_param sp;
322
+   pthread_t pt = (pthread_t)thread;
323
+   int err;
324
+
325
+   spa_zero(sp);
326
+   if ((err = pthread_setschedparam(pt, SCHED_OTHER | PW_SCHED_RESET_ON_FORK, &sp)) != 0) {
327
+       pw_log_debug("thread %p: SCHED_OTHER|SCHED_RESET_ON_FORK failed: %s",
328
+               thread, strerror(err));
329
+       return -err;
330
+   }
331
+   pw_log_info("thread %p dropped realtime priority", thread);
332
+   return 0;
333
+}
334
+
335
+#ifdef HAVE_DBUS
336
 static struct thread *find_thread_by_pt(struct impl *impl, pthread_t pt)
337
 {
338
    struct thread *t;
339
@@ -714,42 +766,66 @@
340
 
341
        if ((err = pw_rtkit_make_realtime(impl->system_bus, pid, priority)) < 0) {
342
            pw_log_warn("could not make thread %d realtime using RTKit: %s", pid, spa_strerror(err));
343
-       } else {
344
-           pw_log_info("acquired realtime priority %d for thread %d using RTKit", priority, pid);
345
+           return err;
346
        }
347
+
348
+       pw_log_info("acquired realtime priority %d for thread %d using RTKit", priority, pid);
349
+       return 0;
350
    } else {
351
-       if (priority < sched_get_priority_min(REALTIME_POLICY) ||
352
-           priority > sched_get_priority_max(REALTIME_POLICY)) {
353
-           pw_log_warn("invalid priority %d for policy %d", priority, REALTIME_POLICY);
354
-           return -EINVAL;
355
-       }
356
+       return impl_acquire_rt_sched(thread, priority);
357
+   }
358
+}
359
 
360
-       spa_zero(sp);
361
-       sp.sched_priority = priority;
362
-       if ((err = pthread_setschedparam(pt, REALTIME_POLICY | PW_SCHED_RESET_ON_FORK, &sp)) != 0) {
363
-           pw_log_warn("could not make thread %p realtime: %s", thread, strerror(err));
364
-           return -err;
365
-       }
366
-       pw_log_info("acquired realtime priority %d for thread %p", priority, thread);
367
+static const struct spa_thread_utils_methods impl_thread_utils = {
368
+   SPA_VERSION_THREAD_UTILS_METHODS,
369
+   .create = impl_create,
370
+   .join = impl_join,
371
+   .get_rt_range = impl_get_rt_range,
372
+   .acquire_rt = impl_acquire_rt,
373
+   .drop_rt = impl_drop_rt_generic,
374
+};
375
+
376
+#else /* HAVE_DBUS */
377
+
378
+static struct spa_thread *impl_create(void *data, const struct spa_dict *props,
379
+       void *(*start_routine)(void*), void *arg)
380
+{
381
+   pthread_t pt;
382
+   int err;
383
+
384
+   err = pthread_create(&pt, NULL, start_routine, arg);
385
+   if (err != 0) {
386
+       errno = err;
387
+       return NULL;
388
    }
389
+   return (struct spa_thread*)pt;
390
+}
391
+
392
+static int impl_join(void *data, struct spa_thread *thread, void **retval)
393
+{
394
+   return pthread_join((pthread_t)thread, retval);
395
+}
396
 
397
+static int impl_get_rt_range(void *data, const struct spa_dict *props,
398
+       int *min, int *max)
399
+{
400
+   if (min)
401
+       *min = sched_get_priority_min(REALTIME_POLICY);
402
+   if (max)
403
+       *max = sched_get_priority_max(REALTIME_POLICY);
404
    return 0;
405
 }
406
 
407
-static int impl_drop_rt(void *data, struct spa_thread *thread)
408
+static int impl_acquire_rt(void *data, struct spa_thread *thread, int priority)
409
 {
410
-   struct sched_param sp;
411
-   pthread_t pt = (pthread_t)thread;
412
-   int err;
413
+   struct impl *impl = data;
414
 
415
-   spa_zero(sp);
416
-   if ((err = pthread_setschedparam(pt, SCHED_OTHER | PW_SCHED_RESET_ON_FORK, &sp)) != 0) {
417
-       pw_log_debug("thread %p: SCHED_OTHER|SCHED_RESET_ON_FORK failed: %s",
418
-               thread, strerror(err));
419
-       return -err;
420
+   /* See the docstring on `spa_thread_utils_methods::acquire_rt` */
421
+   if (priority == -1) {
422
+       priority = impl->rt_prio;
423
    }
424
-   pw_log_info("thread %p dropped realtime priority", thread);
425
-   return 0;
426
+
427
+   return impl_acquire_rt_sched(thread, priority);
428
 }
429
 
430
 static const struct spa_thread_utils_methods impl_thread_utils = {
431
@@ -758,36 +834,56 @@
432
    .join = impl_join,
433
    .get_rt_range = impl_get_rt_range,
434
    .acquire_rt = impl_acquire_rt,
435
-   .drop_rt = impl_drop_rt,
436
+   .drop_rt = impl_drop_rt_generic,
437
 };
438
+#endif /* HAVE_DBUS */
439
+
440
+
441
+#ifdef HAVE_DBUS
442
+static int should_use_rtkit(struct impl *impl, struct pw_context *context, bool *use_rtkit)
443
+{
444
+   const struct pw_properties *context_props;
445
+   const char *str;
446
+
447
+   *use_rtkit = true;
448
 
449
+   if ((context_props = pw_context_get_properties(context)) != NULL &&
450
+       (str = pw_properties_get(context_props, "support.dbus")) != NULL &&
451
+       !pw_properties_parse_bool(str))
452
+       *use_rtkit = false;
453
+
454
+   /* If the user has permissions to use regular realtime scheduling, then
455
+    * we'll use that instead of RTKit */
456
+   if (check_realtime_privileges(impl->rt_prio)) {
457
+       *use_rtkit = false;
458
+   } else {
459
+       if (!(*use_rtkit)) {
460
+           pw_log_warn("neither regular realtime scheduling nor RTKit are available");
461
+           return -ENOTSUP;
462
+       }
463
+
464
+       /* TODO: Should this be pw_log_warn or pw_log_debug instead? */
465
+       pw_log_info("could not use realtime scheduling, falling back to using RTKit instead");
466
+   }
467
+
468
+   return 0;
469
+}
470
+#endif /* HAVE_DBUS */
471
 
472
 SPA_EXPORT
473
 int pipewire__module_init(struct pw_impl_module *module, const char *args)
474
 {
475
    struct pw_context *context = pw_impl_module_get_context(module);
476
    struct impl *impl;
477
-   const struct pw_properties *context_props;
478
    struct pw_properties *props;
479
-   const char *str;
480
-   bool use_rtkit = true;
481
    int res = 0;
482
 
483
    PW_LOG_TOPIC_INIT(mod_topic);
484
 
485
-   if ((context_props = pw_context_get_properties(context)) != NULL &&
486
-       (str = pw_properties_get(context_props, "support.dbus")) != NULL &&
487
-       !pw_properties_parse_bool(str))
488
-       use_rtkit = false;
489
-
490
    impl = calloc(1, sizeof(struct impl));
491
    if (impl == NULL)
492
        return -ENOMEM;
493
 
494
-   spa_list_init(&impl->threads_list);
495
-   pthread_mutex_init(&impl->lock, NULL);
496
-   pthread_cond_init(&impl->cond, NULL);
497
-
498
    pw_log_debug("module %p: new", impl);
499
 
500
    props = args ? pw_properties_new_string(args) : pw_properties_new(NULL, NULL);
501
@@ -802,22 +898,15 @@
502
    impl->rt_time_soft = pw_properties_get_int32(props, "rt.time.soft", DEFAULT_RT_TIME_SOFT);
503
    impl->rt_time_hard = pw_properties_get_int32(props, "rt.time.hard", DEFAULT_RT_TIME_HARD);
504
 
505
-   /* If the user has permissions to use regular realtime scheduling, then
506
-    * we'll use that instead of RTKit */
507
-   if (check_realtime_priviliges(impl->rt_prio)) {
508
-       use_rtkit = false;
509
-   } else {
510
-       if (!use_rtkit) {
511
-           res = -ENOTSUP;
512
-           pw_log_warn("neither regular realtime scheduling nor RTKit are available");
513
-           goto error;
514
-       }
515
+#ifdef HAVE_DBUS
516
+   spa_list_init(&impl->threads_list);
517
+   pthread_mutex_init(&impl->lock, NULL);
518
+   pthread_cond_init(&impl->cond, NULL);
519
 
520
-       /* TODO: Should this be pw_log_warn or pw_log_debug instead? */
521
-       pw_log_info("could not use realtime scheduling, falling back to using RTKit instead");
522
+   if ((res = should_use_rtkit(impl, context, &impl->use_rtkit)) < 0) {
523
+       goto error;
524
    }
525
 
526
-   impl->use_rtkit = use_rtkit;
527
    if (impl->use_rtkit) {
528
        impl->system_bus = pw_rtkit_bus_get_system();
529
        if (impl->system_bus == NULL) {
530
@@ -826,6 +915,13 @@
531
            goto error;
532
        }
533
    }
534
+#else
535
+   if (!check_realtime_privileges(impl->rt_prio)) {
536
+       res = -ENOTSUP;
537
+       pw_log_warn("regular realtime scheduling not available (RTKit fallback disabled)");
538
+       goto error;
539
+   }
540
+#endif
541
 
542
    if (IS_VALID_NICE_LEVEL(impl->nice_level))
543
        set_nice(impl, impl->nice_level);
544
@@ -842,16 +938,23 @@
545
    pw_impl_module_update_properties(module, &SPA_DICT_INIT_ARRAY(module_props));
546
    pw_impl_module_update_properties(module, &props->dict);
547
 
548
+#ifdef HAVE_DBUS
549
    if (impl->use_rtkit) {
550
        pw_log_debug("initialized using RTKit");
551
    } else {
552
        pw_log_debug("initialized using regular realtime scheduling");
553
    }
554
+#else
555
+   pw_log_debug("initialized using regular realtime scheduling");
556
+#endif
557
+
558
    goto done;
559
 
560
 error:
561
+#ifdef HAVE_DBUS
562
    if (impl->system_bus)
563
        pw_rtkit_bus_free(impl->system_bus);
564
+#endif
565
    free(impl);
566
 done:
567
    pw_properties_free(props);
568
pipewire-0.3.48.tar.gz/src/modules/module-zeroconf-discover/avahi-poll.c -> pipewire-0.3.49.tar.gz/src/modules/module-zeroconf-discover/avahi-poll.c Changed
39
 
1
@@ -37,6 +37,7 @@
2
    AvahiWatchEvent events;
3
    AvahiWatchCallback callback;
4
    void *userdata;
5
+   unsigned int dispatching;
6
 };
7
 
8
 struct AvahiTimeout {
9
@@ -65,9 +66,14 @@
10
 {
11
    AvahiWatch *w = data;
12
 
13
+   w->dispatching += 1;
14
+
15
    w->events = from_pw_events(mask);
16
    w->callback(w, fd, w->events, w->userdata);
17
    w->events = 0;
18
+
19
+   if (--w->dispatching == 0 && !w->source)
20
+       free(w);
21
 }
22
 
23
 static AvahiWatch* watch_new(const AvahiPoll *api, int fd, AvahiWatchEvent event,
24
@@ -103,9 +109,11 @@
25
 
26
 static void watch_free(AvahiWatch *w)
27
 {
28
-   struct impl *impl = w->impl;
29
-   pw_loop_destroy_source(impl->loop, w->source);
30
-   free(w);
31
+   pw_loop_destroy_source(w->impl->loop, w->source);
32
+   w->source = NULL;
33
+
34
+   if (!w->dispatching)
35
+       free(w);
36
 }
37
 
38
 static void timeout_callback(void *data, uint64_t expirations)
39
pipewire-0.3.48.tar.gz/src/pipewire/filter.c -> pipewire-0.3.49.tar.gz/src/pipewire/filter.c Changed
10
 
1
@@ -642,7 +642,7 @@
2
        clear_params(impl, port, id);
3
    } else {
4
        for (i = 0; i < n_params; i++) {
5
-           if (!spa_pod_is_object(params[i]))
6
+           if (params[i] == NULL || !spa_pod_is_object(params[i]))
7
                continue;
8
            clear_params(impl, port, SPA_POD_OBJECT_ID(params[i]));
9
        }
10
pipewire-0.3.48.tar.gz/src/pipewire/pipewire.c -> pipewire-0.3.49.tar.gz/src/pipewire/pipewire.c Changed
65
 
1
@@ -89,7 +89,7 @@
2
    struct spa_interface i18n_iface;
3
    struct spa_support support[MAX_SUPPORT];
4
    uint32_t n_support;
5
-   unsigned int initialized:1;
6
+   uint32_t init_count;
7
    unsigned int in_valgrind:1;
8
    unsigned int no_color:1;
9
    unsigned int no_config:1;
10
@@ -413,11 +413,9 @@
11
 
12
 static void init_i18n(struct support *support)
13
 {
14
-   /* Load locale from the environment. */
15
-   setlocale(LC_ALL, "");
16
-   /* Set LC_NUMERIC to C so that floating point strings are consistently
17
-    * formatted and parsed across locales. */
18
-   setlocale(LC_NUMERIC, "C");
19
+   /* XXX: we should remove this setlocale() call, after wireplumber
20
+    * XXX: starts setting the locale */
21
+   setlocale(LC_MESSAGES, "");
22
    bindtextdomain(GETTEXT_PACKAGE, LOCALEDIR);
23
    bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
24
    pw_set_domain(GETTEXT_PACKAGE);
25
@@ -594,7 +592,7 @@
26
    char level[32];
27
 
28
    pthread_mutex_lock(&init_lock);
29
-   if (support->initialized)
30
+   if (support->init_count > 0)
31
        goto done;
32
 
33
    pthread_mutex_lock(&support_lock);
34
@@ -666,9 +664,9 @@
35
    add_i18n(support);
36
 
37
    pw_log_info("version %s", pw_get_library_version());
38
-   support->initialized = true;
39
    pthread_mutex_unlock(&support_lock);
40
 done:
41
+   support->init_count++;
42
    pthread_mutex_unlock(&init_lock);
43
 }
44
 
45
@@ -680,6 +678,11 @@
46
    struct plugin *p;
47
 
48
    pthread_mutex_lock(&init_lock);
49
+   if (support->init_count == 0)
50
+       goto done;
51
+   if (--support->init_count > 0)
52
+       goto done;
53
+
54
    pthread_mutex_lock(&support_lock);
55
    pw_log_set(NULL);
56
    spa_list_consume(p, &registry->plugins, link) {
57
@@ -693,6 +696,7 @@
58
    free(support->i18n_domain);
59
    spa_zero(global_support);
60
    pthread_mutex_unlock(&support_lock);
61
+done:
62
    pthread_mutex_unlock(&init_lock);
63
 
64
 }
65
pipewire-0.3.48.tar.gz/src/pipewire/stream.c -> pipewire-0.3.49.tar.gz/src/pipewire/stream.c Changed
261
 
1
@@ -116,6 +116,8 @@
2
    struct spa_io_clock *clock;
3
    struct spa_io_position *position;
4
    struct spa_io_buffers *io;
5
+   struct spa_io_rate_match *rate_match;
6
+   uint32_t rate_queued;
7
    struct {
8
        struct spa_io_position *position;
9
    } rt;
10
@@ -295,7 +297,7 @@
11
        clear_params(impl, id);
12
    } else {
13
        for (i = 0; i < n_params; i++) {
14
-           if (!spa_pod_is_object(params[i]))
15
+           if (params[i] == NULL || !spa_pod_is_object(params[i]))
16
                continue;
17
            clear_params(impl, SPA_POD_OBJECT_ID(params[i]));
18
        }
19
@@ -390,14 +392,14 @@
20
 {
21
    struct stream *impl = user_data;
22
    struct pw_stream *stream = &impl->this;
23
-   pw_log_trace("%p: do process", stream);
24
+   pw_log_trace_fp("%p: do process", stream);
25
    pw_stream_emit_process(stream);
26
    return 0;
27
 }
28
 
29
 static void call_process(struct stream *impl)
30
 {
31
-   pw_log_trace("%p: call process rt:%u", impl, impl->process_rt);
32
+   pw_log_trace_fp("%p: call process rt:%u", impl, impl->process_rt);
33
    if (impl->process_rt)
34
        spa_callbacks_call(&impl->rt_callbacks, struct pw_stream_events, process, 0);
35
    else
36
@@ -411,7 +413,7 @@
37
 {
38
    struct stream *impl = user_data;
39
    struct pw_stream *stream = &impl->this;
40
-   pw_log_trace("%p: drained", stream);
41
+   pw_log_trace_fp("%p: drained", stream);
42
    pw_stream_emit_drained(stream);
43
    return 0;
44
 }
45
@@ -428,7 +430,7 @@
46
 {
47
    struct stream *impl = user_data;
48
    struct pw_stream *stream = &impl->this;
49
-   pw_log_trace("%p: trigger_done", stream);
50
+   pw_log_trace_fp("%p: trigger_done", stream);
51
    pw_stream_emit_trigger_done(stream);
52
    return 0;
53
 }
54
@@ -669,6 +671,12 @@
55
        else
56
            impl->io = NULL;
57
        break;
58
+   case SPA_IO_RateMatch:
59
+       if (data && size >= sizeof(struct spa_io_rate_match))
60
+           impl->rate_match = data;
61
+       else
62
+           impl->rate_match = NULL;
63
+       break;
64
    }
65
    pw_stream_emit_io_changed(stream, id, data, size);
66
 
67
@@ -917,8 +925,9 @@
68
 static inline void copy_position(struct stream *impl, int64_t queued)
69
 {
70
    struct spa_io_position *p = impl->rt.position;
71
-   if (SPA_UNLIKELY(p != NULL)) {
72
-       SEQ_WRITE(impl->seq);
73
+
74
+   SEQ_WRITE(impl->seq);
75
+   if (SPA_LIKELY(p != NULL)) {
76
        impl->time.now = p->clock.nsec;
77
        impl->time.rate = p->clock.rate;
78
        if (SPA_UNLIKELY(impl->clock_id != p->clock.id)) {
79
@@ -929,8 +938,10 @@
80
        impl->time.delay = 0;
81
        impl->time.queued = queued;
82
        impl->quantum = p->clock.duration;
83
-       SEQ_WRITE(impl->seq);
84
    }
85
+   if (SPA_LIKELY(impl->rate_match != NULL))
86
+       impl->rate_queued = impl->rate_match->delay;
87
+   SEQ_WRITE(impl->seq);
88
 }
89
 
90
 static int impl_node_process_input(void *object)
91
@@ -940,7 +951,7 @@
92
    struct spa_io_buffers *io = impl->io;
93
    struct buffer *b;
94
 
95
-   pw_log_trace("%p: process in status:%d id:%d ticks:%"PRIu64" delay:%"PRIi64,
96
+   pw_log_trace_fp("%p: process in status:%d id:%d ticks:%"PRIu64" delay:%"PRIi64,
97
            stream, io->status, io->buffer_id, impl->time.ticks, impl->time.delay);
98
 
99
    if (io->status == SPA_STATUS_HAVE_DATA &&
100
@@ -956,7 +967,7 @@
101
    if (io->status != SPA_STATUS_NEED_DATA) {
102
        /* pop buffer to recycle */
103
        if ((b = pop_queue(impl, &impl->queued))) {
104
-           pw_log_trace("%p: recycle buffer %d", stream, b->id);
105
+           pw_log_trace_fp("%p: recycle buffer %d", stream, b->id);
106
        } else if (io->status == -EPIPE)
107
            return io->status;
108
        io->buffer_id = b ? b->id : SPA_ID_INVALID;
109
@@ -976,16 +987,19 @@
110
    struct buffer *b;
111
    int res;
112
    uint32_t index;
113
+   bool recycled;
114
 
115
 again:
116
-   pw_log_trace("%p: process out status:%d id:%d", stream,
117
+   pw_log_trace_fp("%p: process out status:%d id:%d", stream,
118
            io->status, io->buffer_id);
119
 
120
+   recycled = false;
121
    if ((res = io->status) != SPA_STATUS_HAVE_DATA) {
122
        /* recycle old buffer */
123
        if ((b = get_buffer(stream, io->buffer_id)) != NULL) {
124
-           pw_log_trace("%p: recycle buffer %d", stream, b->id);
125
+           pw_log_trace_fp("%p: recycle buffer %d", stream, b->id);
126
            push_queue(impl, &impl->dequeued, b);
127
+           recycled = true;
128
        }
129
 
130
        /* pop new buffer */
131
@@ -993,17 +1007,17 @@
132
            impl->drained = false;
133
            io->buffer_id = b->id;
134
            res = io->status = SPA_STATUS_HAVE_DATA;
135
-           pw_log_trace("%p: pop %d %p", stream, b->id, io);
136
+           pw_log_trace_fp("%p: pop %d %p", stream, b->id, io);
137
        } else if (impl->draining || impl->drained) {
138
            impl->draining = true;
139
            impl->drained = true;
140
            io->buffer_id = SPA_ID_INVALID;
141
            res = io->status = SPA_STATUS_DRAINED;
142
-           pw_log_trace("%p: draining", stream);
143
+           pw_log_trace_fp("%p: draining", stream);
144
        } else {
145
            io->buffer_id = SPA_ID_INVALID;
146
            res = io->status = SPA_STATUS_NEED_DATA;
147
-           pw_log_trace("%p: no more buffers %p", stream, io);
148
+           pw_log_trace_fp("%p: no more buffers %p", stream, io);
149
        }
150
    }
151
 
152
@@ -1012,12 +1026,12 @@
153
    if (!impl->draining && !impl->driving) {
154
        /* we're not draining, not a driver check if we need to get
155
         * more buffers */
156
-       if (!impl->process_rt) {
157
+       if (!impl->process_rt && (recycled || res == SPA_STATUS_NEED_DATA)) {
158
            /* not realtime and we have a free buffer, trigger process so that we have
159
             * data in the next round. */
160
            if (spa_ringbuffer_get_read_index(&impl->dequeued.ring, &index) > 0)
161
                call_process(impl);
162
-       } else if (io->status == SPA_STATUS_NEED_DATA) {
163
+       } else if (res == SPA_STATUS_NEED_DATA) {
164
            /* realtime and we don't have a buffer, trigger process and try
165
             * again when there is something in the queue now */
166
            call_process(impl);
167
@@ -1027,7 +1041,7 @@
168
        }
169
    }
170
 
171
-   pw_log_trace("%p: res %d", stream, res);
172
+   pw_log_trace_fp("%p: res %d", stream, res);
173
 
174
    if (impl->driving && impl->using_trigger && res != SPA_STATUS_HAVE_DATA)
175
        call_trigger_done(impl);
176
@@ -2061,10 +2075,12 @@
177
 {
178
    struct stream *impl = SPA_CONTAINER_OF(stream, struct stream, this);
179
    uintptr_t seq1, seq2;
180
+   uint32_t rate_queued;
181
 
182
    do {
183
        seq1 = SEQ_READ(impl->seq);
184
        *time = impl->time;
185
+       rate_queued = impl->rate_queued;
186
        seq2 = SEQ_READ(impl->seq);
187
    } while (!SEQ_READ_SUCCESS(seq1, seq2));
188
 
189
@@ -2073,11 +2089,13 @@
190
    else
191
        time->queued = (int64_t)(impl->queued.incount - time->queued);
192
 
193
+   time->queued += rate_queued;
194
+
195
    time->delay += ((impl->latency.min_quantum + impl->latency.max_quantum) / 2) * impl->quantum;
196
    time->delay += (impl->latency.min_rate + impl->latency.max_rate) / 2;
197
    time->delay += ((impl->latency.min_ns + impl->latency.max_ns) / 2) * time->rate.denom / SPA_NSEC_PER_SEC;
198
 
199
-   pw_log_trace("%p: %"PRIi64" %"PRIi64" %"PRIu64" %d/%d %"PRIu64" %"
200
+   pw_log_trace_fp("%p: %"PRIi64" %"PRIi64" %"PRIu64" %d/%d %"PRIu64" %"
201
            PRIu64" %"PRIu64" %"PRIu64" %"PRIu64, stream,
202
            time->now, time->delay, time->ticks,
203
            time->rate.num, time->rate.denom, time->queued,
204
@@ -2105,17 +2123,17 @@
205
 
206
    if ((b = pop_queue(impl, &impl->dequeued)) == NULL) {
207
        res = -errno;
208
-       pw_log_trace("%p: no more buffers: %m", stream);
209
+       pw_log_trace_fp("%p: no more buffers: %m", stream);
210
        errno = -res;
211
        return NULL;
212
    }
213
-   pw_log_trace("%p: dequeue buffer %d", stream, b->id);
214
+   pw_log_trace_fp("%p: dequeue buffer %d size:%"PRIu64, stream, b->id, b->this.size);
215
 
216
    if (b->busy && impl->direction == SPA_DIRECTION_OUTPUT) {
217
        if (ATOMIC_INC(b->busy->count) > 1) {
218
            ATOMIC_DEC(b->busy->count);
219
            push_queue(impl, &impl->dequeued, b);
220
-           pw_log_trace("%p: buffer busy", stream);
221
+           pw_log_trace_fp("%p: buffer busy", stream);
222
            errno = EBUSY;
223
            return NULL;
224
        }
225
@@ -2133,7 +2151,7 @@
226
    if (b->busy)
227
        ATOMIC_DEC(b->busy->count);
228
 
229
-   pw_log_trace("%p: queue buffer %d", stream, b->id);
230
+   pw_log_trace_fp("%p: queue buffer %d", stream, b->id);
231
    if ((res = push_queue(impl, &impl->queued, b)) < 0)
232
        return res;
233
 
234
@@ -2153,7 +2171,7 @@
235
    struct stream *impl = user_data;
236
    struct buffer *b;
237
 
238
-   pw_log_trace("%p: flush", impl);
239
+   pw_log_trace_fp("%p: flush", impl);
240
    do {
241
        b = pop_queue(impl, &impl->queued);
242
        if (b != NULL)
243
@@ -2171,7 +2189,7 @@
244
                  bool async, uint32_t seq, const void *data, size_t size, void *user_data)
245
 {
246
    struct stream *impl = user_data;
247
-   pw_log_trace("%p", impl);
248
+   pw_log_trace_fp("%p", impl);
249
    impl->draining = true;
250
    impl->drained = false;
251
    return 0;
252
@@ -2230,7 +2248,7 @@
253
    struct stream *impl = SPA_CONTAINER_OF(stream, struct stream, this);
254
    int res = 0;
255
 
256
-   pw_log_trace("%p", impl);
257
+   pw_log_trace_fp("%p", impl);
258
 
259
    /* flag to check for old or new behaviour */
260
    impl->using_trigger = true;
261
pipewire-0.3.48.tar.gz/src/pipewire/stream.h -> pipewire-0.3.49.tar.gz/src/pipewire/stream.h Changed
38
 
1
@@ -193,16 +193,30 @@
2
 
3
 /** A time structure */
4
 struct pw_time {
5
-   int64_t now;            /**< the monotonic time in nanoseconds */
6
-   struct spa_fraction rate;   /**< the rate of \a ticks and delay */
7
+   int64_t now;            /**< the monotonic time in nanoseconds. This is the time
8
+                     *  when this time report was updated. It is usually
9
+                     *  updated every graph cycle. You can use the current
10
+                     *  monotonic time to calculate the elapsed time between
11
+                     *  this report and the current state and calculate
12
+                     *  updated ticks and delay values. */
13
+   struct spa_fraction rate;   /**< the rate of \a ticks and delay. This is usually
14
+                     *  expressed in 1/<samplerate>. */
15
    uint64_t ticks;         /**< the ticks at \a now. This is the current time that
16
                      *  the remote end is reading/writing. */
17
-   int64_t delay;          /**< delay to device, add to ticks to get the time of the
18
-                     *  device. Positive for INPUT streams and
19
-                     *  negative for OUTPUT streams. */
20
+   int64_t delay;          /**< delay to device. This is the time it will take for
21
+                     *  the next sample in the stream to be presented by
22
+                     *  the playback device or the time a sample traveled
23
+                     *  from the capture device. This delay includes the
24
+                     *  delay introduced by all filters on the path between
25
+                     *  the stream and the device. The delay is normally
26
+                     *  constant in a graph and can change when the topology
27
+                     *  of the graph or the quantum changes. */
28
    uint64_t queued;        /**< data queued in the stream, this is the sum
29
                      *  of the size fields in the pw_buffer that are
30
-                     *  currently queued */
31
+                     *  currently queued and, for audio streams, the extra
32
+                     *  data queued in the resampler. For audio streams, it
33
+                     *  is thus highly recommended to use the buffer size
34
+                     *  field as the sample count in the buffer. */
35
 };
36
 
37
 #include <pipewire/port.h>
38
pipewire-0.3.48.tar.gz/src/tools/dsffile.c -> pipewire-0.3.49.tar.gz/src/tools/dsffile.c Changed
64
 
1
@@ -214,33 +214,39 @@
2
 dsf_file_read(struct dsf_file *f, void *data, size_t samples, const struct dsf_layout *layout)
3
 {
4
    uint8_t *d = data;
5
-   const uint8_t *s;
6
-   uint32_t i, j, k, total, stride, bytes;
7
-   int32_t interleave = layout->interleave;
8
+   int step = SPA_ABS(layout->interleave);
9
    bool rev = layout->lsb != f->info.lsb;
10
-
11
-   stride = layout->channels * layout->interleave;
12
-   bytes = samples * stride;
13
-   bytes -= bytes % f->info.blocksize;
14
-
15
-   for (total = 0; total < bytes; total += layout->channels * f->info.blocksize) {
16
-       s = f->p + f->offset;
17
-
18
-       for (i = 0; i < f->info.blocksize; i += interleave)  {
19
-           for (j = 0; j < layout->channels; j++) {
20
-               const uint8_t *c = &s[f->info.blocksize * j + i];
21
-               if (interleave > 0) {
22
-                   for (k = 0; k < (uint32_t)interleave; k++)
23
-                       *d++ = rev ? bitrev[c[k]] : c[k];
24
-               } else {
25
-                   for (k = -interleave; k > 0; k--)
26
-                       *d++ = rev ? bitrev[c[k-1]] : c[k-1];
27
-               }
28
+   size_t total, block, offset, pos;
29
+
30
+   block = f->offset / f->info.blocksize;
31
+   offset = block * f->info.blocksize * f->info.channels;
32
+   pos = f->offset % f->info.blocksize;
33
+
34
+   for (total = 0; total < samples && offset + pos < f->info.length; total++) {
35
+       const uint8_t *s = f->p + offset + pos;
36
+       uint32_t i;
37
+
38
+       for (i = 0; i < layout->channels; i++) {
39
+           const uint8_t *c = &s[f->info.blocksize * i];
40
+           int j;
41
+
42
+           if (layout->interleave > 0) {
43
+               for (j = 0; j < step; j++)
44
+                   *d++ = rev ? bitrev[c[j]] : c[j];
45
+           } else {
46
+               for (j = step-1; j >= 0; j--)
47
+                   *d++ = rev ? bitrev[c[j]] : c[j];
48
            }
49
        }
50
-       f->offset += f->info.channels * f->info.blocksize;
51
+       pos += step;
52
+       if (pos == f->info.blocksize) {
53
+           pos = 0;
54
+           offset += f->info.blocksize * f->info.channels;
55
+       }
56
    }
57
-   return total / stride;
58
+   f->offset += total * step;
59
+
60
+   return total;
61
 }
62
 
63
 int dsf_file_close(struct dsf_file *f)
64
pipewire-0.3.48.tar.gz/src/tools/pw-cat.c -> pipewire-0.3.49.tar.gz/src/tools/pw-cat.c Changed
43
 
1
@@ -34,6 +34,7 @@
2
 #include <unistd.h>
3
 #include <assert.h>
4
 #include <ctype.h>
5
+#include <locale.h>
6
 
7
 #include <sndfile.h>
8
 
9
@@ -915,7 +916,7 @@
10
    data->dsf.layout.channels = info.info.dsd.channels;
11
    data->dsf.layout.lsb = info.info.dsd.bitorder == SPA_PARAM_BITORDER_lsb;
12
 
13
-   data->stride = data->dsf.info.channels * SPA_ABS(data->dsf.layout.interleave);
14
+   data->stride = data->dsf.layout.channels * SPA_ABS(data->dsf.layout.interleave);
15
 
16
    if (data->verbose) {
17
        printf("DSD out: channels:%d bitorder:%s interleave:%d\n",
18
@@ -1268,13 +1269,14 @@
19
    }
20
 
21
    if (data->verbose)
22
-       printf("opened file \"%s\" channels:%d rate:%d bitorder:%s\n",
23
+       printf("opened file \"%s\" channels:%d rate:%d samples:%"PRIu64" bitorder:%s\n",
24
                data->filename,
25
                data->dsf.info.channels, data->dsf.info.rate,
26
+               data->dsf.info.samples,
27
                data->dsf.info.lsb ? "lsb" : "msb");
28
 
29
    data->fill = dsf_play;
30
-;
31
+
32
    return 0;
33
 }
34
 
35
@@ -1488,6 +1490,7 @@
36
    int exit_code = EXIT_FAILURE, c, ret;
37
    enum pw_stream_flags flags = 0;
38
 
39
+   setlocale(LC_ALL, "");
40
    pw_init(&argc, &argv);
41
 
42
    flags |= PW_STREAM_FLAG_AUTOCONNECT;
43
pipewire-0.3.48.tar.gz/src/tools/pw-cli.c -> pipewire-0.3.49.tar.gz/src/tools/pw-cli.c Changed
17
 
1
@@ -35,6 +35,7 @@
2
 #include <fnmatch.h>
3
 #include <readline/readline.h>
4
 #include <readline/history.h>
5
+#include <locale.h>
6
 
7
 #if !defined(FNM_EXTMATCH)
8
 #define FNM_EXTMATCH 0
9
@@ -3076,6 +3077,7 @@
10
 
11
    setlinebuf(stdout);
12
 
13
+   setlocale(LC_ALL, "");
14
    pw_init(&argc, &argv);
15
 
16
    while ((c = getopt_long(argc, argv, "hVdr:", long_options, NULL)) != -1) {
17
pipewire-0.3.48.tar.gz/src/tools/pw-dot.c -> pipewire-0.3.49.tar.gz/src/tools/pw-dot.c Changed
600
 
1
@@ -25,9 +25,16 @@
2
 #include <stdio.h>
3
 #include <signal.h>
4
 #include <getopt.h>
5
+#include <unistd.h>
6
+#include <sys/types.h>
7
+#include <sys/stat.h>
8
+#include <sys/mman.h>
9
+#include <fcntl.h>
10
+#include <locale.h>
11
 
12
 #include <spa/utils/result.h>
13
 #include <spa/utils/string.h>
14
+#include <spa/utils/json.h>
15
 #include <spa/debug/pod.h>
16
 #include <spa/debug/format.h>
17
 #include <spa/debug/types.h>
18
@@ -77,7 +84,6 @@
19
 #define INTERFACE_Module   5
20
 #define INTERFACE_Factory  6
21
    uint32_t type;
22
-   struct pw_properties *props;
23
    void *info;
24
 
25
    pw_destroy_t info_destroy;
26
@@ -152,9 +158,8 @@
27
    }
28
 }
29
 
30
-static SPA_PRINTF_FUNC(7,0) void draw_vlabel(char **str, const char *name, uint32_t id, bool detail,
31
-              const struct spa_dict *info_p, const struct spa_dict *p,
32
-              const char *fmt, va_list varargs)
33
+static SPA_PRINTF_FUNC(6,0) void draw_vlabel(char **str, const char *name, uint32_t id, bool detail,
34
+              const struct spa_dict *props, const char *fmt, va_list varargs)
35
 {
36
    /* draw the label header */
37
    dot_str_add(str, "%s_%u [label=\"", name, id);
38
@@ -162,22 +167,19 @@
39
    /* draw the label body */
40
    dot_str_vadd(str, fmt, varargs);
41
 
42
-   if (detail) {
43
-       draw_dict(str, "info_props", info_p);
44
-       draw_dict(str, "properties", p);
45
-   }
46
+   if (detail)
47
+       draw_dict(str, "properties", props);
48
 
49
    /*draw the label footer */
50
    dot_str_add(str, "%s", "\"];\n");
51
 }
52
 
53
-static SPA_PRINTF_FUNC(7,8) void draw_label(char **str, const char *name, uint32_t id, bool detail,
54
-              const struct spa_dict *info_p, const struct spa_dict *p,
55
-              const char *fmt, ...)
56
+static SPA_PRINTF_FUNC(6,7) void draw_label(char **str, const char *name, uint32_t id, bool detail,
57
+              const struct spa_dict *props, const char *fmt, ...)
58
 {
59
    va_list varargs;
60
    va_start(varargs, fmt);
61
-   draw_vlabel(str, name, id, detail, info_p, p, fmt, varargs);
62
+   draw_vlabel(str, name, id, detail, props, fmt, varargs);
63
    va_end(varargs);
64
 }
65
 
66
@@ -199,7 +201,7 @@
67
 
68
    /* draw the label */
69
    draw_label(dot_str,
70
-       "port", g->id, g->data->show_detail, info->props, &g->props->dict,
71
+       "port", g->id, g->data->show_detail, info->props,
72
        "port_id: %u\\lname: %s\\ldirection: %s\\l",
73
        g->id,
74
        spa_dict_lookup(info->props, PW_KEY_PORT_NAME),
75
@@ -237,10 +239,9 @@
76
        g->id,
77
        spa_dict_lookup(info->props, PW_KEY_NODE_NAME),
78
        spa_dict_lookup(info->props, PW_KEY_MEDIA_CLASS));
79
-   if (g->data->show_detail) {
80
-       draw_dict(dot_str, "info_props", info->props);
81
-       draw_dict(dot_str, "properties", &g->props->dict);
82
-   }
83
+
84
+   if (g->data->show_detail)
85
+       draw_dict(dot_str, "properties", info->props);
86
 
87
    /*draw the label footer */
88
    dot_str_add(dot_str, "%s", "\"\n");
89
@@ -249,11 +250,13 @@
90
    struct global *p;
91
    const char *prop_node_id;
92
    spa_list_for_each(p, &g->data->globals, link) {
93
+       struct pw_port_info *pinfo;
94
        if (p->info == NULL)
95
            continue;
96
        if (p->type != INTERFACE_Port)
97
            continue;
98
-       prop_node_id = pw_properties_get(p->props, PW_KEY_NODE_ID);
99
+       pinfo = p->info;
100
+       prop_node_id = spa_dict_lookup(pinfo->props, PW_KEY_NODE_ID);
101
        if (!prop_node_id || (uint32_t)atoi(prop_node_id) != g->id)
102
            continue;
103
        if (p->draw)
104
@@ -290,7 +293,7 @@
105
 
106
    /* draw the label */
107
    draw_label(dot_str,
108
-       "link", g->id, g->data->show_detail, info->props, &g->props->dict,
109
+       "link", g->id, g->data->show_detail, info->props,
110
        "link_id: %u\\loutput_node_id: %u\\linput_node_id: %u\\loutput_port_id: %u\\linput_port_id: %u\\lstate: %s\\l",
111
        g->id,
112
        info->output_node_id,
113
@@ -318,7 +321,7 @@
114
 
115
    /* draw the label */
116
    draw_label(dot_str,
117
-       "client", g->id, g->data->show_detail, info->props, &g->props->dict,
118
+       "client", g->id, g->data->show_detail, info->props,
119
        "client_id: %u\\lname: %s\\lpid: %s\\l",
120
        g->id,
121
        spa_dict_lookup(info->props, PW_KEY_APP_NAME),
122
@@ -345,7 +348,7 @@
123
 
124
    /* draw the label */
125
    draw_label(dot_str,
126
-       "device", g->id, g->data->show_detail, info->props, &g->props->dict,
127
+       "device", g->id, g->data->show_detail, info->props,
128
        "device_id: %u\\lname: %s\\lmedia_class: %s\\lapi: %s\\lpath: %s\\l",
129
        g->id,
130
        spa_dict_lookup(info->props, PW_KEY_DEVICE_NAME),
131
@@ -376,7 +379,7 @@
132
 
133
    /* draw the label */
134
    draw_label(dot_str,
135
-       "factory", g->id, g->data->show_detail, info->props, &g->props->dict,
136
+       "factory", g->id, g->data->show_detail, info->props,
137
        "factory_id: %u\\lname: %s\\lmodule_id: %u\\l",
138
        g->id, info->name, module_id
139
    );
140
@@ -399,7 +402,7 @@
141
 
142
    /* draw the label */
143
    draw_label(dot_str,
144
-       "module", g->id, g->data->show_detail, info->props, &g->props->dict,
145
+       "module", g->id, g->data->show_detail, info->props,
146
        "module_id: %u\\lname: %s\\l",
147
        g->id, info->name
148
    );
149
@@ -611,9 +614,6 @@
150
    struct global *g = user_data;
151
    spa_hook_remove(&g->object_listener);
152
    spa_hook_remove(&g->proxy_listener);
153
-   pw_properties_free(g->props);
154
-   if (g->info)
155
-       g->info_destroy(g->info);
156
 }
157
 
158
 static const struct pw_proxy_events proxy_events = {
159
@@ -701,20 +701,17 @@
160
        return;
161
    }
162
 
163
-        proxy = pw_registry_bind(d->registry, id, type,
164
-                      client_version,
165
-                      sizeof(struct global));
166
+   proxy = pw_registry_bind(d->registry, id, type, client_version, 0);
167
    if (proxy == NULL)
168
        return;
169
 
170
    /* set the global data */
171
-   g = pw_proxy_get_user_data(proxy);
172
+   g = calloc(1, sizeof(struct global));
173
    g->data = d;
174
    g->proxy = proxy;
175
 
176
    g->id = id;
177
    g->type = object_type;
178
-   g->props = props ? pw_properties_new_dict(props) : NULL;
179
    g->info = NULL;
180
 
181
    g->info_destroy = info_destroy;
182
@@ -762,6 +759,300 @@
183
    pw_main_loop_quit(d->loop);
184
 }
185
 
186
+static int get_data_from_pipewire(struct data *data, const char *opt_remote)
187
+{
188
+   struct pw_loop *l;
189
+   struct global *g;
190
+
191
+   data->loop = pw_main_loop_new(NULL);
192
+   if (data->loop == NULL) {
193
+       fprintf(stderr, "can't create main loop: %m\n");
194
+       return -1;
195
+   }
196
+
197
+   l = pw_main_loop_get_loop(data->loop);
198
+   pw_loop_add_signal(l, SIGINT, do_quit, &data);
199
+   pw_loop_add_signal(l, SIGTERM, do_quit, &data);
200
+
201
+   data->context = pw_context_new(l, NULL, 0);
202
+   if (data->context == NULL) {
203
+       fprintf(stderr, "can't create context: %m\n");
204
+       pw_main_loop_destroy(data->loop);
205
+       return -1;
206
+   }
207
+
208
+   data->core = pw_context_connect(data->context,
209
+           pw_properties_new(
210
+               PW_KEY_REMOTE_NAME, opt_remote,
211
+               NULL),
212
+           0);
213
+   if (data->core == NULL) {
214
+       fprintf(stderr, "can't connect: %m\n");
215
+       pw_context_destroy(data->context);
216
+       pw_main_loop_destroy(data->loop);
217
+       return -1;
218
+   }
219
+
220
+   pw_core_add_listener(data->core,
221
+                &data->core_listener,
222
+                &core_events, data);
223
+
224
+   data->registry = pw_core_get_registry(data->core,
225
+                         PW_VERSION_REGISTRY, 0);
226
+   pw_registry_add_listener(data->registry,
227
+                &data->registry_listener,
228
+                &registry_events, data);
229
+
230
+   pw_main_loop_run(data->loop);
231
+
232
+   spa_hook_remove(&data->registry_listener);
233
+   pw_proxy_destroy((struct pw_proxy*)data->registry);
234
+   spa_list_for_each(g, &data->globals, link)
235
+       pw_proxy_destroy(g->proxy);
236
+   spa_hook_remove(&data->core_listener);
237
+   pw_context_destroy(data->context);
238
+   pw_main_loop_destroy(data->loop);
239
+
240
+   return 0;
241
+}
242
+
243
+static void handle_json_obj(struct data *data, struct pw_properties *obj)
244
+{
245
+   struct global *g;
246
+   struct pw_properties *info, *props;
247
+   const char *str;
248
+
249
+   str = pw_properties_get(obj, "type");
250
+   if (!str) {
251
+       fprintf(stderr, "invalid object without type\n");
252
+       return;
253
+   }
254
+
255
+   g = calloc(1, sizeof (struct global));
256
+   g->data = data;
257
+
258
+   if (spa_streq(str, PW_TYPE_INTERFACE_Port)) {
259
+       g->info_destroy = (pw_destroy_t)pw_port_info_free;
260
+       g->draw = draw_port;
261
+       g->type = INTERFACE_Port;
262
+   }
263
+   else if (spa_streq(str, PW_TYPE_INTERFACE_Node)) {
264
+       g->info_destroy = (pw_destroy_t)pw_node_info_free;
265
+       g->draw = draw_node;
266
+       g->type = INTERFACE_Node;
267
+   }
268
+   else if (spa_streq(str, PW_TYPE_INTERFACE_Link)) {
269
+       g->info_destroy = (pw_destroy_t)pw_link_info_free;
270
+       g->draw = draw_link;
271
+       g->type = INTERFACE_Link;
272
+   }
273
+   else if (spa_streq(str, PW_TYPE_INTERFACE_Client)) {
274
+       g->info_destroy = (pw_destroy_t)pw_client_info_free;
275
+       g->draw = draw_client;
276
+       g->type = INTERFACE_Client;
277
+   }
278
+   else if (spa_streq(str, PW_TYPE_INTERFACE_Device)) {
279
+       g->info_destroy = (pw_destroy_t)pw_device_info_free;
280
+       g->draw = draw_device;
281
+       g->type = INTERFACE_Device;
282
+   }
283
+   else if (spa_streq(str, PW_TYPE_INTERFACE_Factory)) {
284
+       g->info_destroy = (pw_destroy_t)pw_factory_info_free;
285
+       g->draw = draw_factory;
286
+       g->type = INTERFACE_Factory;
287
+   }
288
+   else if (spa_streq(str, PW_TYPE_INTERFACE_Module)) {
289
+       g->info_destroy = (pw_destroy_t)pw_module_info_free;
290
+       g->draw = draw_module;
291
+       g->type = INTERFACE_Module;
292
+   }
293
+   else {
294
+       free(g);
295
+       return;
296
+   }
297
+
298
+   g->id = pw_properties_get_uint32(obj, "id", 0);
299
+
300
+   str = pw_properties_get(obj, "info");
301
+   info = pw_properties_new_string(str);
302
+
303
+   str = pw_properties_get(info, "props");
304
+   props = str ? pw_properties_new_string(str) : NULL;
305
+
306
+   switch (g->type) {
307
+       case INTERFACE_Port: {
308
+           struct pw_port_info pinfo = {0};
309
+           pinfo.id = g->id;
310
+           str = pw_properties_get(info, "direction");
311
+           pinfo.direction = spa_streq(str, "output") ?
312
+               PW_DIRECTION_OUTPUT : PW_DIRECTION_INPUT;
313
+           pinfo.props = props ? &props->dict : NULL;
314
+           pinfo.change_mask = PW_PORT_CHANGE_MASK_PROPS;
315
+           g->info = pw_port_info_update(NULL, &pinfo);
316
+           break;
317
+       }
318
+       case INTERFACE_Node: {
319
+           struct pw_node_info ninfo = {0};
320
+           ninfo.id = g->id;
321
+           ninfo.max_input_ports =
322
+               pw_properties_get_uint32(info, "max-input-ports", 0);
323
+           ninfo.max_output_ports =
324
+               pw_properties_get_uint32(info, "max-output-ports", 0);
325
+           ninfo.n_input_ports =
326
+               pw_properties_get_uint32(info, "n-input-ports", 0);
327
+           ninfo.n_output_ports =
328
+               pw_properties_get_uint32(info, "n-output-ports", 0);
329
+
330
+           str = pw_properties_get(info, "state");
331
+           if (spa_streq(str, "running"))
332
+               ninfo.state = PW_NODE_STATE_RUNNING;
333
+           else if (spa_streq(str, "idle"))
334
+               ninfo.state = PW_NODE_STATE_IDLE;
335
+           else if (spa_streq(str, "suspended"))
336
+               ninfo.state = PW_NODE_STATE_SUSPENDED;
337
+           else if (spa_streq(str, "creating"))
338
+               ninfo.state = PW_NODE_STATE_CREATING;
339
+           else
340
+               ninfo.state = PW_NODE_STATE_ERROR;
341
+           ninfo.error = pw_properties_get(info, "error");
342
+
343
+           ninfo.props = props ? &props->dict : NULL;
344
+           ninfo.change_mask = PW_NODE_CHANGE_MASK_INPUT_PORTS |
345
+                       PW_NODE_CHANGE_MASK_OUTPUT_PORTS |
346
+                       PW_NODE_CHANGE_MASK_STATE |
347
+                       PW_NODE_CHANGE_MASK_PROPS;
348
+           g->info = pw_node_info_update(NULL, &ninfo);
349
+           break;
350
+       }
351
+       case INTERFACE_Link: {
352
+           struct pw_link_info linfo = {0};
353
+           linfo.id = g->id;
354
+           linfo.output_node_id =
355
+               pw_properties_get_uint32(info, "output-node-id", 0);
356
+           linfo.output_port_id =
357
+               pw_properties_get_uint32(info, "output-port-id", 0);
358
+           linfo.input_node_id =
359
+               pw_properties_get_uint32(info, "input-node-id", 0);
360
+           linfo.input_port_id =
361
+               pw_properties_get_uint32(info, "input-port-id", 0);
362
+
363
+           str = pw_properties_get(info, "state");
364
+           if (spa_streq(str, "active"))
365
+               linfo.state = PW_LINK_STATE_ACTIVE;
366
+           else if (spa_streq(str, "paused"))
367
+               linfo.state = PW_LINK_STATE_PAUSED;
368
+           else if (spa_streq(str, "allocating"))
369
+               linfo.state = PW_LINK_STATE_ALLOCATING;
370
+           else if (spa_streq(str, "negotiating"))
371
+               linfo.state = PW_LINK_STATE_NEGOTIATING;
372
+           else if (spa_streq(str, "init"))
373
+               linfo.state = PW_LINK_STATE_INIT;
374
+           else if (spa_streq(str, "unlinked"))
375
+               linfo.state = PW_LINK_STATE_UNLINKED;
376
+           else
377
+               linfo.state = PW_LINK_STATE_ERROR;
378
+           linfo.error = pw_properties_get(info, "error");
379
+
380
+           linfo.props = props ? &props->dict : NULL;
381
+           linfo.change_mask = PW_LINK_CHANGE_MASK_STATE |
382
+                       PW_LINK_CHANGE_MASK_PROPS;
383
+           g->info = pw_link_info_update(NULL, &linfo);
384
+           break;
385
+       }
386
+       case INTERFACE_Client: {
387
+           struct pw_client_info cinfo = {0};
388
+           cinfo.id = g->id;
389
+           cinfo.props = props ? &props->dict : NULL;
390
+           cinfo.change_mask = PW_CLIENT_CHANGE_MASK_PROPS;
391
+           g->info = pw_client_info_update(NULL, &cinfo);
392
+           break;
393
+       }
394
+       case INTERFACE_Device: {
395
+           struct pw_device_info dinfo = {0};
396
+           dinfo.id = g->id;
397
+           dinfo.props = props ? &props->dict : NULL;
398
+           dinfo.change_mask = PW_DEVICE_CHANGE_MASK_PROPS;
399
+           g->info = pw_device_info_update(NULL, &dinfo);
400
+           break;
401
+       }
402
+       case INTERFACE_Factory: {
403
+           struct pw_factory_info finfo = {0};
404
+           finfo.id = g->id;
405
+           finfo.name = pw_properties_get(info, "name");
406
+           finfo.type = pw_properties_get(info, "type");
407
+           finfo.version = pw_properties_get_uint32(info, "version", 0);
408
+           finfo.props = props ? &props->dict : NULL;
409
+           finfo.change_mask = PW_FACTORY_CHANGE_MASK_PROPS;
410
+           g->info = pw_factory_info_update(NULL, &finfo);
411
+           break;
412
+       }
413
+       case INTERFACE_Module: {
414
+           struct pw_module_info minfo = {0};
415
+           minfo.id = g->id;
416
+           minfo.name = pw_properties_get(info, "name");
417
+           minfo.filename = pw_properties_get(info, "filename");
418
+           minfo.args = pw_properties_get(info, "args");
419
+           minfo.props = props ? &props->dict : NULL;
420
+           minfo.change_mask = PW_MODULE_CHANGE_MASK_PROPS;
421
+           g->info = pw_module_info_update(NULL, &minfo);
422
+           break;
423
+       }
424
+       default:
425
+           break;
426
+   }
427
+
428
+   pw_properties_free(info);
429
+   pw_properties_free(props);
430
+
431
+   /* add the global to the list */
432
+   spa_list_insert(&data->globals, &g->link);
433
+}
434
+
435
+static int get_data_from_json(struct data *data, const char *json_path)
436
+{
437
+   int fd, len;
438
+   void *json;
439
+   struct stat sbuf;
440
+   struct spa_json it[2];
441
+   const char *value;
442
+
443
+   if ((fd = open(json_path,  O_CLOEXEC | O_RDONLY)) < 0) {
444
+       fprintf(stderr, "error opening file '%s': %m\n", json_path);
445
+       return -1;
446
+   }
447
+   if (fstat(fd, &sbuf) < 0) {
448
+       fprintf(stderr, "error statting file '%s': %m\n", json_path);
449
+       close(fd);
450
+       return -1;
451
+   }
452
+   if ((json = mmap(NULL, sbuf.st_size, PROT_READ, MAP_PRIVATE, fd, 0)) == MAP_FAILED) {
453
+       fprintf(stderr, "error mmapping file '%s': %m\n", json_path);
454
+       close(fd);
455
+       return -1;
456
+   }
457
+
458
+   close(fd);
459
+   spa_json_init(&it[0], json, sbuf.st_size);
460
+
461
+   if (spa_json_enter_array(&it[0], &it[1]) <= 0) {
462
+       fprintf(stderr, "expected top-level array in JSON file '%s'\n", json_path);
463
+       munmap(json, sbuf.st_size);
464
+       return -1;
465
+   }
466
+
467
+   while ((len = spa_json_next(&it[1], &value)) > 0 && spa_json_is_object(value, len)) {
468
+       struct pw_properties *obj;
469
+       obj = pw_properties_new(NULL, NULL);
470
+       len = spa_json_container_len(&it[1], value, len);
471
+       pw_properties_update_string(obj, value, len);
472
+       handle_json_obj(data, obj);
473
+       pw_properties_free(obj);
474
+   }
475
+
476
+   munmap(json, sbuf.st_size);
477
+   return 0;
478
+}
479
+
480
 static void show_help(const char *name, bool error)
481
 {
482
         fprintf(error ? stderr : stdout, "%s [options]\n"
483
@@ -773,7 +1064,8 @@
484
        "  -r, --remote                          Remote daemon name\n"
485
        "  -o, --output                          Output file (Default %s)\n"
486
        "  -L, --lr                              Use left-right rank direction\n"
487
-       "  -9, --90                              Use orthogonal edges\n",
488
+       "  -9, --90                              Use orthogonal edges\n"
489
+       "  -j, --json                            Read objects from pw-dump JSON file\n",
490
        name,
491
        DEFAULT_DOT_PATH);
492
 }
493
@@ -781,9 +1073,10 @@
494
 int main(int argc, char *argv[])
495
 {
496
    struct data data = { 0 };
497
-   struct pw_loop *l;
498
+   struct global *g;
499
    const char *opt_remote = NULL;
500
    const char *dot_path = DEFAULT_DOT_PATH;
501
+   const char *json_path = NULL;
502
    static const struct option long_options[] = {
503
        { "help",   no_argument,        NULL, 'h' },
504
        { "version",    no_argument,        NULL, 'V' },
505
@@ -794,13 +1087,15 @@
506
        { "output", required_argument,  NULL, 'o' },
507
        { "lr",     no_argument,        NULL, 'L' },
508
        { "90",     no_argument,        NULL, '9' },
509
+       { "json",   required_argument,  NULL, 'j' },
510
        { NULL, 0, NULL, 0}
511
    };
512
    int c;
513
 
514
+   setlocale(LC_ALL, "");
515
    pw_init(&argc, &argv);
516
 
517
-   while ((c = getopt_long(argc, argv, "hVasdr:o:L9", long_options, NULL)) != -1) {
518
+   while ((c = getopt_long(argc, argv, "hVasdr:o:L9j:", long_options, NULL)) != -1) {
519
        switch (c) {
520
        case 'h' :
521
            show_help(argv[0], false);
522
@@ -841,63 +1136,35 @@
523
            data.dot_orthoedges = true;
524
            fprintf(stderr, "orthogonal edges enabled\n");
525
            break;
526
+       case 'j' :
527
+           json_path = optarg;
528
+           fprintf(stderr, "Using JSON file %s as input\n", json_path);
529
+           break;
530
        default:
531
            show_help(argv[0], true);
532
            return -1;
533
        }
534
    }
535
 
536
-   data.loop = pw_main_loop_new(NULL);
537
-   if (data.loop == NULL) {
538
-       fprintf(stderr, "can't create main loop: %m\n");
539
+   if (!(data.dot_str = dot_str_new()))
540
        return -1;
541
-   }
542
 
543
-   l = pw_main_loop_get_loop(data.loop);
544
-   pw_loop_add_signal(l, SIGINT, do_quit, &data);
545
-   pw_loop_add_signal(l, SIGTERM, do_quit, &data);
546
-
547
-   data.context = pw_context_new(l, NULL, 0);
548
-   if (data.context == NULL) {
549
-       fprintf(stderr, "can't create context: %m\n");
550
-       return -1;
551
-   }
552
+   spa_list_init(&data.globals);
553
 
554
-   data.core = pw_context_connect(data.context,
555
-           pw_properties_new(
556
-               PW_KEY_REMOTE_NAME, opt_remote,
557
-               NULL),
558
-           0);
559
-   if (data.core == NULL) {
560
-       fprintf(stderr, "can't connect: %m\n");
561
+   if (!json_path && get_data_from_pipewire(&data, opt_remote) < 0)
562
        return -1;
563
-   }
564
-
565
-   data.dot_str = dot_str_new();
566
-   if (data.dot_str == NULL)
567
+   else if (json_path && get_data_from_json(&data, json_path) < 0)
568
        return -1;
569
 
570
-   spa_list_init(&data.globals);
571
-
572
-   pw_core_add_listener(data.core,
573
-                  &data.core_listener,
574
-                  &core_events, &data);
575
-   data.registry = pw_core_get_registry(data.core,
576
-                     PW_VERSION_REGISTRY, 0);
577
-   pw_registry_add_listener(data.registry,
578
-                      &data.registry_listener,
579
-                      &registry_events, &data);
580
-
581
-   pw_main_loop_run(data.loop);
582
-
583
    draw_graph(&data, dot_path);
584
 
585
    dot_str_clear(&data.dot_str);
586
-   spa_hook_remove(&data.registry_listener);
587
-   pw_proxy_destroy((struct pw_proxy*)data.registry);
588
-   spa_hook_remove(&data.core_listener);
589
-   pw_context_destroy(data.context);
590
-   pw_main_loop_destroy(data.loop);
591
+   spa_list_consume(g, &data.globals, link) {
592
+       if (g->info && g->info_destroy)
593
+           g->info_destroy(g->info);
594
+       spa_list_remove(&g->link);
595
+       free(g);
596
+   }
597
    pw_deinit();
598
 
599
    return 0;
600
pipewire-0.3.48.tar.gz/src/tools/pw-dump.c -> pipewire-0.3.49.tar.gz/src/tools/pw-dump.c Changed
46
 
1
@@ -31,6 +31,7 @@
2
 #include <limits.h>
3
 #include <math.h>
4
 #include <fnmatch.h>
5
+#include <locale.h>
6
 
7
 #if !defined(FNM_EXTMATCH)
8
 #define FNM_EXTMATCH 0
9
@@ -278,13 +279,15 @@
10
 
11
 static void put_double(struct data *d, const char *key, double val)
12
 {
13
-   put_fmt(d, key, "%s%f%s", NUMBER, val, NORMAL);
14
+   char buf[128];
15
+   put_fmt(d, key, "%s%s%s", NUMBER,
16
+           spa_json_format_float(buf, sizeof(buf), val), NORMAL);
17
 }
18
 
19
 static void put_value(struct data *d, const char *key, const char *val)
20
 {
21
    int64_t li;
22
-   double dv;
23
+   float fv;
24
 
25
    if (val == NULL)
26
        put_literal(d, key, "null");
27
@@ -292,8 +295,8 @@
28
        put_literal(d, key, val);
29
    else if (spa_atoi64(val, &li, 10))
30
        put_int(d, key, li);
31
-   else if (spa_atod(val, &dv))
32
-       put_double(d, key, dv);
33
+   else if (spa_json_parse_float(val, strlen(val), &fv))
34
+       put_double(d, key, fv);
35
    else
36
        put_string(d, key, val);
37
 }
38
@@ -1487,6 +1490,7 @@
39
    };
40
    int c;
41
 
42
+   setlocale(LC_ALL, "");
43
    pw_init(&argc, &argv);
44
 
45
    data.out = stdout;
46
pipewire-0.3.48.tar.gz/src/tools/pw-link.c -> pipewire-0.3.49.tar.gz/src/tools/pw-link.c Changed
17
 
1
@@ -27,6 +27,7 @@
2
 #include <math.h>
3
 #include <getopt.h>
4
 #include <regex.h>
5
+#include <locale.h>
6
 
7
 #include <spa/utils/result.h>
8
 #include <spa/utils/string.h>
9
@@ -599,6 +600,7 @@
10
        { NULL, 0, NULL, 0}
11
    };
12
 
13
+   setlocale(LC_ALL, "");
14
    pw_init(&argc, &argv);
15
    spa_list_init(&data.objects);
16
 
17
pipewire-0.3.48.tar.gz/src/tools/pw-loopback.c -> pipewire-0.3.49.tar.gz/src/tools/pw-loopback.c Changed
17
 
1
@@ -29,6 +29,7 @@
2
 #include <getopt.h>
3
 #include <limits.h>
4
 #include <math.h>
5
+#include <locale.h>
6
 
7
 #include <spa/utils/result.h>
8
 #include <spa/pod/builder.h>
9
@@ -124,6 +125,7 @@
10
    };
11
    int c, res = -1;
12
 
13
+   setlocale(LC_ALL, "");
14
    pw_init(&argc, &argv);
15
 
16
    data.channels = DEFAULT_CHANNELS;
17
pipewire-0.3.48.tar.gz/src/tools/pw-metadata.c -> pipewire-0.3.49.tar.gz/src/tools/pw-metadata.c Changed
17
 
1
@@ -26,6 +26,7 @@
2
 #include <signal.h>
3
 #include <math.h>
4
 #include <getopt.h>
5
+#include <locale.h>
6
 
7
 #include <spa/utils/result.h>
8
 #include <spa/utils/string.h>
9
@@ -198,6 +199,7 @@
10
 
11
    setlinebuf(stdout);
12
 
13
+   setlocale(LC_ALL, "");
14
    pw_init(&argc, &argv);
15
 
16
    data.opt_name = "default";
17
pipewire-0.3.48.tar.gz/src/tools/pw-mididump.c -> pipewire-0.3.49.tar.gz/src/tools/pw-mididump.c Changed
17
 
1
@@ -26,6 +26,7 @@
2
 #include <signal.h>
3
 #include <math.h>
4
 #include <getopt.h>
5
+#include <locale.h>
6
 
7
 #include <spa/utils/result.h>
8
 #include <spa/utils/defs.h>
9
@@ -195,6 +196,7 @@
10
        { NULL, 0, NULL, 0}
11
    };
12
 
13
+   setlocale(LC_ALL, "");
14
    pw_init(&argc, &argv);
15
 
16
    setlinebuf(stdout);
17
pipewire-0.3.48.tar.gz/src/tools/pw-mon.c -> pipewire-0.3.49.tar.gz/src/tools/pw-mon.c Changed
17
 
1
@@ -26,6 +26,7 @@
2
 #include <signal.h>
3
 #include <getopt.h>
4
 #include <unistd.h>
5
+#include <locale.h>
6
 
7
 #include <spa/utils/result.h>
8
 #include <spa/utils/string.h>
9
@@ -771,6 +772,7 @@
10
    int c;
11
    bool colors = false;
12
 
13
+   setlocale(LC_ALL, "");
14
    pw_init(&argc, &argv);
15
 
16
    setlinebuf(stdout);
17
pipewire-0.3.48.tar.gz/src/tools/pw-profiler.c -> pipewire-0.3.49.tar.gz/src/tools/pw-profiler.c Changed
17
 
1
@@ -25,6 +25,7 @@
2
 #include <stdio.h>
3
 #include <signal.h>
4
 #include <getopt.h>
5
+#include <locale.h>
6
 
7
 #include <spa/utils/result.h>
8
 #include <spa/utils/string.h>
9
@@ -565,6 +566,7 @@
10
    };
11
    int c;
12
 
13
+   setlocale(LC_ALL, "");
14
    pw_init(&argc, &argv);
15
 
16
    while ((c = getopt_long(argc, argv, "hVr:o:", long_options, NULL)) != -1) {
17
pipewire-0.3.48.tar.gz/src/tools/pw-reserve.c -> pipewire-0.3.49.tar.gz/src/tools/pw-reserve.c Changed
17
 
1
@@ -26,6 +26,7 @@
2
 
3
 #include <getopt.h>
4
 #include <signal.h>
5
+#include <locale.h>
6
 
7
 #include <dbus/dbus.h>
8
 
9
@@ -121,6 +122,7 @@
10
 
11
    setlinebuf(stdout);
12
 
13
+   setlocale(LC_ALL, "");
14
    pw_init(&argc, &argv);
15
 
16
    while ((c = getopt_long(argc, argv, "hVn:a:p:m", long_options, NULL)) != -1) {
17
pipewire-0.3.48.tar.gz/test/meson.build -> pipewire-0.3.49.tar.gz/test/meson.build Changed
30
 
1
@@ -119,6 +119,28 @@
2
                link_with: pwtest_lib)
3
 )
4
 
5
+openal_info = find_program('openal-info', required: false)
6
+if openal_info.found()
7
+    cdata.set_quoted('OPENAL_INFO_PATH', openal_info.full_path())
8
+endif
9
+summary({'openal-info': openal_info.found()}, bool_yn: true, section: 'Functional test programs')
10
+
11
+pactl = find_program('pactl', required: false)
12
+if pactl.found()
13
+    cdata.set_quoted('PACTL_PATH', pactl.full_path())
14
+endif
15
+summary({'pactl': pactl.found()}, bool_yn: true, section: 'Functional test programs')
16
+
17
+if default_sm == 'media-session' or default_sm == 'wireplumber'
18
+  test('test-functional',
19
+    executable('test-functional',
20
+               'test-functional.c',
21
+               include_directories: pwtest_inc,
22
+               dependencies: [ spa_dep ],
23
+               link_with: pwtest_lib)
24
+  )
25
+endif
26
+
27
 valgrind = find_program('valgrind', required: false)
28
 summary({'valgrind (test setup)': valgrind.found()}, bool_yn: true, section: 'Optional programs')
29
 if valgrind.found()
30
pipewire-0.3.48.tar.gz/test/pwtest.c -> pipewire-0.3.49.tar.gz/test/pwtest.c Changed
369
 
1
@@ -128,6 +128,8 @@
2
    struct spa_list suites;
3
    unsigned int timeout;
4
    bool no_fork;
5
+   bool terminate;
6
+   struct spa_list cleanup_pids;
7
 
8
    const char *test_filter;
9
    bool has_iteration_filter;
10
@@ -135,6 +137,11 @@
11
    char *xdg_dir;
12
 };
13
 
14
+struct cleanup_pid {
15
+   struct spa_list link;
16
+   pid_t pid;
17
+};
18
+
19
 struct pwtest_context *pwtest_get_context(struct pwtest_test *t)
20
 {
21
    return ctx;
22
@@ -175,6 +182,56 @@
23
    }
24
 }
25
 
26
+static int add_cleanup_pid(struct pwtest_context *ctx, pid_t pid)
27
+{
28
+   struct cleanup_pid *cpid;
29
+
30
+   if (pid == 0)
31
+       return -EINVAL;
32
+
33
+   cpid = calloc(1, sizeof(struct cleanup_pid));
34
+   if (cpid == NULL)
35
+       return -errno;
36
+
37
+   cpid->pid = pid;
38
+   spa_list_append(&ctx->cleanup_pids, &cpid->link);
39
+
40
+   return 0;
41
+}
42
+
43
+static void remove_cleanup_pid(struct pwtest_context *ctx, pid_t pid)
44
+{
45
+   struct cleanup_pid *cpid, *t;
46
+
47
+   spa_list_for_each_safe(cpid, t, &ctx->cleanup_pids, link) {
48
+       if (cpid->pid == pid) {
49
+           spa_list_remove(&cpid->link);
50
+           free(cpid);
51
+       }
52
+   }
53
+}
54
+
55
+static void terminate_cleanup_pids(struct pwtest_context *ctx)
56
+{
57
+   struct cleanup_pid *cpid;
58
+   spa_list_for_each(cpid, &ctx->cleanup_pids, link) {
59
+       /* Don't free here, to be signal-safe */
60
+       if (cpid->pid != 0) {
61
+           kill(cpid->pid, SIGTERM);
62
+           cpid->pid = 0;
63
+       }
64
+   }
65
+}
66
+
67
+static void free_cleanup_pids(struct pwtest_context *ctx)
68
+{
69
+   struct cleanup_pid *cpid;
70
+   spa_list_consume(cpid, &ctx->cleanup_pids, link) {
71
+       spa_list_remove(&cpid->link);
72
+       free(cpid);
73
+   }
74
+}
75
+
76
 static void pwtest_backtrace(pid_t p)
77
 {
78
 #ifdef HAVE_GSTACK
79
@@ -407,6 +464,34 @@
80
        pwtest_error_with_msg("Unable to create temporary file: %s", strerror(errno));
81
 }
82
 
83
+int
84
+pwtest_spawn(const char *file, char *const argv[])
85
+{
86
+   int r;
87
+   int status = -1;
88
+   pid_t pid;
89
+   const int fail_code = 121;
90
+
91
+   pid = fork();
92
+   if (pid == 0) {
93
+       /* child process */
94
+       execvp(file, (char **)argv);
95
+       exit(fail_code);
96
+   } else if (pid < 0)
97
+       pwtest_error_with_msg("Unable to fork: %s", strerror(errno));
98
+
99
+   add_cleanup_pid(ctx, pid);
100
+   r = waitpid(pid, &status, 0);
101
+   remove_cleanup_pid(ctx, pid);
102
+   if (r <= 0)
103
+       pwtest_error_with_msg("waitpid failed: %s", strerror(errno));
104
+
105
+   if (WEXITSTATUS(status) == fail_code)
106
+       pwtest_error_with_msg("exec %s failed", file);
107
+
108
+   return status;
109
+}
110
+
111
 void _pwtest_add(struct pwtest_context *ctx, struct pwtest_suite *suite,
112
         const char *funcname, const void *func, ...)
113
 {
114
@@ -461,6 +546,8 @@
115
            pw_properties_set(t->args.env, key, value);
116
            break;
117
        case PWTEST_ARG_DAEMON:
118
+           if (RUNNING_ON_VALGRIND)
119
+               t->result = PWTEST_SKIP;
120
            t->args.pw_daemon = true;
121
            break;
122
        }
123
@@ -568,6 +655,9 @@
124
 {
125
    struct pwtest_suite *c, *tmp;
126
 
127
+   terminate_cleanup_pids(ctx);
128
+   free_cleanup_pids(ctx);
129
+
130
    spa_list_for_each_safe(c, tmp, &ctx->suites, link) {
131
        free_suite(c);
132
    }
133
@@ -585,13 +675,14 @@
134
    sigaction(signal, &act, NULL);
135
 
136
    pwtest_backtrace(0);
137
+   terminate_cleanup_pids(ctx);
138
    raise(signal);
139
 }
140
 
141
 static inline void log_append(struct pw_array *buffer, int fd)
142
 {
143
    int r = 0;
144
-   const int sz = 1024;
145
+   const int sz = 65536;
146
 
147
    while (true) {
148
        r = pw_array_ensure_size(buffer, sz);
149
@@ -646,6 +737,7 @@
150
    pid_t pid;
151
    char pw_remote[64];
152
    int status;
153
+   int r;
154
 
155
    spa_scnprintf(pw_remote, sizeof(pw_remote), "pwtest-pw-%u\n", count++);
156
    replace_env(t, "PIPEWIRE_REMOTE", pw_remote);
157
@@ -653,15 +745,26 @@
158
    pid = fork();
159
    if (pid == 0) {
160
        /* child */
161
+       setpgid(0, 0);
162
+
163
        setenv("PIPEWIRE_CORE", pw_remote, 1);
164
+       setenv("PIPEWIRE_DEBUG", "4", 0);
165
+       setenv("WIREPLUMBER_DEBUG", "4", 0);
166
+
167
+       r = dup2(stderr_fd, STDERR_FILENO);
168
+       spa_assert_se(r != -1);
169
+       r = dup2(stderr_fd, STDOUT_FILENO);
170
+       spa_assert_se(r != -1);
171
 
172
-       dup2(stderr_fd, STDERR_FILENO);
173
-       setlinebuf(stderr);
174
        execl(daemon, daemon, (char*)NULL);
175
        return -errno;
176
 
177
+   } else if (pid < 0) {
178
+       return pid;
179
    }
180
 
181
+   add_cleanup_pid(ctx, -pid);
182
+
183
    /* parent */
184
    sleep(1); /* FIXME how to wait for pw to be ready? */
185
    if (waitpid(pid, &status, WNOHANG) > 0) {
186
@@ -701,6 +804,7 @@
187
    replace_env(t, "TMPDIR", xdg_runtime_dir);
188
 
189
    replace_env(t, "SPA_PLUGIN_DIR", BUILD_ROOT "/spa/plugins");
190
+   replace_env(t, "SPA_DATA_DIR", SOURCE_ROOT "/spa/plugins");
191
    replace_env(t, "PIPEWIRE_CONFIG_DIR", BUILD_ROOT "/src/daemon");
192
    replace_env(t, "PIPEWIRE_MODULE_DIR", BUILD_ROOT "/src/modules");
193
    replace_env(t, "ACP_PATHS_DIR", SOURCE_ROOT "/spa/plugins/alsa/mixer/paths");
194
@@ -711,7 +815,8 @@
195
 static void close_pipes(int fds[_FD_LAST])
196
 {
197
    for (int i = 0; i < _FD_LAST; i++) {
198
-       close(fds[i]);
199
+       if (fds[i] >= 0)
200
+           close(fds[i]);
201
        fds[i] = -1;
202
    }
203
 }
204
@@ -720,20 +825,40 @@
205
 {
206
    int r;
207
    int i;
208
+   int pipe_max_size = 4194304;
209
 
210
    for (i = 0; i < _FD_LAST; i++) {
211
        read_fds[i] = -1;
212
        write_fds[i] = -1;
213
    }
214
 
215
+#ifdef __linux__
216
+   {
217
+       FILE *f;
218
+       f = fopen("/proc/sys/fs/pipe-max-size", "r");
219
+       if (f) {
220
+           if (fscanf(f, "%d", &r) == 1)
221
+               pipe_max_size = SPA_MIN(r, pipe_max_size);
222
+           fclose(f);
223
+       }
224
+   }
225
+#endif
226
+
227
    for (i = 0; i < _FD_LAST; i++) {
228
        int pipe[2];
229
 
230
-       r = pipe2(pipe, O_NONBLOCK);
231
+       r = pipe2(pipe, O_CLOEXEC | O_NONBLOCK);
232
        if (r < 0)
233
            goto error;
234
        read_fds[i] = pipe[0];
235
        write_fds[i] = pipe[1];
236
+#ifdef __linux__
237
+       /* Max pipe buffers, to avoid scrambling if reading lags.
238
+        * Can't use blocking write fds, since reading too slow
239
+        * then affects execution.
240
+        */
241
+       fcntl(write_fds[i], F_SETPIPE_SZ, pipe_max_size);
242
+#endif
243
    }
244
 
245
    return 0;
246
@@ -783,6 +908,9 @@
247
 
248
    close_pipes(read_fds);
249
 
250
+   /* Reset cleanup pid list */
251
+   free_cleanup_pids(ctx);
252
+
253
    /* Catch any crashers so we can insert a backtrace */
254
    sigemptyset(&act.sa_mask);
255
    act.sa_flags = 0;
256
@@ -963,12 +1091,14 @@
257
            errno = -r;
258
            goto error;
259
        }
260
+       add_cleanup_pid(ctx, pid);
261
 
262
        r = monitor_test_forked(t, pid, read_fds);
263
        if (r < 0) {
264
            errno = -r;
265
            goto error;
266
        }
267
+       remove_cleanup_pid(ctx, pid);
268
    }
269
 
270
    errno = 0;
271
@@ -976,6 +1106,12 @@
272
    if (errno)
273
        t->sig_or_errno = -errno;
274
 
275
+   if (ctx->terminate) {
276
+       char *buf = pw_array_add(&t->logs[FD_LOG], 64);
277
+       spa_scnprintf(buf, 64, "pwtest: tests terminated by signal\n");
278
+       t->result = PWTEST_SYSTEM_ERROR;
279
+   }
280
+
281
    for (size_t i = 0; i < SPA_N_ELEMENTS(read_fds); i++) {
282
        log_append(&t->logs[i], read_fds[i]);
283
    }
284
@@ -983,8 +1119,16 @@
285
    if (pw_daemon > 0) {
286
        int status;
287
 
288
-       kill(pw_daemon, SIGTERM);
289
-       r = waitpid(pw_daemon, &status, 0);
290
+       kill(-pw_daemon, SIGTERM);
291
+       remove_cleanup_pid(ctx, -pw_daemon);
292
+
293
+       /* blocking read. the other end closes when done */
294
+       close_pipes(write_fds);
295
+       fcntl(read_fds[FD_DAEMON], F_SETFL, O_CLOEXEC);
296
+       do {
297
+           log_append(&t->logs[FD_DAEMON], read_fds[FD_DAEMON]);
298
+       } while ((r = waitpid(pw_daemon, &status, WNOHANG)) == 0);
299
+
300
        if (r > 0) {
301
            /* write_fds are closed in the parent process, so we append directly */
302
            char *buf = pw_array_add(&t->logs[FD_DAEMON], 64);
303
@@ -1147,6 +1291,11 @@
304
                        r = EXIT_FAILURE;
305
                        break;
306
                }
307
+
308
+               if (ctx->terminate) {
309
+                   r = EXIT_FAILURE;
310
+                   return r;
311
+               }
312
            }
313
        }
314
    }
315
@@ -1211,7 +1360,7 @@
316
        "  -h, --help       Show this help\n"
317
        "  --verbose        Verbose output\n"
318
        "  --list       List all available suites and tests\n"
319
-       "  --timeout=N      Set the test timeout to N seconds (default: 30)\n"
320
+       "  --timeout=N      Set the test timeout to N seconds (default: 15)\n"
321
        "  --filter-test=glob   Run only tests matching the given glob\n"
322
        "  --filter-suites=glob Run only suites matching the given glob\n"
323
        "  --filter-iteration=N Run only iteration N\n"
324
@@ -1223,6 +1372,17 @@
325
        progname);
326
 }
327
 
328
+static void sigterm_handler(int signo)
329
+{
330
+   terminate_cleanup_pids(ctx);
331
+   ctx->terminate = true;
332
+   if (ctx->no_fork) {
333
+       signal(SIGTERM, SIG_DFL);
334
+       signal(SIGINT, SIG_DFL);
335
+       raise(signo);
336
+   }
337
+}
338
+
339
 int main(int argc, char **argv)
340
 {
341
    int r = EXIT_SUCCESS;
342
@@ -1249,7 +1409,7 @@
343
    };
344
    struct pwtest_context test_ctx = {
345
        .suites = SPA_LIST_INIT(&test_ctx.suites),
346
-       .timeout = 30,
347
+       .timeout = 15,
348
        .has_iteration_filter = false,
349
    };
350
    enum {
351
@@ -1258,6 +1418,8 @@
352
    } mode = MODE_TEST;
353
    const char *suite_filter = NULL;
354
 
355
+   spa_list_init(&test_ctx.cleanup_pids);
356
+
357
    ctx = &test_ctx;
358
 
359
    while (1) {
360
@@ -1315,6 +1477,8 @@
361
        break;
362
    case MODE_TEST:
363
        setrlimit(RLIMIT_CORE, &((struct rlimit){0, 0}));
364
+       signal(SIGTERM, sigterm_handler);
365
+       signal(SIGINT, sigterm_handler);
366
        r = run_tests(ctx);
367
        break;
368
    }
369
pipewire-0.3.48.tar.gz/test/pwtest.h -> pipewire-0.3.49.tar.gz/test/pwtest.h Changed
13
 
1
@@ -558,6 +558,11 @@
2
  */
3
 void pwtest_mkstemp(char path[PATH_MAX]);
4
 
5
+/**
6
+ * Run a command and wait for it to return.
7
+ */
8
+int pwtest_spawn(const char *file, char *const argv[]);
9
+
10
 
11
 /**
12
  * \}
13
pipewire-0.3.49.tar.gz/test/test-functional.c Added
64
 
1
@@ -0,0 +1,62 @@
2
+/* PipeWire
3
+ *
4
+ * Copyright © 2019 Wim Taymans
5
+ *
6
+ * Permission is hereby granted, free of charge, to any person obtaining a
7
+ * copy of this software and associated documentation files (the "Software"),
8
+ * to deal in the Software without restriction, including without limitation
9
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10
+ * and/or sell copies of the Software, and to permit persons to whom the
11
+ * Software is furnished to do so, subject to the following conditions:
12
+ *
13
+ * The above copyright notice and this permission notice (including the next
14
+ * paragraph) shall be included in all copies or substantial portions of the
15
+ * Software.
16
+ *
17
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
20
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
23
+ * DEALINGS IN THE SOFTWARE.
24
+ */
25
+
26
+
27
+#include "config.h"
28
+
29
+#include "pwtest.h"
30
+
31
+PWTEST(openal_info_test)
32
+{
33
+   int status;
34
+
35
+#ifdef OPENAL_INFO_PATH
36
+   status = pwtest_spawn(OPENAL_INFO_PATH, (char *[]){ "openal-info", NULL });
37
+   pwtest_int_eq(WEXITSTATUS(status), 0);
38
+   return PWTEST_PASS;
39
+#else
40
+   return PWTEST_SKIP;
41
+#endif
42
+}
43
+
44
+PWTEST(pactl_test)
45
+{
46
+   int status;
47
+
48
+#ifdef PACTL_PATH
49
+   status = pwtest_spawn(PACTL_PATH, (char *[]){ "pactl", "info", NULL });
50
+   pwtest_int_eq(WEXITSTATUS(status), 0);
51
+   return PWTEST_PASS;
52
+#else
53
+   return PWTEST_SKIP;
54
+#endif
55
+}
56
+
57
+PWTEST_SUITE(pw_array)
58
+{
59
+   pwtest_add(pactl_test, PWTEST_ARG_DAEMON);
60
+   pwtest_add(openal_info_test, PWTEST_ARG_DAEMON);
61
+
62
+   return PWTEST_PASS;
63
+}
64
pipewire-0.3.48.tar.gz/test/test-lib.c -> pipewire-0.3.49.tar.gz/test/test-lib.c Changed
23
 
1
@@ -49,9 +49,21 @@
2
    return PWTEST_PASS;
3
 }
4
 
5
+PWTEST(init_deinit)
6
+{
7
+   pw_init(0, NULL);
8
+   pw_deinit();
9
+   pw_init(0, NULL);
10
+   pw_init(0, NULL);
11
+   pw_deinit();
12
+   pw_deinit();
13
+   return PWTEST_PASS;
14
+}
15
+
16
 PWTEST_SUITE(properties)
17
 {
18
    pwtest_add(library_version, PWTEST_NOARG);
19
+   pwtest_add(init_deinit, PWTEST_NOARG);
20
 
21
    return PWTEST_PASS;
22
 }
23
pipewire-0.3.48.tar.gz/test/test-loop.c -> pipewire-0.3.49.tar.gz/test/test-loop.c Changed
246
 
1
@@ -44,6 +44,22 @@
2
    int count;
3
 };
4
 
5
+static inline void write_eventfd(int evfd)
6
+{
7
+   uint64_t value = 1;
8
+   ssize_t r = write(evfd, &value, sizeof(value));
9
+   pwtest_errno_ok(r);
10
+   pwtest_int_eq(r, (ssize_t) sizeof(value));
11
+}
12
+
13
+static inline void read_eventfd(int evfd)
14
+{
15
+   uint64_t value = 0;
16
+   ssize_t r = read(evfd, &value, sizeof(value));
17
+   pwtest_errno_ok(r);
18
+   pwtest_int_eq(r, (ssize_t) sizeof(value));
19
+}
20
+
21
 static void on_event(struct spa_source *source)
22
 {
23
    struct data *d = source->data;
24
@@ -86,8 +102,8 @@
25
    pw_loop_add_source(data.l, &data.a->source);
26
    pw_loop_add_source(data.l, &data.b->source);
27
 
28
-   write(data.a->source.fd, &(uint64_t){1}, sizeof(uint64_t));
29
-   write(data.b->source.fd, &(uint64_t){1}, sizeof(uint64_t));
30
+   write_eventfd(data.a->source.fd);
31
+   write_eventfd(data.b->source.fd);
32
 
33
    pw_main_loop_run(data.ml);
34
    pw_main_loop_destroy(data.ml);
35
@@ -102,12 +118,11 @@
36
 {
37
    static bool first = true;
38
    struct data *d = source->data;
39
-   uint64_t val;
40
 
41
    ++d->count;
42
    pwtest_int_lt(d->count, 3);
43
 
44
-   read(source->fd, &val, sizeof(val));
45
+   read_eventfd(source->fd);
46
 
47
    if (first) {
48
        first = false;
49
@@ -146,8 +161,8 @@
50
    pw_loop_add_source(data.l, &data.a->source);
51
    pw_loop_add_source(data.l, &data.b->source);
52
 
53
-   write(data.a->source.fd, &(uint64_t){1}, sizeof(uint64_t));
54
-   write(data.b->source.fd, &(uint64_t){1}, sizeof(uint64_t));
55
+   write_eventfd(data.a->source.fd);
56
+   write_eventfd(data.b->source.fd);
57
 
58
    pw_main_loop_run(data.ml);
59
    pw_main_loop_destroy(data.ml);
60
@@ -165,12 +180,11 @@
61
 {
62
    static bool first = true;
63
    struct data *d = source->data;
64
-   uint64_t val;
65
 
66
    ++d->count;
67
    pwtest_int_lt(d->count, 3);
68
 
69
-   read(source->fd, &val, sizeof(val));
70
+   read_eventfd(source->fd);
71
 
72
    if (first) {
73
        first = false;
74
@@ -216,8 +230,8 @@
75
    pw_loop_add_source(data.l, &data.a->source);
76
    pw_loop_add_source(data.l, &data.b->source);
77
 
78
-   write(data.a->source.fd, &(uint64_t){1}, sizeof(uint64_t));
79
-   write(data.b->source.fd, &(uint64_t){1}, sizeof(uint64_t));
80
+   write_eventfd(data.a->source.fd);
81
+   write_eventfd(data.b->source.fd);
82
 
83
    pw_main_loop_run(data.ml);
84
    pw_main_loop_destroy(data.ml);
85
@@ -227,33 +241,133 @@
86
    return PWTEST_PASS;
87
 }
88
 
89
-PWTEST(thread_loop_destroy_between_poll_and_lock)
90
+static void
91
+on_event_fail_if_called(void *data, int fd, uint32_t mask)
92
+{
93
+   pwtest_fail_if_reached();
94
+}
95
+
96
+struct dmsbd_data {
97
+   struct pw_loop *l;
98
+   struct pw_main_loop *ml;
99
+   struct spa_source *source;
100
+   struct spa_hook hook;
101
+};
102
+
103
+static void dmsbd_after(void *data)
104
+{
105
+   struct dmsbd_data *d = data;
106
+
107
+   pw_loop_destroy_source(d->l, d->source);
108
+   pw_main_loop_quit(d->ml);
109
+}
110
+
111
+static const struct spa_loop_control_hooks dmsbd_hooks = {
112
+   SPA_VERSION_LOOP_CONTROL_HOOKS,
113
+   .after = dmsbd_after,
114
+};
115
+
116
+PWTEST(destroy_managed_source_before_dispatch)
117
 {
118
    pw_init(NULL, NULL);
119
 
120
-   struct pw_thread_loop *thread_loop = pw_thread_loop_new("uaf", NULL);
121
-   pwtest_ptr_notnull(thread_loop);
122
+   struct dmsbd_data data = {0};
123
+
124
+   data.ml = pw_main_loop_new(NULL);
125
+   pwtest_ptr_notnull(data.ml);
126
+
127
+   data.l = pw_main_loop_get_loop(data.ml);
128
+   pwtest_ptr_notnull(data.l);
129
+
130
+   data.source = pw_loop_add_io(data.l, eventfd(0, 0), SPA_IO_IN, true, on_event_fail_if_called, NULL);
131
+   pwtest_ptr_notnull(data.source);
132
 
133
-   struct pw_loop *loop = pw_thread_loop_get_loop(thread_loop);
134
-   pwtest_ptr_notnull(loop);
135
+   pw_loop_add_hook(data.l, &data.hook, &dmsbd_hooks, &data);
136
 
137
-   int evfd = eventfd(0, 0);
138
-   pwtest_errno_ok(evfd);
139
+   write_eventfd(data.source->fd);
140
 
141
-   struct spa_source *source = pw_loop_add_io(loop, evfd, SPA_IO_IN, true, NULL, NULL);
142
-   pwtest_ptr_notnull(source);
143
+   pw_main_loop_run(data.ml);
144
+   pw_main_loop_destroy(data.ml);
145
 
146
-   pw_thread_loop_start(thread_loop);
147
+   pw_deinit();
148
 
149
-   pw_thread_loop_lock(thread_loop);
150
-   {
151
-       write(evfd, &(uint64_t){1}, sizeof(uint64_t));
152
-       sleep(1);
153
-       pw_loop_destroy_source(loop, source);
154
+   return PWTEST_PASS;
155
+}
156
+
157
+struct dmsbd_recurse_data {
158
+   struct pw_loop *l;
159
+   struct pw_main_loop *ml;
160
+   struct spa_source *a, *b;
161
+   struct spa_hook hook;
162
+   bool first;
163
+};
164
+
165
+static void dmsbd_recurse_on_event(void *data, int fd, uint32_t mask)
166
+{
167
+   struct dmsbd_recurse_data *d = data;
168
+
169
+   read_eventfd(fd);
170
+
171
+   pw_loop_enter(d->l);
172
+   pw_loop_iterate(d->l, 0);
173
+   pw_loop_leave(d->l);
174
+
175
+   pw_main_loop_quit(d->ml);
176
+}
177
+
178
+static void dmswp_recurse_before(void *data)
179
+{
180
+   struct dmsbd_recurse_data *d = data;
181
+
182
+   if (d->first) {
183
+       write_eventfd(d->a->fd);
184
+       write_eventfd(d->b->fd);
185
    }
186
-   pw_thread_loop_unlock(thread_loop);
187
+}
188
+
189
+static void dmsbd_recurse_after(void *data)
190
+{
191
+   struct dmsbd_recurse_data *d = data;
192
+
193
+   if (d->first) {
194
+       pw_loop_destroy_source(d->l, d->b);
195
+
196
+       d->first = false;
197
+   }
198
+}
199
+
200
+static const struct spa_loop_control_hooks dmsbd_recurse_hooks = {
201
+   SPA_VERSION_LOOP_CONTROL_HOOKS,
202
+   .before = dmswp_recurse_before,
203
+   .after = dmsbd_recurse_after,
204
+};
205
+
206
+PWTEST(destroy_managed_source_before_dispatch_recurse)
207
+{
208
+   pw_init(NULL, NULL);
209
+
210
+   struct dmsbd_recurse_data data = {
211
+       .first = true,
212
+   };
213
+
214
+   data.ml = pw_main_loop_new(NULL);
215
+   pwtest_ptr_notnull(data.ml);
216
+
217
+   data.l = pw_main_loop_get_loop(data.ml);
218
+   pwtest_ptr_notnull(data.l);
219
+
220
+   data.l = pw_main_loop_get_loop(data.ml);
221
+   pwtest_ptr_notnull(data.l);
222
 
223
-   pw_thread_loop_destroy(thread_loop);
224
+   data.a = pw_loop_add_io(data.l, eventfd(0, 0), SPA_IO_IN, true, dmsbd_recurse_on_event, &data);
225
+   data.b = pw_loop_add_io(data.l, eventfd(0, 0), SPA_IO_IN, true, on_event_fail_if_called, NULL);
226
+   pwtest_ptr_notnull(data.a);
227
+   pwtest_ptr_notnull(data.b);
228
+
229
+   pw_loop_add_hook(data.l, &data.hook, &dmsbd_recurse_hooks, &data);
230
+
231
+   pw_main_loop_run(data.ml);
232
+   pw_main_loop_destroy(data.ml);
233
 
234
    pw_deinit();
235
 
236
@@ -265,7 +379,8 @@
237
    pwtest_add(pwtest_loop_destroy2, PWTEST_NOARG);
238
    pwtest_add(pwtest_loop_recurse1, PWTEST_NOARG);
239
    pwtest_add(pwtest_loop_recurse2, PWTEST_NOARG);
240
-   pwtest_add(thread_loop_destroy_between_poll_and_lock, PWTEST_NOARG);
241
+   pwtest_add(destroy_managed_source_before_dispatch, PWTEST_NOARG);
242
+   pwtest_add(destroy_managed_source_before_dispatch_recurse, PWTEST_NOARG);
243
 
244
    return PWTEST_PASS;
245
 }
246
pipewire-0.3.48.tar.gz/test/test-spa-json.c -> pipewire-0.3.49.tar.gz/test/test-spa-json.c Changed
83
 
1
@@ -22,6 +22,8 @@
2
  * DEALINGS IN THE SOFTWARE.
3
  */
4
 
5
+#include <locale.h>
6
+
7
 #include "pwtest.h"
8
 
9
 #include <spa/utils/defs.h>
10
@@ -223,6 +225,63 @@
11
    return PWTEST_PASS;
12
 }
13
 
14
+PWTEST(json_float)
15
+{
16
+   struct {
17
+       const char *str;
18
+       double val;
19
+   } val[] = {
20
+       { "0.0", 0.0f },
21
+       { ".0", 0.0f },
22
+       { ".0E0", 0.0E0f },
23
+       { "1.0", 1.0f },
24
+       { "1.011", 1.011f },
25
+       { "176543.123456", 176543.123456f },
26
+       { "-176543.123456", -176543.123456f },
27
+       { "-5678.5432E10", -5678.5432E10f },
28
+       { "-5678.5432e10", -5678.5432e10f },
29
+       { "-5678.5432e-10", -5678.5432e-10f },
30
+       { "5678.5432e+10", 5678.5432e+10f },
31
+       { "00.000100", 00.000100f },
32
+       { "-0.000100", -0.000100f },
33
+   };
34
+   float v;
35
+   unsigned i;
36
+   char buf1[128], buf2[128], *b1 = buf1, *b2 = buf2;
37
+
38
+   pwtest_int_eq(spa_json_parse_float("", 0, &v), 0);
39
+
40
+   setlocale(LC_NUMERIC, "C");
41
+   for (i = 0; i < SPA_N_ELEMENTS(val); i++) {
42
+       pwtest_int_gt(spa_json_parse_float(val[i].str, strlen(val[i].str), &v), 0);
43
+       pwtest_double_eq(v, val[i].val);
44
+   }
45
+   setlocale(LC_NUMERIC, "fr_FR");
46
+   for (i = 0; i < SPA_N_ELEMENTS(val); i++) {
47
+       pwtest_int_gt(spa_json_parse_float(val[i].str, strlen(val[i].str), &v), 0);
48
+       pwtest_double_eq(v, val[i].val);
49
+   }
50
+   pwtest_ptr_eq(spa_json_format_float(buf1, sizeof(buf1), 0.0f), b1);
51
+   pwtest_str_eq(buf1, "0.000000");
52
+   pwtest_ptr_eq(spa_json_format_float(buf1, sizeof(buf1), NAN), b1);
53
+   pwtest_str_eq(buf1, "0.000000");
54
+   pwtest_ptr_eq(spa_json_format_float(buf1, sizeof(buf1), INFINITY), b1);
55
+   pwtest_ptr_eq(spa_json_format_float(buf2, sizeof(buf2), FLT_MAX), b2);
56
+   pwtest_str_eq(buf1, buf2);
57
+   pwtest_ptr_eq(spa_json_format_float(buf1, sizeof(buf1), -INFINITY), b1);
58
+   pwtest_ptr_eq(spa_json_format_float(buf2, sizeof(buf2), FLT_MIN), b2);
59
+   pwtest_str_eq(buf1, buf2);
60
+
61
+   return PWTEST_PASS;
62
+}
63
+
64
+PWTEST(json_int)
65
+{
66
+   int v;
67
+   pwtest_int_eq(spa_json_parse_int("", 0, &v), 0);
68
+   return PWTEST_PASS;
69
+}
70
+
71
 PWTEST_SUITE(spa_json)
72
 {
73
    pwtest_add(json_abi, PWTEST_NOARG);
74
@@ -230,6 +289,8 @@
75
    pwtest_add(json_encode, PWTEST_NOARG);
76
    pwtest_add(json_array, PWTEST_NOARG);
77
    pwtest_add(json_overflow, PWTEST_NOARG);
78
+   pwtest_add(json_float, PWTEST_NOARG);
79
+   pwtest_add(json_int, PWTEST_NOARG);
80
 
81
    return PWTEST_PASS;
82
 }
83
Refresh

No build results available

Refresh

No rpmlint results available

Request History
Bjørn Lie's avatar

zaitor created request about 3 years ago

New upstream release


Bjørn Lie's avatar

zaitor accepted request about 3 years ago

Xin