Overview

Request 5854 (accepted)

New upstream rel

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
+Sun Oct  8 16:26:36 UTC 2023 - Bjørn Lie <zaitor@opensuse.org>
4
+
5
+- Update to version 0.3.81
6
+
7
+-------------------------------------------------------------------
8
 Fri Sep 22 17:21:32 UTC 2023 - Bjørn Lie <zaitor@opensuse.org>
9
 
10
 - Update to version 0.3.80
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.80
6
+Version:        0.3.81
7
 Release:        0
8
 Summary:        PipeWire Bluetooth aptX codec plugin
9
 License:        MIT
10
pipewire-0.3.80.tar.gz/.gitlab-ci.yml -> pipewire-0.3.81.tar.gz/.gitlab-ci.yml Changed
18
 
1
@@ -458,8 +458,15 @@
2
   extends:
3
     - .build_on_fedora
4
   stage: analysis
5
+  variables:
6
+    MESON_OPTIONS: >-
7
+        -Dpipewire-v4l2=enabled
8
+        -Dpipewire-jack=enabled
9
   script:
10
-    - shellcheck $(git grep -l "#\!/.*bin/.*sh")
11
+    - echo "Configuring with meson options $MESON_OPTIONS"
12
+    - meson setup "$BUILD_DIR" --prefix="$PREFIX" $MESON_OPTIONS
13
+    - shellcheck $(git ls-files '*.sh')
14
+    - shellcheck $(grep -rl "#\!/.*bin/.*sh" "$BUILD_DIR")
15
 
16
 spellcheck:
17
   extends:
18
pipewire-0.3.80.tar.gz/NEWS -> pipewire-0.3.81.tar.gz/NEWS Changed
100
 
1
@@ -1,3 +1,88 @@
2
+# PipeWire 0.3.81 (2023-10-06)
3
+
4
+This is the first 1.0 release candidate that is API and ABI compatible
5
+with previous 0.3.x releases.
6
+
7
+## Highlights
8
+  - jackdbus support is now enabled by default.
9
+  - IRQ based scheduling in ALSA was improved and enabled by default for
10
+    Pro-Audio profile. It will also link the pcms together to get lower
11
+    latency. This now matches what JACK does and gives equal latency to
12
+    PipeWire for Pro-Audio profiles.
13
+  - Support both old and new versions of webrtc-audio-processing to make
14
+    the transition easier.
15
+  - Forced quantum changes by nodes or metadata will now also force a
16
+    suspend and resume of the graph, like the rate changes to make sure all
17
+    nodes adapt to the new quantum. This is important for Pro-Audio nodes
18
+    that need to reconfigure the hardware to a new period in IRQ based
19
+    scheduling.
20
+  - Fix a regression in regex parsing.
21
+  - Many bugfixes and improvements.
22
+
23
+
24
+## PipeWire
25
+  - jackdbus is by default enabled now. The idea is that when jackdbus is
26
+    installed, the real libjack.so is in the path and we can become a
27
+    real JACK client.
28
+  - Forces quantum changes by nodes or metadata will now also force a
29
+    suspend and resume in the graph, like the rate changes to make sure all
30
+    nodes adapt to the new quantum. This is important for Pro-Audio nodes
31
+    that need to reconfigure the hardware to a new period.
32
+  - The stream now has an EARLY_PROCESS option that can be used to implement
33
+    custum buffer fill levels. (#3480)
34
+  - Fix a regression in regex parsing. (#3528)
35
+  - Fix a bug in position reporting in the driver node. (#3189) (#3544)
36
+  - Destroying a link will now recalculate the graph correctly.
37
+  - Fix the rate comparison for finding the best rate in the graph.
38
+  - Use malloc_trim() when available to release memory. (#1840)
39
+
40
+## Tools
41
+  - pw-cat now supports DFF DSD files.
42
+  - pw-cli avoid some NULL derefs in some cases.
43
+
44
+## Modules
45
+  - The RAOP sink has seen some cleanups and improvements. It will now ask
46
+    for feedback every 2 seconds to keep some devices alive.
47
+  - A bug in filter-chain was fixed where it would fail to apply the gain
48
+    when mixing just one source.
49
+  - The filter-chain can now pass the stream volume to a control in the
50
+    filter-chain graph. (#3434)
51
+  - Improve volume handling in RAOP sink.
52
+
53
+## Pulse-server
54
+  - Some cleanup in the pending_stream handling.
55
+  - Fix a regression in the event emission code where it failed to emit
56
+    a changed event when a node was linked. (#3522)
57
+  - Lower the realtime priority of pulseaudio clients.
58
+  - Set pulse.module.id on the echo-cancel streams. (#3541)
59
+
60
+## SPA
61
+  - Support both old and new versions of webrtc-audio-processing to make
62
+    the transition easier.
63
+  - The ALSA driver now does the sync of all followers directly from the
64
+    wakeup event. This results in more stable rate matching.
65
+  - IRQ based scheduling in ALSA was improved and enabled by default for
66
+    Pro-Audio profile. It will also link the pcms together to get lower
67
+    latency. This now matches what JACK does and gives equal latency to
68
+    PipeWire for Pro-Audio profiles.
69
+  - GNU/Hurd support was added.
70
+  - Some improvements to passthrough handling.
71
+
72
+## Bluetooth
73
+  - Improvements to the codec handling when PipeWire is used as Audio
74
+    Gateway.
75
+  - Adapt to new Bluez API for BAP devices.
76
+
77
+## JACK
78
+  - When the jack library is set in the default library path, avoid using
79
+    LD_LIBRARY_PATH because this can cause confusion.
80
+  - Handle clearing the latency on a port.
81
+  - jack_property now always manages to actually change the metadata because
82
+    it waits for a roundtrip before exiting.
83
+
84
+Older versions:
85
+
86
+
87
 # PipeWire 0.3.80 (2023-09-14)
88
 
89
 This is a bugfix release that is API and ABI compatible with previous
90
@@ -63,9 +148,6 @@
91
   - The mixer io areas are updated and handled safely now to avoid
92
     crashes. (#3506)
93
 
94
-Older versions:
95
-
96
-
97
 # PipeWire 0.3.79 (2023-08-29)
98
 
99
 This is a quick bugfix release that is API and ABI compatible with previous
100
pipewire-0.3.80.tar.gz/doc/input-filter-h.sh -> pipewire-0.3.81.tar.gz/doc/input-filter-h.sh Changed
9
 
1
@@ -11,6 +11,7 @@
2
 # Add \ingroup commands for the file, for each \addgroup in it
3
 BASEFILE=$(echo "$FILENAME" | sed -e 's@.*src/pipewire/@pipewire/@; s@.*spa/include/spa/@spa/@; s@.*src/test/@test/@;')
4
 
5
+# shellcheck disable=SC2028 # \file is not an escape sequence
6
 echo "/** \file"
7
 echo "\`$BASEFILE\`"
8
 sed -n -e '/.*\\addtogroup a-zA-Z0-9_.*/ { s/.*addtogroup /\\ingroup /; p; }' < "$FILENAME" | sort | uniq
9
pipewire-0.3.80.tar.gz/meson.build -> pipewire-0.3.81.tar.gz/meson.build Changed
57
 
1
@@ -1,5 +1,5 @@
2
 project('pipewire', 'c' ,
3
-  version : '0.3.80',
4
+  version : '0.3.81',
5
   license :  'MIT', 'LGPL-2.1-or-later', 'GPL-2.0-only' ,
6
   meson_version : '>= 0.61.1',
7
   default_options :  'warning_level=3',
8
@@ -23,15 +23,15 @@
9
 spaversion = '0.2'
10
 apiversion = '0.3'
11
 soversion = 0
12
-libversion = '@0@.@1@.0'.format(soversion, pipewire_version_minor.to_int() * 100 + pipewire_version_micro.to_int())
13
+libversion_minor = pipewire_version_major.to_int() * 1000 + pipewire_version_minor.to_int() * 100 + pipewire_version_micro.to_int()
14
+libversion = '@0@.@1@.0'.format(soversion, libversion_minor)
15
 
16
 # LADI/jack
17
 # 3, for PipeWire being the third JACK implementation, after JACK1 and jackdmp/JACK2)
18
 jack_version_major = 3
19
-jack_version_minor = pipewire_version_minor.to_int() * 100 + pipewire_version_micro.to_int()
20
+jack_version_minor = libversion_minor
21
 # libjackserver version has 0 for major (for compatibility with other implementations),
22
-# 3 for minor, and "100*pipewire_version_minor + pipewire_version_micro"
23
-# as micro version (the minor libpipewire soversion number)
24
+# 3 for minor, and "1000*major + 100*minor + micro" as micro version (the minor libpipewire soversion number)
25
 libjackversion = '@0@.@1@.@2@'.format(soversion, jack_version_major, jack_version_minor)
26
 
27
 pipewire_name = 'pipewire-@0@'.format(apiversion)
28
@@ -377,9 +377,17 @@
29
 
30
 webrtc_dep = dependency('webrtc-audio-processing-1',
31
   version : '>= 1.2' ,
32
-  required : get_option('echo-cancel-webrtc'))
33
-summary({'WebRTC Echo Canceling': webrtc_dep.found()}, bool_yn: true, section: 'Misc dependencies')
34
-cdata.set('HAVE_WEBRTC', webrtc_dep.found())
35
+  required : false)
36
+cdata.set('HAVE_WEBRTC1', webrtc_dep.found())
37
+if webrtc_dep.found()
38
+  summary({'WebRTC Echo Canceling >= 1.2': webrtc_dep.found()}, bool_yn: true, section: 'Misc dependencies')
39
+else
40
+  webrtc_dep = dependency('webrtc-audio-processing',
41
+    version : '>= 0.2', '< 1.0',
42
+    required : get_option('echo-cancel-webrtc'))
43
+  cdata.set('HAVE_WEBRTC', webrtc_dep.found())
44
+  summary({'WebRTC Echo Canceling < 1.0': webrtc_dep.found()}, bool_yn: true, section: 'Misc dependencies')
45
+endif
46
 
47
 # On FreeBSD and MidnightBSD, epoll-shim library is required for eventfd() and timerfd()
48
 epoll_shim_dep = (host_machine.system() == 'freebsd' or host_machine.system() == 'midnightbsd'
49
@@ -427,6 +435,7 @@
50
   'reallocarray', '#include <stdlib.h>', '-D_GNU_SOURCE', ,
51
   'sigabbrev_np', '#include <string.h>', '-D_GNU_SOURCE', ,
52
   'XSetIOErrorExitHandler', '#include <X11/Xlib.h>', , x11_dep,
53
+  'malloc_trim', '#include <malloc.h>', , ,
54
 
55
 
56
 foreach f : check_functions
57
pipewire-0.3.80.tar.gz/pipewire-jack/src/meson.build -> pipewire-0.3.81.tar.gz/pipewire-jack/src/meson.build Changed
21
 
1
@@ -21,12 +21,19 @@
2
 if libjack_path == ''
3
   libjack_path = modules_install_dir / 'jack'
4
   libjack_path_dlopen = modules_install_dir_dlopen / 'jack'
5
+  libjack_path_enable = ''
6
+elif libjack_path == get_option('libdir') or libjack_path == pipewire_libdir
7
+  libjack_path = pipewire_libdir
8
+  libjack_path_dlopen = libjack_path
9
+  libjack_path_enable = '#'
10
 else
11
   libjack_path_dlopen = libjack_path
12
+  libjack_path_enable = ''
13
 endif
14
 
15
 tools_config = configuration_data()
16
 tools_config.set('LIBJACK_PATH', libjack_path_dlopen)
17
+tools_config.set('LIBJACK_PATH_ENABLE', libjack_path_enable)
18
 
19
 configure_file(input : 'pw-jack.in',
20
                output : 'pw-jack',
21
pipewire-0.3.80.tar.gz/pipewire-jack/src/metadata.c -> pipewire-0.3.81.tar.gz/pipewire-jack/src/metadata.c Changed
47
 
1
@@ -230,7 +230,7 @@
2
    pw_log_info("set id:%u (%"PRIu64") '%s' to '%s@%s'", o->id, subject, key, value, type);
3
    if (update_property(c, subject, key, type, value))
4
        pw_metadata_set_property(c->metadata->proxy, o->id, key, type, value);
5
-   res = 0;
6
+   res = do_sync(c);
7
 done:
8
    pw_thread_loop_unlock(c->context.loop);
9
 
10
@@ -340,7 +340,7 @@
11
    pw_log_info("remove id:%u (%"PRIu64") '%s'", id, subject, key);
12
    pw_metadata_set_property(c->metadata->proxy,
13
            id, key, NULL, NULL);
14
-   res = 0;
15
+   res = do_sync(c);
16
 done:
17
    pw_thread_loop_unlock(c->context.loop);
18
 
19
@@ -365,7 +365,7 @@
20
    pw_log_info("remove id:%u (%"PRIu64")", id, subject);
21
    pw_metadata_set_property(c->metadata->proxy,
22
            id, NULL, NULL, NULL);
23
-   res = 0;
24
+   res = do_sync(c);
25
 done:
26
    pw_thread_loop_unlock(c->context.loop);
27
 
28
@@ -375,15 +375,17 @@
29
 SPA_EXPORT
30
 int jack_remove_all_properties (jack_client_t* client)
31
 {
32
+   int res;
33
    struct client *c = (struct client *) client;
34
 
35
    spa_return_val_if_fail(c != NULL, -EINVAL);
36
 
37
    pw_thread_loop_lock(c->context.loop);
38
    pw_metadata_clear(c->metadata->proxy);
39
+   res = do_sync(c);
40
    pw_thread_loop_unlock(c->context.loop);
41
 
42
-   return 0;
43
+   return res;
44
 }
45
 
46
 SPA_EXPORT
47
pipewire-0.3.80.tar.gz/pipewire-jack/src/pipewire-jack.c -> pipewire-0.3.81.tar.gz/pipewire-jack/src/pipewire-jack.c Changed
24
 
1
@@ -2441,9 +2441,11 @@
2
    int res;
3
 
4
    if (param == NULL)
5
-       return 0;
6
-   if ((res = spa_latency_parse(param, &info)) < 0)
7
+       info = SPA_LATENCY_INFO(SPA_DIRECTION_REVERSE(p->direction));
8
+   else if ((res = spa_latency_parse(param, &info)) < 0)
9
        return res;
10
+   if (info.direction == p->direction)
11
+       return 0;
12
 
13
    current = &p->object->port.latencyinfo.direction;
14
    if (spa_latency_info_compare(current, &info) == 0)
15
@@ -2457,8 +2459,6 @@
16
            info.min_rate, info.max_rate,
17
            info.min_ns, info.max_ns);
18
 
19
-   if (info.direction == p->direction)
20
-       return 0;
21
 
22
    if (info.direction == SPA_DIRECTION_INPUT)
23
        mode = JackPlaybackLatency;
24
pipewire-0.3.80.tar.gz/pipewire-jack/src/pw-jack.in -> pipewire-0.3.81.tar.gz/pipewire-jack/src/pw-jack.in Changed
13
 
1
@@ -52,7 +52,9 @@
2
    fi
3
    export PIPEWIRE_QUANTUM
4
 fi
5
-LD_LIBRARY_PATH='@LIBJACK_PATH@'"${LD_LIBRARY_PATH+":$LD_LIBRARY_PATH"}"
6
-export LD_LIBRARY_PATH
7
+
8
+# shellcheck disable=SC2016 # ${LIB} is interpreted by ld.so, not the shell
9
+@LIBJACK_PATH_ENABLE@LD_LIBRARY_PATH='@LIBJACK_PATH@'"${LD_LIBRARY_PATH+":$LD_LIBRARY_PATH"}"
10
+@LIBJACK_PATH_ENABLE@export LD_LIBRARY_PATH
11
 
12
 exec "$@"
13
pipewire-0.3.80.tar.gz/pipewire-v4l2/src/pw-v4l2.in -> pipewire-0.3.81.tar.gz/pipewire-v4l2/src/pw-v4l2.in Changed
9
 
1
@@ -37,6 +37,7 @@
2
 if  "$PW_UNINSTALLED" = 1  ; then
3
    PW_V4L2_LD_PRELOAD="$PW_BUILDDIR"'/pipewire-v4l2/src/libpw-v4l2.so'
4
 else
5
+   # shellcheck disable=SC2016 # ${LIB} is interpreted by ld.so, not the shell
6
    PW_V4L2_LD_PRELOAD='@LIBV4L2_PATH@/libpw-v4l2.so'
7
 fi
8
 
9
pipewire-0.3.80.tar.gz/spa/examples/adapter-control.c -> pipewire-0.3.81.tar.gz/spa/examples/adapter-control.c Changed
13
 
1
@@ -906,9 +906,9 @@
2
        printf("got error %d\n", res);
3
 }
4
 
5
-static char *getscale(uint32_t scale)
6
+static const char *getscale(uint32_t scale)
7
 {
8
-   char *scale_s = NULL;
9
+   const char *scale_s = NULL;
10
 
11
    if (scale == SPA_AUDIO_VOLUME_RAMP_LINEAR)
12
        scale_s = LINEAR;
13
pipewire-0.3.80.tar.gz/spa/include/spa/debug/log.h -> pipewire-0.3.81.tar.gz/spa/include/spa/debug/log.h Changed
12
 
1
@@ -15,6 +15,10 @@
2
 #include <spa/utils/defs.h>
3
 #include <spa/support/log.h>
4
 #include <spa/debug/context.h>
5
+#include <spa/debug/dict.h>
6
+#include <spa/debug/format.h>
7
+#include <spa/debug/mem.h>
8
+#include <spa/debug/pod.h>
9
 
10
 /**
11
  * \addtogroup spa_debug
12
pipewire-0.3.80.tar.gz/spa/include/spa/utils/cleanup.h -> pipewire-0.3.81.tar.gz/spa/include/spa/utils/cleanup.h Changed
47
 
1
@@ -35,15 +35,16 @@
2
 
3
 #define spa_exchange(var, new_value) \
4
 __extension__ ({ \
5
-   __typeof__(var) _old_value = (var); \
6
-   (var) = (new_value); \
7
+   __typeof__(var) *_ptr = &(var); \
8
+   __typeof__(var) _old_value = *_ptr; \
9
+   *_ptr = (new_value); \
10
    _old_value; \
11
 })
12
 
13
-#if __GNUC__ > 10 || defined(__clang__)
14
+#if __GNUC__ >= 10 || defined(__clang__)
15
 #define spa_steal_ptr(ptr) ((__typeof__(*(ptr)) *) spa_exchange((ptr), NULL))
16
 #else
17
-#define spa_steal_ptr(ptr) ((__typeof__(ptr)) spa_exchange((ptr), NULL))
18
+#define spa_steal_ptr(ptr) spa_exchange((ptr), NULL)
19
 #endif
20
 
21
 #define spa_steal_fd(fd) spa_exchange((fd), -1)
22
@@ -52,16 +53,6 @@
23
 
24
 #include <stdlib.h>
25
 
26
-
27
-#if __GNUC__ > 10 || defined(__clang__)
28
-#define spa_clear_ptr(ptr, destructor) \
29
-__extension__ ({ \
30
-   __typeof__(*(ptr)) *_old_value = spa_steal_ptr(ptr); \
31
-   if (_old_value) \
32
-       destructor(_old_value); \
33
-   (void) 0; \
34
-})
35
-#else
36
 #define spa_clear_ptr(ptr, destructor) \
37
 __extension__ ({ \
38
    __typeof__(ptr) _old_value = spa_steal_ptr(ptr); \
39
@@ -69,7 +60,6 @@
40
        destructor(_old_value); \
41
    (void) 0; \
42
 })
43
-#endif
44
 
45
 static inline void _spa_autofree_cleanup_func(void *p)
46
 {
47
pipewire-0.3.80.tar.gz/spa/plugins/aec/aec-webrtc.cpp -> pipewire-0.3.81.tar.gz/spa/plugins/aec/aec-webrtc.cpp Changed
196
 
1
@@ -3,6 +3,8 @@
2
 /* SPDX-FileCopyrightText: Copyright © 2021 Arun Raghavan <arun@asymptotic.io> */
3
 /* SPDX-License-Identifier: MIT */
4
 
5
+#include "config.h"
6
+
7
 #include <memory>
8
 #include <utility>
9
 
10
@@ -13,7 +15,13 @@
11
 #include <spa/utils/json.h>
12
 #include <spa/support/plugin.h>
13
 
14
+#ifdef HAVE_WEBRTC
15
+#include <webrtc/modules/audio_processing/include/audio_processing.h>
16
+#include <webrtc/modules/interface/module_common_types.h>
17
+#include <webrtc/system_wrappers/include/trace.h>
18
+#else
19
 #include <modules/audio_processing/include/audio_processing.h>
20
+#endif
21
 
22
 struct impl_data {
23
    struct spa_handle handle;
24
@@ -39,6 +47,54 @@
25
    return default_value;
26
 }
27
 
28
+#ifdef HAVE_WEBRTC
29
+/*  f0 f1 f2  */
30
+static int parse_point(struct spa_json *it, float (&f)3)
31
+{
32
+   struct spa_json arr;
33
+   int i, res;
34
+
35
+   if (spa_json_enter_array(it, &arr) <= 0)
36
+       return -EINVAL;
37
+
38
+   for (i = 0; i < 3; i++) {
39
+       if ((res = spa_json_get_float(&arr, &fi)) <= 0)
40
+           return -EINVAL;
41
+   }
42
+   return 0;
43
+}
44
+
45
+/*  point1 point2 ...  */
46
+static int parse_mic_geometry(struct impl_data *impl, const char *mic_geometry,
47
+       std::vector<webrtc::Point>& geometry)
48
+{
49
+   int res;
50
+   size_t i;
51
+   struct spa_json it2;
52
+
53
+   spa_json_init(&it0, mic_geometry, strlen(mic_geometry));
54
+   if (spa_json_enter_array(&it0, &it1) <= 0) {
55
+       spa_log_error(impl->log, "Error: webrtc.mic-geometry expects an array");
56
+       return -EINVAL;
57
+   }
58
+
59
+   for (i = 0; i < geometry.size(); i++) {
60
+       float f3;
61
+
62
+       if ((res = parse_point(&it1, f)) < 0) {
63
+           spa_log_error(impl->log, "Error: can't parse webrtc.mic-geometry points: %d", res);
64
+           return res;
65
+       }
66
+
67
+       spa_log_info(impl->log, "mic %zd position: (%g %g %g)", i, f0, f1, f2);
68
+       geometryi.c0 = f0;
69
+       geometryi.c1 = f1;
70
+       geometryi.c2 = f2;
71
+   }
72
+   return 0;
73
+}
74
+#endif
75
+
76
 static int webrtc_init2(void *object, const struct spa_dict *args,
77
        struct spa_audio_info_raw *rec_info, struct spa_audio_info_raw *out_info,
78
        struct spa_audio_info_raw *play_info)
79
@@ -48,9 +104,18 @@
80
 
81
    bool high_pass_filter = webrtc_get_spa_bool(args, "webrtc.high_pass_filter", true);
82
    bool noise_suppression = webrtc_get_spa_bool(args, "webrtc.noise_suppression", true);
83
-   bool transient_suppression = webrtc_get_spa_bool(args, "webrtc.transient_suppression", true);
84
    bool voice_detection = webrtc_get_spa_bool(args, "webrtc.voice_detection", true);
85
-
86
+#ifdef HAVE_WEBRTC
87
+   bool extended_filter = webrtc_get_spa_bool(args, "webrtc.extended_filter", true);
88
+   bool delay_agnostic = webrtc_get_spa_bool(args, "webrtc.delay_agnostic", true);
89
+   // Disable experimental flags by default
90
+   bool experimental_agc = webrtc_get_spa_bool(args, "webrtc.experimental_agc", false);
91
+   bool experimental_ns = webrtc_get_spa_bool(args, "webrtc.experimental_ns", false);
92
+
93
+   bool beamforming = webrtc_get_spa_bool(args, "webrtc.beamforming", false);
94
+#else
95
+   bool transient_suppression = webrtc_get_spa_bool(args, "webrtc.transient_suppression", true);
96
+#endif
97
    // Note: AGC seems to mess up with Agnostic Delay Detection, especially with speech,
98
    // result in very poor performance, disable by default
99
    bool gain_control = webrtc_get_spa_bool(args, "webrtc.gain_control", false);
100
@@ -59,6 +124,51 @@
101
    // This filter will modify playback buffer (when calling ProcessReverseStream), but now
102
    // playback buffer modifications are discarded.
103
 
104
+#ifdef HAVE_WEBRTC
105
+   webrtc::Config config;
106
+   config.Set<webrtc::ExtendedFilter>(new webrtc::ExtendedFilter(extended_filter));
107
+   config.Set<webrtc::DelayAgnostic>(new webrtc::DelayAgnostic(delay_agnostic));
108
+   config.Set<webrtc::ExperimentalAgc>(new webrtc::ExperimentalAgc(experimental_agc));
109
+   config.Set<webrtc::ExperimentalNs>(new webrtc::ExperimentalNs(experimental_ns));
110
+
111
+   if (beamforming) {
112
+       std::vector<webrtc::Point> geometry(rec_info->channels);
113
+       const char *mic_geometry, *target_direction;
114
+
115
+       /* The beamformer gives a single mono channel */
116
+       out_info->channels = 1;
117
+       out_info->position0 = SPA_AUDIO_CHANNEL_MONO;
118
+
119
+       if ((mic_geometry = spa_dict_lookup(args, "webrtc.mic-geometry")) == NULL) {
120
+           spa_log_error(impl->log, "Error: webrtc.beamforming requires webrtc.mic-geometry");
121
+           return -EINVAL;
122
+       }
123
+
124
+       if ((res = parse_mic_geometry(impl, mic_geometry, geometry)) < 0)
125
+           return res;
126
+
127
+       if ((target_direction = spa_dict_lookup(args, "webrtc.target-direction")) != NULL) {
128
+           webrtc::SphericalPointf direction(0.0f, 0.0f, 0.0f);
129
+           struct spa_json it;
130
+           float f3;
131
+
132
+           spa_json_init(&it, target_direction, strlen(target_direction));
133
+           if (parse_point(&it, f) < 0) {
134
+               spa_log_error(impl->log, "Error: can't parse target-direction %s",
135
+                       target_direction);
136
+               return -EINVAL;
137
+           }
138
+
139
+           direction.s0 = f0;
140
+           direction.s1 = f1;
141
+           direction.s2 = f2;
142
+
143
+           config.Set<webrtc::Beamforming>(new webrtc::Beamforming(true, geometry, direction));
144
+       } else {
145
+           config.Set<webrtc::Beamforming>(new webrtc::Beamforming(true, geometry));
146
+       }
147
+   }
148
+#else
149
    webrtc::AudioProcessing::Config config;
150
    config.echo_canceller.enabled = true;
151
    // FIXME: Example code enables both gain controllers, but that seems sus
152
@@ -73,6 +183,7 @@
153
    // FIXME: expose pre/postamp gain
154
    config.transient_suppression.enabled = transient_suppression;
155
    config.voice_detection.enabled = voice_detection;
156
+#endif
157
 
158
    webrtc::ProcessingConfig pconfig = {{
159
        webrtc::StreamConfig(rec_info->rate, rec_info->channels, false), /* input stream */
160
@@ -81,15 +192,35 @@
161
        webrtc::StreamConfig(play_info->rate, play_info->channels, false), /* reverse output stream */
162
    }};
163
 
164
+#ifdef HAVE_WEBRTC
165
+   auto apm = std::unique_ptr<webrtc::AudioProcessing>(webrtc::AudioProcessing::Create(config));
166
+#else
167
    auto apm = std::unique_ptr<webrtc::AudioProcessing>(webrtc::AudioProcessingBuilder().Create());
168
 
169
    apm->ApplyConfig(config);
170
+#endif
171
 
172
    if ((res = apm->Initialize(pconfig)) != webrtc::AudioProcessing::kNoError) {
173
        spa_log_error(impl->log, "Error initialising webrtc audio processing module: %d", res);
174
        return -EINVAL;
175
    }
176
 
177
+#ifdef HAVE_WEBRTC
178
+   apm->high_pass_filter()->Enable(high_pass_filter);
179
+   // Always disable drift compensation since PipeWire will already do
180
+   // drift compensation on all sinks and sources linked to this echo-canceler
181
+   apm->echo_cancellation()->enable_drift_compensation(false);
182
+   apm->echo_cancellation()->Enable(true);
183
+   // TODO: wire up supression levels to args
184
+   apm->echo_cancellation()->set_suppression_level(webrtc::EchoCancellation::kHighSuppression);
185
+   apm->noise_suppression()->set_level(webrtc::NoiseSuppression::kHigh);
186
+   apm->noise_suppression()->Enable(noise_suppression);
187
+   apm->voice_detection()->Enable(voice_detection);
188
+   // TODO: wire up AGC parameters to args
189
+   apm->gain_control()->set_analog_level_limits(0, 255);
190
+   apm->gain_control()->set_mode(webrtc::GainControl::kAdaptiveDigital);
191
+   apm->gain_control()->Enable(gain_control);
192
+#endif
193
    impl->apm = std::move(apm);
194
    impl->rec_info = *rec_info;
195
    impl->out_info = *out_info;
196
pipewire-0.3.80.tar.gz/spa/plugins/alsa/acp/acp.c -> pipewire-0.3.81.tar.gz/spa/plugins/alsa/acp/acp.c Changed
19
 
1
@@ -388,6 +388,8 @@
2
                pa_alsa_init_proplist_pcm(NULL, m->output_proplist, m->output_pcm);
3
                pa_proplist_setf(m->output_proplist, "clock.name", "api.alsa.%u", index);
4
                pa_proplist_setf(m->output_proplist, "device.profile.pro", "true");
5
+               pa_proplist_setf(m->output_proplist, "node.group", "pro-audio-%u", index);
6
+               pa_proplist_setf(m->output_proplist, "node.link-group", "pro-audio-%u", index);
7
                pa_alsa_close(&m->output_pcm);
8
                m->supported = true;
9
                pa_channel_map_init_auto(&m->channel_map, m->sample_spec.channels, PA_CHANNEL_MAP_AUX);
10
@@ -419,6 +421,8 @@
11
                pa_alsa_init_proplist_pcm(NULL, m->input_proplist, m->input_pcm);
12
                pa_proplist_setf(m->input_proplist, "clock.name", "api.alsa.%u", index);
13
                pa_proplist_setf(m->input_proplist, "device.profile.pro", "true");
14
+               pa_proplist_setf(m->input_proplist, "node.group", "pro-audio-%u", index);
15
+               pa_proplist_setf(m->input_proplist, "node.link-group", "pro-audio-%u", index);
16
                pa_alsa_close(&m->input_pcm);
17
                m->supported = true;
18
                pa_channel_map_init_auto(&m->channel_map, m->sample_spec.channels, PA_CHANNEL_MAP_AUX);
19
pipewire-0.3.80.tar.gz/spa/plugins/alsa/alsa-compress-offload-sink.c -> pipewire-0.3.81.tar.gz/spa/plugins/alsa/alsa-compress-offload-sink.c Changed
16
 
1
@@ -843,12 +843,12 @@
2
 
3
    nextptr = device + 3;
4
    for (value_index = 0; ; ++value_index) {
5
-       char *value_label;
6
+       const char *value_label;
7
 
8
        switch (value_index) {
9
        case 0: value_label = "card"; break;
10
        case 1: value_label = "device"; break;
11
-       default: assert(false);
12
+       default: spa_assert_not_reached();
13
        }
14
 
15
        errno = 0;
16
pipewire-0.3.80.tar.gz/spa/plugins/alsa/alsa-pcm-sink.c -> pipewire-0.3.81.tar.gz/spa/plugins/alsa/alsa-pcm-sink.c Changed
15
 
1
@@ -252,9 +252,13 @@
2
 
3
    switch (id) {
4
    case SPA_IO_Clock:
5
+       if (size > 0 && size < sizeof(struct spa_io_clock))
6
+           return -EINVAL;
7
        this->clock = data;
8
        break;
9
    case SPA_IO_Position:
10
+       if (size > 0 && size < sizeof(struct spa_io_position))
11
+           return -EINVAL;
12
        this->position = data;
13
        break;
14
    default:
15
pipewire-0.3.80.tar.gz/spa/plugins/alsa/alsa-pcm-source.c -> pipewire-0.3.81.tar.gz/spa/plugins/alsa/alsa-pcm-source.c Changed
10
 
1
@@ -235,6 +235,8 @@
2
        this->clock = data;
3
        break;
4
    case SPA_IO_Position:
5
+       if (size > 0 && size < sizeof(struct spa_io_position))
6
+           return -EINVAL;
7
        this->position = data;
8
        break;
9
    default:
10
pipewire-0.3.80.tar.gz/spa/plugins/alsa/alsa-pcm.c -> pipewire-0.3.81.tar.gz/spa/plugins/alsa/alsa-pcm.c Changed
1469
 
1
@@ -17,6 +17,7 @@
2
 #include "alsa-pcm.h"
3
 
4
 static struct spa_list cards = SPA_LIST_INIT(&cards);
5
+static struct spa_list states = SPA_LIST_INIT(&states);
6
 
7
 static struct card *find_card(uint32_t index)
8
 {
9
@@ -501,6 +502,9 @@
10
    int err;
11
    const char *str;
12
 
13
+   spa_list_init(&state->followers);
14
+   spa_list_init(&state->rt.followers);
15
+
16
    snd_config_update_free_global();
17
 
18
    if ((str = spa_dict_lookup(info, "device.profile.pro")) != NULL)
19
@@ -508,6 +512,7 @@
20
 
21
    state->multi_rate = true;
22
    state->htimestamp = false;
23
+   state->disable_tsched = state->is_pro;
24
    for (i = 0; info && i < info->n_items; i++) {
25
        const char *k = info->itemsi.key;
26
        const char *s = info->itemsi.value;
27
@@ -547,6 +552,8 @@
28
    }
29
    CHECK(snd_output_stdio_attach(&state->output, state->log_file, 0), "attach failed");
30
 
31
+   spa_list_append(&states, &state->link);
32
+
33
    state->rate_limit.interval = 2 * SPA_NSEC_PER_SEC;
34
    state->rate_limit.burst = 1;
35
 
36
@@ -557,6 +564,7 @@
37
 {
38
    int err;
39
 
40
+   spa_list_remove(&state->link);
41
    release_card(state->card);
42
 
43
    state->card = NULL;
44
@@ -584,7 +592,7 @@
45
    err = snd_ctl_open(&state->ctl, device_name, SND_CTL_NONBLOCK);
46
    if (err < 0) {
47
        spa_log_info(state->log, "%s could not find ctl device: %s",
48
-               state->props.device, snd_strerror(err));
49
+               device_name, snd_strerror(err));
50
        state->ctl = NULL;
51
        goto error;
52
    }
53
@@ -599,7 +607,7 @@
54
    err = snd_ctl_elem_read(state->ctl, state->pitch_elem);
55
    if (err < 0) {
56
        spa_log_debug(state->log, "%s: did not find ctl %s: %s",
57
-               state->props.device, elem_name, snd_strerror(err));
58
+               device_name, elem_name, snd_strerror(err));
59
 
60
        snd_ctl_elem_value_free(state->pitch_elem);
61
        state->pitch_elem = NULL;
62
@@ -613,13 +621,34 @@
63
    CHECK(snd_ctl_elem_write(state->ctl, state->pitch_elem), "snd_ctl_elem_write");
64
    state->last_rate = 1.0;
65
 
66
-   spa_log_info(state->log, "%s: found ctl %s", state->props.device, elem_name);
67
+   spa_log_info(state->log, "%s: found ctl %s", device_name, elem_name);
68
    err = 0;
69
 error:
70
    snd_lib_error_set_handler(NULL);
71
    return err;
72
 }
73
 
74
+static int do_link(struct state *driver, struct state *state)
75
+{
76
+   int res;
77
+   snd_pcm_status_t *status;
78
+
79
+   snd_pcm_status_alloca(&status);
80
+   snd_pcm_status(driver->hndl, status);
81
+   snd_pcm_status_dump(status, state->output);
82
+   snd_pcm_status(state->hndl, status);
83
+   snd_pcm_status_dump(status, state->output);
84
+   fflush(state->log_file);
85
+
86
+   res = snd_pcm_link(driver->hndl, state->hndl);
87
+   if (res >= 0 || res == -EALREADY)
88
+       state->linked = true;
89
+
90
+   spa_log_info(state->log, "%p: linked to driver %p: %u (%s)",
91
+           state, driver, state->linked, snd_strerror(res));
92
+   return 0;
93
+}
94
+
95
 int spa_alsa_open(struct state *state, const char *params)
96
 {
97
    int err;
98
@@ -632,6 +661,8 @@
99
    spa_scnprintf(device_name, sizeof(device_name), "%s%s%s",
100
            state->card->ucm_prefix ? state->card->ucm_prefix : "",
101
            props->device, params ? params : "");
102
+   spa_scnprintf(state->name, sizeof(state->name), "%s%s",
103
+           props->device, state->stream == SND_PCM_STREAM_CAPTURE ? "c" : "p");
104
 
105
    spa_log_info(state->log, "%p: ALSA device open '%s' %s", state, device_name,
106
            state->stream == SND_PCM_STREAM_CAPTURE ? "capture" : "playback");
107
@@ -655,9 +686,6 @@
108
         * these are initialised in spa_alsa_start() */
109
    }
110
 
111
-   if (state->clock)
112
-       spa_scnprintf(state->clock->name, sizeof(state->clock->name),
113
-               "%s", state->clock_name);
114
    state->opened = true;
115
    state->sample_count = 0;
116
    state->sample_time = 0;
117
@@ -667,12 +695,32 @@
118
    return 0;
119
 
120
 error_exit_close:
121
-   spa_log_info(state->log, "%p: Device '%s' closing: %s", state, state->props.device,
122
+   spa_log_info(state->log, "%p: Device '%s' closing: %s", state, state->name,
123
            spa_strerror(err));
124
    snd_pcm_close(state->hndl);
125
    return err;
126
 }
127
 
128
+static void try_unlink(struct state *state)
129
+{
130
+   struct state *follower;
131
+
132
+   if (state->driver != NULL && state->linked) {
133
+       snd_pcm_unlink(state->hndl);
134
+       spa_log_info(state->log, "%p: unlinked from driver %p",
135
+               state, state->driver);
136
+       state->linked = false;
137
+   }
138
+   spa_list_for_each(follower, &state->followers, driver_link) {
139
+       if (follower->opened && follower->linked) {
140
+           snd_pcm_unlink(follower->hndl);
141
+           spa_log_info(state->log, "%p: follower unlinked from driver %p",
142
+               follower, state);
143
+           follower->linked = false;
144
+       }
145
+   }
146
+}
147
+
148
 int spa_alsa_close(struct state *state)
149
 {
150
    int err = 0;
151
@@ -680,11 +728,13 @@
152
    if (!state->opened)
153
        return 0;
154
 
155
+   try_unlink(state);
156
+
157
    spa_alsa_pause(state);
158
 
159
-   spa_log_info(state->log, "%p: Device '%s' closing", state, state->props.device);
160
+   spa_log_info(state->log, "%p: Device '%s' closing", state, state->name);
161
    if ((err = snd_pcm_close(state->hndl)) < 0)
162
-       spa_log_warn(state->log, "%s: close failed: %s", state->props.device,
163
+       spa_log_warn(state->log, "%s: close failed: %s", state->name,
164
                snd_strerror(err));
165
 
166
    if (!state->disable_tsched)
167
@@ -697,6 +747,7 @@
168
 
169
    state->have_format = false;
170
    state->opened = false;
171
+   state->linked = false;
172
 
173
    if (state->pitch_elem) {
174
        snd_ctl_elem_value_free(state->pitch_elem);
175
@@ -1094,7 +1145,7 @@
176
        CHECK(snd_pcm_hw_params_set_channels_near(hndl, params, &rchannels), "set_channels");
177
        if (state->default_channels != rchannels) {
178
            spa_log_warn(state->log, "%s: Channels doesn't match (requested %u, got %u)",
179
-               state->props.device, state->default_channels, rchannels);
180
+               state->name, state->default_channels, rchannels);
181
        }
182
    }
183
    if (state->default_rate != 0) {
184
@@ -1102,7 +1153,7 @@
185
        CHECK(snd_pcm_hw_params_set_rate_near(hndl, params, &rrate, 0), "set_rate_near");
186
        if (state->default_rate != rrate) {
187
            spa_log_warn(state->log, "%s: Rate doesn't match (requested %u, got %u)",
188
-               state->props.device, state->default_rate, rrate);
189
+               state->name, state->default_rate, rrate);
190
        }
191
    }
192
 
193
@@ -1164,7 +1215,7 @@
194
            }
195
        }
196
        spa_log_warn(state->log, "%s: no format found (def:%d) formats:%s",
197
-               state->props.device, state->default_format, buf);
198
+               state->name, state->default_format, buf);
199
 
200
        for (i = 0, offs = 0; i <= SND_PCM_ACCESS_LAST; i++) {
201
            if (snd_pcm_access_mask_test(amask, (snd_pcm_access_t)i)) {
202
@@ -1175,7 +1226,7 @@
203
                offs += r;
204
            }
205
        }
206
-       spa_log_warn(state->log, "%s: access:%s", state->props.device, buf);
207
+       spa_log_warn(state->log, "%s: access:%s", state->name, buf);
208
        return -ENOTSUP;
209
    }
210
 
211
@@ -1550,7 +1601,7 @@
212
 
213
    if (rformat == SND_PCM_FORMAT_UNKNOWN) {
214
        spa_log_warn(state->log, "%s: unknown format",
215
-               state->props.device);
216
+               state->name);
217
        return -EINVAL;
218
    }
219
 
220
@@ -1588,7 +1639,7 @@
221
                planar ? SND_PCM_ACCESS_RW_NONINTERLEAVED
222
                : SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) {
223
            spa_log_error(state->log, "%s: RW not possible: %s",
224
-                   state->props.device, snd_strerror(err));
225
+                   state->name, snd_strerror(err));
226
            return err;
227
        }
228
    }
229
@@ -1605,7 +1656,7 @@
230
    CHECK(snd_pcm_hw_params_set_channels_near(hndl, params, &val), "set_channels");
231
    if (rchannels != val) {
232
        spa_log_warn(state->log, "%s: Channels doesn't match (requested %u, got %u)",
233
-               state->props.device, rchannels, val);
234
+               state->name, rchannels, val);
235
        if (!SPA_FLAG_IS_SET(flags, SPA_NODE_PARAM_FLAG_NEAREST))
236
            return -EINVAL;
237
        if (fmt->media_subtype != SPA_MEDIA_SUBTYPE_raw)
238
@@ -1628,7 +1679,7 @@
239
    CHECK(snd_pcm_hw_params_set_rate_near(hndl, params, &val, 0), "set_rate_near");
240
    if (rrate != val) {
241
        spa_log_warn(state->log, "%s: Rate doesn't match (requested %iHz, got %iHz)",
242
-               state->props.device, rrate, val);
243
+               state->name, rrate, val);
244
        if (!SPA_FLAG_IS_SET(flags, SPA_NODE_PARAM_FLAG_NEAREST))
245
            return -EINVAL;
246
        if (fmt->media_subtype != SPA_MEDIA_SUBTYPE_raw)
247
@@ -1639,7 +1690,7 @@
248
    }
249
    if (rchannels == 0 || rrate == 0) {
250
        spa_log_error(state->log, "%s: invalid channels:%d or rate:%d",
251
-               state->props.device, rchannels, rrate);
252
+               state->name, rchannels, rrate);
253
        return -EIO;
254
    }
255
 
256
@@ -1655,6 +1706,11 @@
257
    else
258
        state->frame_size *= rchannels;
259
 
260
+   /* make sure we update threshold in check_position_config() because they depend
261
+    * on the samplerate. */
262
+   state->driver_duration = 0;
263
+   state->driver_rate.denom = 0;
264
+
265
    state->have_format = true;
266
    if (state->card->format_ref++ == 0)
267
        state->card->rate = rrate;
268
@@ -1690,7 +1746,7 @@
269
    CHECK(snd_pcm_hw_params_set_period_size_near(hndl, params, &period_size, &dir), "set_period_size_near");
270
 
271
    if (period_size == 0) {
272
-       spa_log_error(state->log, "%s: invalid period_size 0 (driver error?)", state->props.device);
273
+       spa_log_error(state->log, "%s: invalid period_size 0 (driver error?)", state->name);
274
        return -EIO;
275
    }
276
 
277
@@ -1710,7 +1766,7 @@
278
        periods = state->buffer_frames / period_size;
279
    }
280
    if (state->buffer_frames == 0) {
281
-       spa_log_error(state->log, "%s: invalid buffer_frames 0 (driver error?)", state->props.device);
282
+       spa_log_error(state->log, "%s: invalid buffer_frames 0 (driver error?)", state->name);
283
        return -EIO;
284
    }
285
 
286
@@ -1745,12 +1801,10 @@
287
    state->latencystate->port_direction.min_rate =
288
        state->latencystate->port_direction.max_rate = latency;
289
 
290
-   spa_log_info(state->log, "%s (%s): format:%s access:%s-%s rate:%d channels:%d "
291
+   spa_log_info(state->log, "%s: format:%s access:%s-%s rate:%d channels:%d "
292
            "buffer frames %lu, period frames %lu, periods %u, frame_size %zd "
293
            "headroom %u start-delay:%u batch:%u tsched:%u",
294
-           state->props.device,
295
-           state->stream == SND_PCM_STREAM_CAPTURE ? "capture" : "playback",
296
-           snd_pcm_format_name(state->format),
297
+           state->name, snd_pcm_format_name(state->format),
298
            state->use_mmap ? "mmap" : "rw",
299
            planar ? "planar" : "interleaved",
300
            state->rate, state->channels, state->buffer_frames, state->period_frames,
301
@@ -1791,7 +1845,7 @@
302
    CHECK(snd_ctl_elem_write(state->ctl, state->pitch_elem), "snd_ctl_elem_write");
303
 
304
    spa_log_trace_fp(state->log, "%s %u set rate to %g",
305
-           state->props.device, state->stream, state->rate_match->rate);
306
+           state->name, state->stream, state->rate_match->rate);
307
 
308
    state->last_rate = state->rate_match->rate;
309
 
310
@@ -1854,10 +1908,6 @@
311
 static int set_timeout(struct state *state, uint64_t time)
312
 {
313
    struct itimerspec ts;
314
-
315
-   if (state->disable_tsched)
316
-       return 0;
317
-
318
    ts.it_value.tv_sec = time / SPA_NSEC_PER_SEC;
319
    ts.it_value.tv_nsec = time % SPA_NSEC_PER_SEC;
320
    ts.it_interval.tv_sec = 0;
321
@@ -1867,7 +1917,7 @@
322
    return 0;
323
 }
324
 
325
-int spa_alsa_silence(struct state *state, snd_pcm_uframes_t silence)
326
+static int spa_alsa_silence(struct state *state, snd_pcm_uframes_t silence)
327
 {
328
    snd_pcm_t *hndl = state->hndl;
329
    const snd_pcm_channel_area_t *my_areas;
330
@@ -1879,7 +1929,7 @@
331
 
332
        if (SPA_UNLIKELY((res = snd_pcm_mmap_begin(hndl, &my_areas, &offset, &frames)) < 0)) {
333
            spa_log_error(state->log, "%s: snd_pcm_mmap_begin error: %s",
334
-                   state->props.device, snd_strerror(res));
335
+                   state->name, snd_strerror(res));
336
            return res;
337
        }
338
        silence = SPA_MIN(silence, frames);
339
@@ -1890,7 +1940,7 @@
340
 
341
        if (SPA_UNLIKELY((res = snd_pcm_mmap_commit(hndl, offset, silence)) < 0)) {
342
            spa_log_error(state->log, "%s: snd_pcm_mmap_commit error: %s",
343
-                   state->props.device, snd_strerror(res));
344
+                   state->name, snd_strerror(res));
345
            return res;
346
        }
347
    } else {
348
@@ -1909,14 +1959,79 @@
349
    return 0;
350
 }
351
 
352
+static void reset_buffers(struct state *this)
353
+{
354
+   uint32_t i;
355
+
356
+   spa_list_init(&this->free);
357
+   spa_list_init(&this->ready);
358
+
359
+   for (i = 0; i < this->n_buffers; i++) {
360
+       struct buffer *b = &this->buffersi;
361
+       if (this->stream == SND_PCM_STREAM_PLAYBACK) {
362
+           SPA_FLAG_SET(b->flags, BUFFER_FLAG_OUT);
363
+           spa_node_call_reuse_buffer(&this->callbacks, 0, b->id);
364
+       } else {
365
+           spa_list_append(&this->free, &b->link);
366
+           SPA_FLAG_CLEAR(b->flags, BUFFER_FLAG_OUT);
367
+       }
368
+   }
369
+}
370
+
371
+
372
+static int do_prepare(struct state *state)
373
+{
374
+   int err;
375
+
376
+   state->last_threshold = state->threshold;
377
+
378
+   spa_log_debug(state->log, "%p: start threshold:%d duration:%d rate:%d follower:%d match:%d resample:%d",
379
+           state, state->threshold, state->driver_duration, state->driver_rate.denom,
380
+           state->following, state->matching, state->resample);
381
+
382
+   CHECK(set_swparams(state), "swparams");
383
+
384
+   if ((err = snd_pcm_prepare(state->hndl)) < 0 && err != -EBUSY) {
385
+       spa_log_error(state->log, "%s: snd_pcm_prepare error: %s",
386
+               state->name, snd_strerror(err));
387
+       return err;
388
+   }
389
+   if (state->stream == SND_PCM_STREAM_PLAYBACK) {
390
+       snd_pcm_uframes_t silence = state->start_delay + state->threshold + state->headroom;
391
+       if (state->disable_tsched)
392
+           silence += state->threshold;
393
+       spa_alsa_silence(state, silence);
394
+   }
395
+
396
+   reset_buffers(state);
397
+   state->alsa_sync = true;
398
+   state->alsa_sync_warning = false;
399
+   state->alsa_recovering = false;
400
+   state->alsa_started = false;
401
+
402
+   return 0;
403
+}
404
+
405
+static inline int do_drop(struct state *state)
406
+{
407
+   int res;
408
+   spa_log_debug(state->log, "%p: snd_pcm_drop %u", state, state->linked);
409
+   if (!state->linked && (res = snd_pcm_drop(state->hndl)) < 0) {
410
+       spa_log_error(state->log, "%s: snd_pcm_drop: %s",
411
+               state->name, snd_strerror(res));
412
+       return res;
413
+   }
414
+   return 0;
415
+}
416
+
417
 static inline int do_start(struct state *state)
418
 {
419
    int res;
420
    if (SPA_UNLIKELY(!state->alsa_started)) {
421
-       spa_log_trace(state->log, "%p: snd_pcm_start", state);
422
-       if ((res = snd_pcm_start(state->hndl)) < 0) {
423
+       spa_log_debug(state->log, "%p: snd_pcm_start %u", state, state->linked);
424
+       if (!state->linked && (res = snd_pcm_start(state->hndl)) < 0) {
425
            spa_log_error(state->log, "%s: snd_pcm_start: %s",
426
-                   state->props.device, snd_strerror(res));
427
+                   state->name, snd_strerror(res));
428
            return res;
429
        }
430
        state->alsa_started = true;
431
@@ -1924,15 +2039,18 @@
432
    return 0;
433
 }
434
 
435
+static inline int check_position_config(struct state *state);
436
+
437
 static int alsa_recover(struct state *state, int err)
438
 {
439
    int res, st;
440
    snd_pcm_status_t *status;
441
+   struct state *driver, *follower;
442
 
443
    snd_pcm_status_alloca(&status);
444
    if (SPA_UNLIKELY((res = snd_pcm_status(state->hndl, status)) < 0)) {
445
        spa_log_error(state->log, "%s: snd_pcm_status error: %s",
446
-               state->props.device, snd_strerror(res));
447
+               state->name, snd_strerror(res));
448
        goto recover;
449
    }
450
 
451
@@ -1964,7 +2082,7 @@
452
    }
453
    case SND_PCM_STATE_SUSPENDED:
454
        spa_log_info(state->log, "%s: recover from state %s",
455
-               state->props.device, snd_pcm_state_name(st));
456
+               state->name, snd_pcm_state_name(st));
457
        res = snd_pcm_resume(state->hndl);
458
        if (res >= 0)
459
                return res;
460
@@ -1972,24 +2090,48 @@
461
        break;
462
    default:
463
        spa_log_error(state->log, "%s: recover from error state %s",
464
-               state->props.device, snd_pcm_state_name(st));
465
+               state->name, snd_pcm_state_name(st));
466
        break;
467
    }
468
 
469
 recover:
470
    if (SPA_UNLIKELY((res = snd_pcm_recover(state->hndl, err, true)) < 0)) {
471
        spa_log_error(state->log, "%s: snd_pcm_recover error: %s",
472
-               state->props.device, snd_strerror(res));
473
+               state->name, snd_strerror(res));
474
        return res;
475
    }
476
-   spa_dll_init(&state->dll);
477
-   state->alsa_recovering = true;
478
-   state->alsa_started = false;
479
+   if (state->driver && state->linked)
480
+       driver = state->driver;
481
+   else
482
+       driver = state;
483
 
484
-   if (state->stream == SND_PCM_STREAM_PLAYBACK)
485
-       spa_alsa_silence(state, state->start_delay + state->threshold + state->headroom);
486
+   do_drop(driver);
487
+   spa_list_for_each(follower, &driver->rt.followers, rt.driver_link) {
488
+       if (follower != driver && follower->linked) {
489
+           do_drop(follower);
490
+           check_position_config(follower);
491
+       }
492
+   }
493
+   do_prepare(driver);
494
+   spa_list_for_each(follower, &driver->rt.followers, rt.driver_link) {
495
+       if (follower != driver && follower->linked)
496
+           do_prepare(follower);
497
+   }
498
+   do_start(driver);
499
+   spa_list_for_each(follower, &driver->rt.followers, rt.driver_link) {
500
+       if (follower != driver && follower->linked)
501
+           do_start(follower);
502
+   }
503
+
504
+   return res;
505
+}
506
 
507
-   return do_start(state);
508
+static inline snd_pcm_sframes_t alsa_avail(struct state *state)
509
+{
510
+   if (state->disable_tsched)
511
+       return snd_pcm_avail_update(state->hndl);
512
+   else
513
+       return snd_pcm_avail(state->hndl);
514
 }
515
 
516
 static int get_avail(struct state *state, uint64_t current_time, snd_pcm_uframes_t *delay)
517
@@ -1997,13 +2139,13 @@
518
    int res, suppressed;
519
    snd_pcm_sframes_t avail;
520
 
521
-   if (SPA_UNLIKELY((avail = snd_pcm_avail(state->hndl)) < 0)) {
522
+   if (SPA_UNLIKELY((avail = alsa_avail(state)) < 0)) {
523
        if ((res = alsa_recover(state, avail)) < 0)
524
            return res;
525
-       if ((avail = snd_pcm_avail(state->hndl)) < 0) {
526
+       if ((avail = alsa_avail(state)) < 0) {
527
            if ((suppressed = spa_ratelimit_test(&state->rate_limit, current_time)) >= 0) {
528
                spa_log_warn(state->log, "%s: (%d suppressed) snd_pcm_avail after recover: %s",
529
-                       state->props.device, suppressed, snd_strerror(avail));
530
+                       state->name, suppressed, snd_strerror(avail));
531
            }
532
            avail = state->threshold * 2;
533
        }
534
@@ -2020,7 +2162,7 @@
535
        if ((res = snd_pcm_htimestamp(state->hndl, &havail, &tstamp)) < 0) {
536
            if ((suppressed = spa_ratelimit_test(&state->rate_limit, current_time)) >= 0) {
537
                spa_log_warn(state->log, "%s: (%d suppressed) snd_pcm_htimestamp error: %s",
538
-                   state->props.device, suppressed, snd_strerror(res));
539
+                   state->name, suppressed, snd_strerror(res));
540
            }
541
            return avail;
542
        }
543
@@ -2042,13 +2184,13 @@
544
            } else {
545
                if (++state->htimestamp_error > MAX_HTIMESTAMP_ERROR) {
546
                    spa_log_error(state->log, "%s: wrong htimestamps from driver, disabling",
547
-                       state->props.device);
548
+                       state->name);
549
                    state->htimestamp_error = 0;
550
                    state->htimestamp = false;
551
                }
552
                else if ((suppressed = spa_ratelimit_test(&state->rate_limit, current_time)) >= 0) {
553
                    spa_log_warn(state->log, "%s: (%d suppressed) impossible htimestamp diff:%"PRIi64,
554
-                       state->props.device, suppressed, diff);
555
+                       state->name, suppressed, diff);
556
                }
557
            }
558
        }
559
@@ -2141,7 +2283,7 @@
560
 
561
        spa_log_debug(state->log, "%s: follower:%d match:%d rate:%f "
562
                "bw:%f thr:%u del:%ld target:%ld err:%f max:%f",
563
-               state->props.device, follower, state->matching,
564
+               state->name, follower, state->matching,
565
                corr, state->dll.bw, state->threshold, delay, target,
566
                err, state->max_error);
567
    }
568
@@ -2162,9 +2304,9 @@
569
 
570
    if (SPA_LIKELY(!follower && state->clock)) {
571
        state->clock->nsec = current_time;
572
-       state->clock->rate = state->clock->target_rate;
573
+       state->clock->rate = state->driver_rate;
574
        state->clock->position += state->clock->duration;
575
-       state->clock->duration = state->duration;
576
+       state->clock->duration = state->driver_duration;
577
        state->clock->delay = delay + state->delay;
578
        state->clock->rate_diff = corr;
579
        state->clock->next_nsec = state->next_time;
580
@@ -2177,11 +2319,6 @@
581
    return 0;
582
 }
583
 
584
-static inline bool is_following(struct state *state)
585
-{
586
-   return state->position && state->clock && state->position->clock.id != state->clock->id;
587
-}
588
-
589
 static int setup_matching(struct state *state)
590
 {
591
    state->matching = state->following;
592
@@ -2195,10 +2332,10 @@
593
    if (spa_streq(state->position->clock.name, state->clock_name))
594
        state->matching = false;
595
 
596
-   state->resample = !state->pitch_elem && (((uint32_t)state->rate != state->rate_denom) || state->matching);
597
+   state->resample = !state->pitch_elem && (((uint32_t)state->rate != state->driver_rate.denom) || state->matching);
598
 
599
    spa_log_info(state->log, "driver clock:'%s'@%d our clock:'%s'@%d matching:%d resample:%d",
600
-           state->position->clock.name, state->rate_denom,
601
+           state->position->clock.name, state->driver_rate.denom,
602
            state->clock_name, state->rate,
603
            state->matching, state->resample);
604
    return 0;
605
@@ -2206,7 +2343,7 @@
606
 
607
 static void update_sources(struct state *state, bool active)
608
 {
609
-   if (state->disable_tsched && state->source0.data != NULL) {
610
+   if (state->disable_tsched && state->rt.sources_added) {
611
        for (int i = 0; i < state->n_fds; i++) {
612
            state->sourcei.mask = active ? state->pfdsi.events : 0;
613
            spa_loop_update_source(state->data_loop, &state->sourcei);
614
@@ -2218,61 +2355,64 @@
615
 {
616
    uint64_t target_duration;
617
    struct spa_fraction target_rate;
618
+   struct spa_io_position *pos;
619
 
620
-   if (SPA_UNLIKELY(state->position  == NULL))
621
+   if (SPA_UNLIKELY((pos = state->position) == NULL))
622
        return 0;
623
 
624
    if (state->disable_tsched && state->started && !state->following) {
625
        target_duration = state->period_frames;
626
        target_rate = SPA_FRACTION(1, state->rate);
627
-       state->position->clock.target_duration = target_duration;
628
-       state->position->clock.target_rate = target_rate;
629
+       pos->clock.target_duration = target_duration;
630
+       pos->clock.target_rate = target_rate;
631
    } else {
632
-       target_duration = state->position->clock.target_duration;
633
-       target_rate = state->position->clock.target_rate;
634
+       target_duration = pos->clock.target_duration;
635
+       target_rate = pos->clock.target_rate;
636
    }
637
+   if (target_duration == 0 || target_rate.denom == 0)
638
+       return -EIO;
639
 
640
-   if (SPA_UNLIKELY((state->duration != target_duration) ||
641
-       (state->rate_denom != target_rate.denom))) {
642
-       state->duration = target_duration;
643
-       state->rate_denom = target_rate.denom;
644
-       if (state->rate_denom == 0 || state->duration == 0)
645
-           return -EIO;
646
-       state->threshold = SPA_SCALE32_UP(state->duration, state->rate, state->rate_denom);
647
+   if (SPA_UNLIKELY((state->driver_duration != target_duration) ||
648
+       (state->driver_rate.denom != target_rate.denom))) {
649
+       spa_log_info(state->log, "%p: follower:%d duration:%u->%"PRIu64" rate:%d->%d",
650
+               state, state->following, state->driver_duration, target_duration,
651
+               state->driver_rate.denom, target_rate.denom);
652
+
653
+       state->driver_duration = target_duration;
654
+       state->driver_rate = target_rate;
655
+       state->threshold = SPA_SCALE32_UP(state->driver_duration, state->rate, state->driver_rate.denom);
656
        state->max_error = SPA_MAX(256.0f, state->threshold / 2.0f);
657
        state->max_resync = SPA_MIN(state->threshold, state->max_error);
658
-       state->resample = ((uint32_t)state->rate != state->rate_denom) || state->matching;
659
+       state->resample = ((uint32_t)state->rate != state->driver_rate.denom) || state->matching;
660
        state->alsa_sync = true;
661
    }
662
    return 0;
663
 }
664
 
665
-int spa_alsa_write(struct state *state)
666
+static int alsa_write_sync(struct state *state, uint64_t current_time)
667
 {
668
-   snd_pcm_t *hndl = state->hndl;
669
-   const snd_pcm_channel_area_t *my_areas;
670
-   snd_pcm_uframes_t written, frames, offset, off, to_write, total_written, max_write;
671
-   snd_pcm_sframes_t commitres;
672
    int res, suppressed;
673
-   size_t frame_size = state->frame_size;
674
+   snd_pcm_uframes_t avail, delay, target;
675
+   bool following = state->following;
676
 
677
-   if ((res = check_position_config(state)) < 0)
678
+   if (SPA_UNLIKELY((res = check_position_config(state)) < 0))
679
        return res;
680
 
681
-   max_write = state->buffer_frames;
682
-
683
-   if (state->following && state->alsa_started) {
684
-       uint64_t current_time;
685
-       snd_pcm_uframes_t avail, delay, target;
686
-
687
-       current_time = state->position->clock.nsec;
688
-
689
-       if (SPA_UNLIKELY((res = get_status(state, current_time, &avail, &delay, &target)) < 0))
690
-           return res;
691
+   if (SPA_UNLIKELY((res = get_status(state, current_time, &avail, &delay, &target)) < 0))
692
+       return res;
693
 
694
-       if (SPA_UNLIKELY((res = update_time(state, current_time, delay, target, true)) < 0))
695
-           return res;
696
+   if (SPA_UNLIKELY(!following && delay > target + state->max_error)) {
697
+       spa_log_trace(state->log, "%p: early wakeup %ld %lu %lu", state,
698
+               avail, delay, target);
699
+       if (delay > target * 3)
700
+           delay = target * 3;
701
+       state->next_time = current_time + (delay - target) * SPA_NSEC_PER_SEC / state->rate;
702
+       return -EAGAIN;
703
+   }
704
+   if (SPA_UNLIKELY((res = update_time(state, current_time, delay, target, following)) < 0))
705
+       return res;
706
 
707
+   if (following && !state->linked) {
708
        if (SPA_UNLIKELY(state->alsa_sync)) {
709
            enum spa_log_level lev;
710
 
711
@@ -2284,7 +2424,7 @@
712
            if ((suppressed = spa_ratelimit_test(&state->rate_limit, current_time)) >= 0) {
713
                spa_log_lev(state->log, lev, "%s: follower avail:%lu delay:%ld "
714
                        "target:%ld thr:%u, resync (%d suppressed)",
715
-                       state->props.device, avail, delay,
716
+                       state->name, avail, delay,
717
                        target, state->threshold, suppressed);
718
            }
719
 
720
@@ -2297,15 +2437,25 @@
721
        } else
722
            state->alsa_sync_warning = true;
723
    }
724
+   return 0;
725
+}
726
+
727
+static int alsa_write_frames(struct state *state)
728
+{
729
+   snd_pcm_t *hndl = state->hndl;
730
+   const snd_pcm_channel_area_t *my_areas;
731
+   snd_pcm_uframes_t written, frames, offset, off, to_write, total_written;
732
+   snd_pcm_sframes_t commitres;
733
+   int res = 0;
734
+   size_t frame_size = state->frame_size;
735
 
736
    total_written = 0;
737
 again:
738
-
739
-   frames = max_write;
740
+   frames = state->buffer_frames;
741
    if (state->use_mmap && frames > 0) {
742
        if (SPA_UNLIKELY((res = snd_pcm_mmap_begin(hndl, &my_areas, &offset, &frames)) < 0)) {
743
            spa_log_error(state->log, "%s: snd_pcm_mmap_begin error: %s",
744
-                   state->props.device, snd_strerror(res));
745
+                   state->name, snd_strerror(res));
746
            alsa_recover(state, res);
747
            return res;
748
        }
749
@@ -2378,13 +2528,13 @@
750
    if (state->use_mmap && written > 0) {
751
        if (SPA_UNLIKELY((commitres = snd_pcm_mmap_commit(hndl, offset, written)) < 0)) {
752
            spa_log_error(state->log, "%s: snd_pcm_mmap_commit error: %s",
753
-                   state->props.device, snd_strerror(commitres));
754
+                   state->name, snd_strerror(commitres));
755
            if (commitres != -EPIPE && commitres != -ESTRPIPE)
756
                return res;
757
        }
758
        if (commitres > 0 && written != (snd_pcm_uframes_t) commitres) {
759
            spa_log_warn(state->log, "%s: mmap_commit wrote %ld instead of %ld",
760
-                    state->props.device, commitres, written);
761
+                    state->name, commitres, written);
762
        }
763
    }
764
 
765
@@ -2401,6 +2551,17 @@
766
    return 0;
767
 }
768
 
769
+int spa_alsa_write(struct state *state)
770
+{
771
+   int res = 0;
772
+   if (state->following && state->rt.driver == NULL) {
773
+       uint64_t current_time = state->position->clock.nsec;
774
+       if ((res = alsa_write_sync(state, current_time)) < 0)
775
+           return res;
776
+   }
777
+   return alsa_write_frames(state);
778
+}
779
+
780
 void spa_alsa_recycle_buffer(struct state *this, uint32_t buffer_id)
781
 {
782
    struct buffer *b = &this->buffersbuffer_id;
783
@@ -2421,7 +2582,7 @@
784
    snd_pcm_uframes_t total_frames = 0;
785
 
786
    if (spa_list_is_empty(&state->free)) {
787
-       spa_log_warn(state->log, "%s: no more buffers", state->props.device);
788
+       spa_log_warn(state->log, "%s: no more buffers", state->name);
789
        total_frames = frames;
790
    } else {
791
        size_t n_bytes, left, frame_size = state->frame_size;
792
@@ -2483,33 +2644,34 @@
793
    return total_frames;
794
 }
795
 
796
-
797
-int spa_alsa_read(struct state *state)
798
+static int alsa_read_sync(struct state *state, uint64_t current_time)
799
 {
800
-   snd_pcm_t *hndl = state->hndl;
801
-   snd_pcm_uframes_t total_read = 0, to_read, max_read;
802
-   const snd_pcm_channel_area_t *my_areas;
803
-   snd_pcm_uframes_t read, frames, offset;
804
-   snd_pcm_sframes_t commitres;
805
    int res, suppressed;
806
+   snd_pcm_uframes_t avail, delay, target, max_read;
807
+   bool following = state->following;
808
 
809
-   if ((res = check_position_config(state)) < 0)
810
-       return res;
811
-
812
-   max_read = state->buffer_frames;
813
+   if (SPA_UNLIKELY(!state->alsa_started))
814
+       return 0;
815
 
816
-   if (state->following && state->alsa_started) {
817
-       uint64_t current_time;
818
-       snd_pcm_uframes_t avail, delay, target;
819
+   if (SPA_UNLIKELY((res = check_position_config(state)) < 0))
820
+       return res;
821
 
822
-       current_time = state->position->clock.nsec;
823
+   if (SPA_UNLIKELY((res = get_status(state, current_time, &avail, &delay, &target)) < 0))
824
+       return res;
825
 
826
-       if ((res = get_status(state, current_time, &avail, &delay, &target)) < 0)
827
-           return res;
828
+   if (SPA_UNLIKELY(!following && avail < state->read_size)) {
829
+       spa_log_trace(state->log, "%p: early wakeup %ld %ld %ld %d", state,
830
+               delay, avail, target, state->read_size);
831
+       state->next_time = current_time + (state->read_size - avail) * SPA_NSEC_PER_SEC /
832
+           state->rate;
833
+       return -EAGAIN;
834
+   }
835
 
836
-       if (SPA_UNLIKELY((res = update_time(state, current_time, delay, target, true)) < 0))
837
-           return res;
838
+   if (SPA_UNLIKELY((res = update_time(state, current_time, delay, target, following)) < 0))
839
+       return res;
840
 
841
+   max_read = state->buffer_frames;
842
+   if (following) {
843
        if (state->alsa_sync) {
844
            enum spa_log_level lev;
845
 
846
@@ -2520,7 +2682,7 @@
847
 
848
            if ((suppressed = spa_ratelimit_test(&state->rate_limit, current_time)) >= 0) {
849
                spa_log_lev(state->log, lev, "%s: follower delay:%ld target:%ld thr:%u, "
850
-                       "resync (%d suppressed)", state->props.device, delay,
851
+                       "resync (%d suppressed)", state->name, delay,
852
                        target, state->threshold, suppressed);
853
            }
854
 
855
@@ -2537,14 +2699,26 @@
856
        if (avail < state->read_size)
857
            max_read = 0;
858
    }
859
+   state->max_read = SPA_MIN(max_read, state->read_size);
860
+   return 0;
861
+}
862
+
863
+static int alsa_read_frames(struct state *state)
864
+{
865
+   snd_pcm_t *hndl = state->hndl;
866
+   snd_pcm_uframes_t total_read = 0, to_read;
867
+   const snd_pcm_channel_area_t *my_areas;
868
+   snd_pcm_uframes_t read, frames, offset;
869
+   snd_pcm_sframes_t commitres;
870
+   int res = 0;
871
 
872
-   frames = SPA_MIN(max_read, state->read_size);
873
+   frames = state->max_read;
874
 
875
    if (state->use_mmap) {
876
        to_read = state->buffer_frames;
877
        if ((res = snd_pcm_mmap_begin(hndl, &my_areas, &offset, &to_read)) < 0) {
878
            spa_log_error(state->log, "%s: snd_pcm_mmap_begin error: %s",
879
-                   state->props.device, snd_strerror(res));
880
+                   state->name, snd_strerror(res));
881
            alsa_recover(state, res);
882
            return res;
883
        }
884
@@ -2568,14 +2742,21 @@
885
        spa_log_trace_fp(state->log, "%p: commit offs:%ld read:%ld count:%"PRIi64, state,
886
                offset, read, state->sample_count);
887
        if ((commitres = snd_pcm_mmap_commit(hndl, offset, read)) < 0) {
888
-           spa_log_error(state->log, "%s: snd_pcm_mmap_commit error %lu %lu: %s",
889
-                   state->props.device, frames, read, snd_strerror(commitres));
890
+           enum spa_log_level lev;
891
+
892
+           if (SPA_UNLIKELY(state->alsa_sync_warning))
893
+               lev = SPA_LOG_LEVEL_ERROR;
894
+           else
895
+               lev = SPA_LOG_LEVEL_INFO;
896
+
897
+           spa_log_lev(state->log, lev, "%s: snd_pcm_mmap_commit error %lu %lu %lu: %s",
898
+                   state->name, frames, to_read, read, snd_strerror(commitres));
899
            if (commitres != -EPIPE && commitres != -ESTRPIPE)
900
                return res;
901
        }
902
        if (commitres > 0 && read != (snd_pcm_uframes_t) commitres) {
903
            spa_log_warn(state->log, "%s: mmap_commit read %ld instead of %ld",
904
-                    state->props.device, commitres, read);
905
+                    state->name, commitres, read);
906
        }
907
    }
908
 
909
@@ -2584,14 +2765,25 @@
910
    return 0;
911
 }
912
 
913
+int spa_alsa_read(struct state *state)
914
+{
915
+   int res;
916
+   if (state->following && state->rt.driver == NULL) {
917
+       uint64_t current_time = state->position->clock.nsec;
918
+       if ((res = alsa_read_sync(state, current_time)) < 0)
919
+           return res;
920
+   }
921
+   return alsa_read_frames(state);
922
+}
923
+
924
 int spa_alsa_skip(struct state *state)
925
 {
926
    struct buffer *b;
927
    struct spa_data *d;
928
    uint32_t i, avail, total_frames, n_bytes, frames;
929
 
930
-   if (spa_list_is_empty(&state->free)) {
931
-       spa_log_warn(state->log, "%s: no more buffers", state->props.device);
932
+   if (SPA_UNLIKELY(spa_list_is_empty(&state->free))) {
933
+       spa_log_warn(state->log, "%s: no more buffers", state->name);
934
        return -EPIPE;
935
    }
936
 
937
@@ -2618,23 +2810,9 @@
938
 }
939
 
940
 
941
-static int handle_play(struct state *state, uint64_t current_time, snd_pcm_uframes_t avail,
942
-       snd_pcm_uframes_t delay, snd_pcm_uframes_t target)
943
+static int playback_ready(struct state *state)
944
 {
945
    struct spa_io_buffers *io = state->io;
946
-   int res;
947
-
948
-   if (state->alsa_started && SPA_UNLIKELY(delay > target + state->max_error)) {
949
-       spa_log_trace(state->log, "%p: early wakeup %ld %lu %lu", state,
950
-               avail, delay, target);
951
-       if (delay > target * 3)
952
-           delay = target * 3;
953
-       state->next_time = current_time + (delay - target) * SPA_NSEC_PER_SEC / state->rate;
954
-       return -EAGAIN;
955
-   }
956
-
957
-   if (SPA_UNLIKELY((res = update_time(state, current_time, delay, target, false)) < 0))
958
-       return res;
959
 
960
    spa_log_trace_fp(state->log, "%p: %d", state, io->status);
961
 
962
@@ -2644,46 +2822,35 @@
963
    return spa_node_call_ready(&state->callbacks, SPA_STATUS_NEED_DATA);
964
 }
965
 
966
-static int handle_capture(struct state *state, uint64_t current_time, snd_pcm_uframes_t avail,
967
-       snd_pcm_uframes_t delay, snd_pcm_uframes_t target)
968
+static int capture_ready(struct state *state)
969
 {
970
-   int res;
971
    struct spa_io_buffers *io;
972
+   bool have_data;
973
 
974
-   if (SPA_UNLIKELY(avail < state->read_size)) {
975
-       spa_log_trace(state->log, "%p: early wakeup %ld %ld %ld %d", state,
976
-               delay, avail, target, state->read_size);
977
-       state->next_time = current_time + (state->read_size - avail) * SPA_NSEC_PER_SEC /
978
-           state->rate;
979
-       return -EAGAIN;
980
-   }
981
-
982
-   if (SPA_UNLIKELY((res = update_time(state, current_time, delay, target, false)) < 0))
983
-       return res;
984
-
985
-   if ((res = spa_alsa_read(state)) < 0)
986
-       return res;
987
-
988
-   if (spa_list_is_empty(&state->ready))
989
-       return 0;
990
+   have_data = !spa_list_is_empty(&state->ready);
991
 
992
    io = state->io;
993
    if (io != NULL &&
994
        (io->status != SPA_STATUS_HAVE_DATA || state->rate_match != NULL)) {
995
        struct buffer *b;
996
 
997
-       if (io->buffer_id < state->n_buffers)
998
+       if (SPA_LIKELY(io->buffer_id < state->n_buffers))
999
            spa_alsa_recycle_buffer(state, io->buffer_id);
1000
 
1001
-       b = spa_list_first(&state->ready, struct buffer, link);
1002
-       spa_list_remove(&b->link);
1003
-       SPA_FLAG_SET(b->flags, BUFFER_FLAG_OUT);
1004
+       if (SPA_LIKELY(have_data)) {
1005
+           b = spa_list_first(&state->ready, struct buffer, link);
1006
+           spa_list_remove(&b->link);
1007
+           SPA_FLAG_SET(b->flags, BUFFER_FLAG_OUT);
1008
 
1009
-       io->buffer_id = b->id;
1010
-       io->status = SPA_STATUS_HAVE_DATA;
1011
-       spa_log_trace_fp(state->log, "%p: output buffer:%d", state, b->id);
1012
+           io->buffer_id = b->id;
1013
+           io->status = SPA_STATUS_HAVE_DATA;
1014
+       } else {
1015
+           io->buffer_id = SPA_ID_INVALID;
1016
+       }
1017
+       spa_log_trace_fp(state->log, "%p: output buffer:%d", state, io->buffer_id);
1018
    }
1019
-   spa_node_call_ready(&state->callbacks, SPA_STATUS_HAVE_DATA);
1020
+   if (have_data)
1021
+       spa_node_call_ready(&state->callbacks, SPA_STATUS_HAVE_DATA);
1022
    return 0;
1023
 }
1024
 
1025
@@ -2697,8 +2864,7 @@
1026
 
1027
 static void alsa_wakeup_event(struct spa_source *source)
1028
 {
1029
-   struct state *state = source->data;
1030
-   snd_pcm_uframes_t avail, delay, target;
1031
+   struct state *state = source->data, *follower;
1032
    uint64_t expire, current_time;
1033
    int res, suppressed;
1034
 
1035
@@ -2742,154 +2908,151 @@
1036
        current_time = state->next_time;
1037
    }
1038
 
1039
-   if (SPA_UNLIKELY((res = check_position_config(state)) < 0)) {
1040
-       spa_log_warn(state->log, "%p: error invalid position: %s",
1041
-                       state, spa_strerror(res));
1042
-       return;
1043
-   }
1044
-
1045
-   if (SPA_UNLIKELY(get_status(state, current_time, &avail, &delay, &target) < 0)) {
1046
-       spa_log_error(state->log, "get_status error");
1047
-       state->next_time += state->threshold * 1e9 / state->rate;
1048
+   /* first do all the sync */
1049
+   if (state->stream == SND_PCM_STREAM_CAPTURE)
1050
+       res = alsa_read_sync(state, current_time);
1051
+   else
1052
+       res = alsa_write_sync(state, current_time);
1053
+   /* we can get -EAGAIN when we need to wait some more */
1054
+   if (SPA_UNLIKELY(res == -EAGAIN))
1055
        goto done;
1056
-   }
1057
 
1058
-#ifndef FASTPATH
1059
-   if (SPA_UNLIKELY(spa_log_level_topic_enabled(state->log, SPA_LOG_TOPIC_DEFAULT, SPA_LOG_LEVEL_TRACE))) {
1060
-       uint64_t nsec = get_time_ns(state);
1061
-       spa_log_trace_fp(state->log, "%p: wakeup %lu %lu %lu %"PRIu64" %"PRIu64" %"PRIi64
1062
-               " %d %"PRIi64, state, avail, delay, target, nsec, nsec,
1063
-               nsec - current_time, state->threshold, state->sample_count);
1064
+   spa_list_for_each(follower, &state->rt.followers, rt.driver_link) {
1065
+       if (follower == state)
1066
+           continue;
1067
+       if (follower->stream == SND_PCM_STREAM_CAPTURE)
1068
+           alsa_read_sync(follower, current_time);
1069
+       else
1070
+           alsa_write_sync(follower, current_time);
1071
    }
1072
-#endif
1073
 
1074
+   /* then read this source, the sinks will be written to when the
1075
+    * graph completes. We can't read other follower sources yet because
1076
+    * the resampler first needs to run. */
1077
+   if (state->stream == SND_PCM_STREAM_CAPTURE)
1078
+       alsa_read_frames(state);
1079
+
1080
+   /* and then trigger the graph */
1081
    if (state->stream == SND_PCM_STREAM_PLAYBACK)
1082
-       handle_play(state, current_time, avail, delay, target);
1083
+       playback_ready(state);
1084
    else
1085
-       handle_capture(state, current_time, avail, delay, target);
1086
+       capture_ready(state);
1087
 
1088
 done:
1089
-   if (!state->disable_tsched &&
1090
-           (state->next_time > current_time + SPA_NSEC_PER_SEC ||
1091
-            current_time > state->next_time + SPA_NSEC_PER_SEC)) {
1092
-       if ((suppressed = spa_ratelimit_test(&state->rate_limit, current_time)) >= 0) {
1093
-           spa_log_error(state->log, "%s: impossible timeout %lu %lu %lu %"
1094
+   if (!state->disable_tsched) {
1095
+       if (state->next_time > current_time + SPA_NSEC_PER_SEC ||
1096
+           current_time > state->next_time + SPA_NSEC_PER_SEC) {
1097
+           if ((suppressed = spa_ratelimit_test(&state->rate_limit, current_time)) >= 0) {
1098
+               spa_log_error(state->log, "%s: impossible timeout %"
1099
                    PRIu64" %"PRIu64" %"PRIi64" %d %"PRIi64" (%d suppressed)",
1100
-                   state->props.device, avail, delay, target,
1101
-                   current_time, state->next_time, state->next_time - current_time,
1102
-                   state->threshold, state->sample_count, suppressed);
1103
+                   state->name, current_time, state->next_time,
1104
+                   state->next_time - current_time, state->threshold,
1105
+                   state->sample_count, suppressed);
1106
+           }
1107
+           state->next_time = current_time + state->threshold * 1e9 / state->rate;
1108
        }
1109
-       state->next_time = current_time + state->threshold * 1e9 / state->rate;
1110
+       set_timeout(state, state->next_time);
1111
    }
1112
-   set_timeout(state, state->next_time);
1113
 }
1114
 
1115
-static void reset_buffers(struct state *this)
1116
+static void remove_sources(struct state *state)
1117
 {
1118
-   uint32_t i;
1119
-
1120
-   spa_list_init(&this->free);
1121
-   spa_list_init(&this->ready);
1122
-
1123
-   for (i = 0; i < this->n_buffers; i++) {
1124
-       struct buffer *b = &this->buffersi;
1125
-       if (this->stream == SND_PCM_STREAM_PLAYBACK) {
1126
-           SPA_FLAG_SET(b->flags, BUFFER_FLAG_OUT);
1127
-           spa_node_call_reuse_buffer(&this->callbacks, 0, b->id);
1128
-       } else {
1129
-           spa_list_append(&this->free, &b->link);
1130
-           SPA_FLAG_CLEAR(b->flags, BUFFER_FLAG_OUT);
1131
-       }
1132
+   int i;
1133
+   if (state->rt.sources_added) {
1134
+       for (i = 0; i < state->n_fds; i++)
1135
+           spa_loop_remove_source(state->data_loop, &state->sourcei);
1136
+       state->rt.sources_added = false;
1137
    }
1138
 }
1139
 
1140
-static void clear_period_sources(struct state *state) {
1141
-   /* This check is to make sure we've actually added the sources
1142
-    * previously */
1143
-   if (state->source0.data) {
1144
-       for (int i = 0; i < state->n_fds; i++) {
1145
-           spa_loop_remove_source(state->data_loop, &state->sourcei);
1146
-           state->sourcei.func = NULL;
1147
-           state->sourcei.data = NULL;
1148
-           state->sourcei.fd = -1;
1149
-           state->sourcei.mask = 0;
1150
-           state->sourcei.rmask = 0;
1151
-       }
1152
+static void add_sources(struct state *state)
1153
+{
1154
+   int i;
1155
+   if (!state->rt.sources_added) {
1156
+       for (i = 0; i < state->n_fds; i++)
1157
+           spa_loop_add_source(state->data_loop, &state->sourcei);
1158
+       state->rt.sources_added = true;
1159
    }
1160
 }
1161
 
1162
-static int setup_sources(struct state *state)
1163
+static int do_state_sync(struct spa_loop *loop, bool async, uint32_t seq,
1164
+       const void *data, size_t size, void *user_data)
1165
 {
1166
-   state->next_time = get_time_ns(state);
1167
+   struct state *state = user_data;
1168
+   struct rt_state *rt = &state->rt;
1169
+
1170
+   if (state->started) {
1171
+       state->next_time = get_time_ns(state);
1172
+
1173
+       if (rt->driver != state->driver) {
1174
+           spa_dll_init(&state->dll);
1175
 
1176
-   if (state->following) {
1177
-       /* Disable wakeups from this node */
1178
-       if (!state->disable_tsched) {
1179
+           if (rt->driver != NULL)
1180
+               spa_list_remove(&rt->driver_link);
1181
+           if (state->driver != NULL)
1182
+               spa_list_append(&state->driver->rt.followers, &rt->driver_link);
1183
+           rt->driver = state->driver;
1184
+           spa_log_debug(state->log, "state:%p -> driver:%p", state, state->driver);
1185
+       }
1186
+       if (state->following) {
1187
+           remove_sources(state);
1188
            set_timeout(state, 0);
1189
        } else {
1190
-           clear_period_sources(state);
1191
+           add_sources(state);
1192
+           if (!state->disable_tsched)
1193
+               set_timeout(state, state->next_time);
1194
        }
1195
    } else {
1196
-       /* We're driving, so let's wake up when data is ready/needed */
1197
-       if (!state->disable_tsched) {
1198
-           set_timeout(state, state->next_time);
1199
-       } else {
1200
-           for (int i = 0; i < state->n_fds; i++) {
1201
-               state->sourcei.func = alsa_wakeup_event;
1202
-               state->sourcei.data = state;
1203
-               state->sourcei.fd = state->pfdsi.fd;
1204
-               state->sourcei.mask = state->pfdsi.events;
1205
-               state->sourcei.rmask = 0;
1206
-               spa_loop_add_source(state->data_loop, &state->sourcei);
1207
-           }
1208
+       if (rt->driver) {
1209
+           spa_list_remove(&rt->driver_link);
1210
+           rt->driver = NULL;
1211
        }
1212
+       if (!state->disable_tsched)
1213
+           set_timeout(state, 0);
1214
+       remove_sources(state);
1215
    }
1216
    return 0;
1217
 }
1218
 
1219
-static int do_setup_sources(struct spa_loop *loop,
1220
-               bool async,
1221
-               uint32_t seq,
1222
-               const void *data,
1223
-               size_t size,
1224
-               void *user_data)
1225
-{
1226
-   struct state *state = user_data;
1227
-   spa_dll_init(&state->dll);
1228
-   setup_sources(state);
1229
-   return 0;
1230
-}
1231
-
1232
-int spa_alsa_start(struct state *state)
1233
+int spa_alsa_prepare(struct state *state)
1234
 {
1235
+   struct state *follower;
1236
    int err;
1237
 
1238
-   if (state->started)
1239
-       return 0;
1240
+   spa_alsa_pause(state);
1241
 
1242
-   state->following = is_following(state);
1243
+   if (state->prepared)
1244
+       return 0;
1245
 
1246
    if (check_position_config(state) < 0) {
1247
-       spa_log_error(state->log, "%s: invalid position config", state->props.device);
1248
+       spa_log_error(state->log, "%s: invalid position config", state->name);
1249
        return -EIO;
1250
    }
1251
+   if ((err = do_prepare(state)) < 0)
1252
+       return err;
1253
 
1254
-   setup_matching(state);
1255
+   spa_list_for_each(follower, &state->followers, driver_link) {
1256
+       if (follower != state && !follower->matching) {
1257
+           spa_alsa_prepare(follower);
1258
+           if (!follower->linked)
1259
+               do_link(state, follower);
1260
+       }
1261
+   }
1262
 
1263
-   spa_dll_init(&state->dll);
1264
-   state->last_threshold = state->threshold;
1265
+   state->prepared = true;
1266
 
1267
-   spa_log_debug(state->log, "%p: start %d duration:%d rate:%d follower:%d match:%d resample:%d",
1268
-           state, state->threshold, state->duration, state->rate_denom,
1269
-           state->following, state->matching, state->resample);
1270
+   return 0;
1271
+}
1272
 
1273
-   CHECK(set_swparams(state), "swparams");
1274
+int spa_alsa_start(struct state *state)
1275
+{
1276
+   struct state *follower;
1277
+   int err;
1278
 
1279
-   if ((err = snd_pcm_prepare(state->hndl)) < 0 && err != -EBUSY) {
1280
-       spa_log_error(state->log, "%s: snd_pcm_prepare error: %s",
1281
-               state->props.device, snd_strerror(err));
1282
-       return err;
1283
-   }
1284
+   if (state->started)
1285
+       return 0;
1286
+
1287
+   spa_alsa_prepare(state);
1288
 
1289
    if (!state->disable_tsched) {
1290
        /* Timer-based scheduling */
1291
@@ -2898,7 +3061,7 @@
1292
        state->source0.fd = state->timerfd;
1293
        state->source0.mask = SPA_IO_IN;
1294
        state->source0.rmask = 0;
1295
-       spa_loop_add_source(state->data_loop, &state->source0);
1296
+       state->n_fds = 1;
1297
    } else {
1298
        /* ALSA period-based scheduling */
1299
        err = snd_pcm_poll_descriptors_count(state->hndl);
1300
@@ -2922,102 +3085,112 @@
1301
        /* We only add the source to the data loop if we're driving.
1302
         * This is done in setup_sources() */
1303
        for (int i = 0; i < state->n_fds; i++) {
1304
-           state->sourcei.func = NULL;
1305
-           state->sourcei.data = NULL;
1306
-           state->sourcei.fd = -1;
1307
-           state->sourcei.mask = 0;
1308
+           state->sourcei.func = alsa_wakeup_event;
1309
+           state->sourcei.data = state;
1310
+           state->sourcei.fd = state->pfdsi.fd;
1311
+           state->sourcei.mask = state->pfdsi.events;
1312
            state->sourcei.rmask = 0;
1313
        }
1314
    }
1315
 
1316
-   reset_buffers(state);
1317
-   state->alsa_sync = true;
1318
-   state->alsa_sync_warning = false;
1319
-   state->alsa_recovering = false;
1320
-   state->alsa_started = false;
1321
+   spa_list_for_each(follower, &state->followers, driver_link)
1322
+       if (follower != state)
1323
+           spa_alsa_start(follower);
1324
 
1325
-   /* start capture now, playback will start after first write. Without tsched, we start
1326
-    * right away so that the fds become active in poll right away. */
1327
-   if (state->stream == SND_PCM_STREAM_PLAYBACK) {
1328
-       spa_alsa_silence(state, state->start_delay + state->threshold + state->headroom);
1329
-       if (state->disable_tsched)
1330
-           do_start(state);
1331
+   /* start capture now. We should have some data when the timer or IRQ
1332
+    * goes off later */
1333
+   if (state->stream == SND_PCM_STREAM_CAPTURE) {
1334
+       if ((err = do_start(state)) < 0)
1335
+           return err;
1336
    }
1337
-   else if ((err = do_start(state)) < 0)
1338
-       return err;
1339
-
1340
-   spa_loop_invoke(state->data_loop, do_setup_sources, 0, NULL, 0, true, state);
1341
 
1342
    state->started = true;
1343
+   spa_loop_invoke(state->data_loop, do_state_sync, 0, NULL, 0, true, state);
1344
 
1345
+   /* playback will start after first write. Without tsched, we start
1346
+    * right away so that the fds become active in poll right away. */
1347
+   if (state->stream == SND_PCM_STREAM_PLAYBACK) {
1348
+       if (state->disable_tsched)
1349
+           if ((err = do_start(state)) < 0)
1350
+               return err;
1351
+   }
1352
    return 0;
1353
 }
1354
 
1355
+static struct state *find_state(uint32_t id)
1356
+{
1357
+   struct state *state;
1358
+   spa_list_for_each(state, &states, link) {
1359
+       if (state->clock != NULL && state->clock->id == id)
1360
+           return state;
1361
+   }
1362
+   return NULL;
1363
+}
1364
+
1365
 int spa_alsa_reassign_follower(struct state *state)
1366
 {
1367
    bool following, freewheel;
1368
+   struct spa_io_position *pos = state->position;
1369
+   struct spa_io_clock *clock = state->clock;
1370
+   struct state *driver;
1371
 
1372
-   if (!state->started)
1373
-       return 0;
1374
+   if (clock != NULL)
1375
+       spa_scnprintf(clock->name, sizeof(clock->name), "%s", state->clock_name);
1376
 
1377
-   following = is_following(state);
1378
+   following = pos && clock && pos->clock.id != clock->id;
1379
+
1380
+   driver = pos != NULL ? find_state(pos->clock.id) : NULL;
1381
+
1382
+   if (driver != state->driver) {
1383
+       spa_log_debug(state->log, "%p: reassign driver %p->%p", state, state->driver, driver);
1384
+       if (state->driver != NULL)
1385
+           spa_list_remove(&state->driver_link);
1386
+       if (driver != NULL) {
1387
+           spa_list_append(&driver->followers, &state->driver_link);
1388
+       }
1389
+       state->driver = driver;
1390
+   }
1391
    if (following != state->following) {
1392
        spa_log_debug(state->log, "%p: reassign follower %d->%d", state, state->following, following);
1393
        state->following = following;
1394
-       spa_loop_invoke(state->data_loop, do_setup_sources, 0, NULL, 0, true, state);
1395
+       setup_matching(state);
1396
    }
1397
-   setup_matching(state);
1398
-
1399
-   freewheel = state->position &&
1400
-       SPA_FLAG_IS_SET(state->position->clock.flags, SPA_IO_CLOCK_FLAG_FREEWHEEL);
1401
+   if (state->started)
1402
+       spa_loop_invoke(state->data_loop, do_state_sync, 0, NULL, 0, true, state);
1403
 
1404
+   freewheel = pos != NULL && SPA_FLAG_IS_SET(pos->clock.flags, SPA_IO_CLOCK_FLAG_FREEWHEEL);
1405
    if (state->freewheel != freewheel) {
1406
        spa_log_debug(state->log, "%p: freewheel %d->%d", state, state->freewheel, freewheel);
1407
        state->freewheel = freewheel;
1408
-       if (freewheel)
1409
-           snd_pcm_pause(state->hndl, 1);
1410
-       else
1411
-           snd_pcm_pause(state->hndl, 0);
1412
+       if (state->started) {
1413
+           if (freewheel)
1414
+               snd_pcm_pause(state->hndl, 1);
1415
+           else
1416
+               snd_pcm_pause(state->hndl, 0);
1417
+       }
1418
    }
1419
-
1420
    state->alsa_sync_warning = false;
1421
    return 0;
1422
 }
1423
 
1424
-static int do_remove_source(struct spa_loop *loop,
1425
-               bool async,
1426
-               uint32_t seq,
1427
-               const void *data,
1428
-               size_t size,
1429
-               void *user_data)
1430
-{
1431
-   struct state *state = user_data;
1432
-
1433
-   if (!state->disable_tsched) {
1434
-       spa_loop_remove_source(state->data_loop, &state->source0);
1435
-       set_timeout(state, 0);
1436
-   } else {
1437
-       clear_period_sources(state);
1438
-   }
1439
-   return 0;
1440
-}
1441
-
1442
 int spa_alsa_pause(struct state *state)
1443
 {
1444
-   int err;
1445
+   struct state *follower;
1446
 
1447
    if (!state->started)
1448
        return 0;
1449
 
1450
    spa_log_debug(state->log, "%p: pause", state);
1451
 
1452
-   spa_loop_invoke(state->data_loop, do_remove_source, 0, NULL, 0, true, state);
1453
+   state->started = false;
1454
+   spa_loop_invoke(state->data_loop, do_state_sync, 0, NULL, 0, true, state);
1455
 
1456
-   if ((err = snd_pcm_drop(state->hndl)) < 0)
1457
-       spa_log_error(state->log, "%s: snd_pcm_drop %s", state->props.device,
1458
-               snd_strerror(err));
1459
+   spa_list_for_each(follower, &state->followers, driver_link)
1460
+       spa_alsa_pause(follower);
1461
 
1462
-   state->started = false;
1463
+   do_drop(state);
1464
+
1465
+   state->prepared = false;
1466
 
1467
    return 0;
1468
 }
1469
pipewire-0.3.80.tar.gz/spa/plugins/alsa/alsa-pcm.h -> pipewire-0.3.81.tar.gz/spa/plugins/alsa/alsa-pcm.h Changed
111
 
1
@@ -82,6 +82,15 @@
2
    uint32_t rate;
3
 };
4
 
5
+struct rt_state {
6
+   struct spa_list followers;
7
+   struct state *driver;
8
+   struct spa_list driver_link;
9
+
10
+   unsigned int sources_added:1;
11
+   unsigned int following:1;
12
+};
13
+
14
 struct state {
15
    struct spa_handle handle;
16
    struct spa_node node;
17
@@ -97,6 +106,7 @@
18
    struct card *card;
19
    snd_pcm_stream_t stream;
20
    snd_output_t *output;
21
+   char name64;
22
 
23
    struct spa_hook_list hooks;
24
    struct spa_callbacks callbacks;
25
@@ -111,7 +121,9 @@
26
    struct spa_param_info paramsN_NODE_PARAMS;
27
    struct props props;
28
 
29
-   bool opened;
30
+   unsigned int opened:1;
31
+   unsigned int prepared:1;
32
+   unsigned int started:1;
33
    snd_pcm_t *hndl;
34
 
35
    bool have_format;
36
@@ -127,9 +139,9 @@
37
    uint32_t allowed_ratesMAX_RATES;
38
    uint32_t n_allowed_rates;
39
    struct channel_map default_pos;
40
-   unsigned int disable_mmap;
41
-   unsigned int disable_batch;
42
-   unsigned int disable_tsched;
43
+   unsigned int disable_mmap:1;
44
+   unsigned int disable_batch:1;
45
+   unsigned int disable_tsched:1;
46
    char clock_name64;
47
    uint32_t quantum_limit;
48
 
49
@@ -141,9 +153,9 @@
50
    size_t frame_size;
51
    size_t frame_scale;
52
    int blocks;
53
-   uint32_t rate_denom;
54
    uint32_t delay;
55
    uint32_t read_size;
56
+   uint32_t max_read;
57
 
58
    uint64_t port_info_all;
59
    struct spa_port_info port_info;
60
@@ -169,7 +181,6 @@
61
 
62
    size_t ready_offset;
63
 
64
-   bool started;
65
    /* Either a single source for tsched, or a set of pollfds from ALSA */
66
    struct spa_source sourceMAX_POLL;
67
    int timerfd;
68
@@ -183,7 +194,9 @@
69
    uint32_t max_delay;
70
    uint32_t htimestamp_error;
71
 
72
-   uint32_t duration;
73
+   struct spa_fraction driver_rate;
74
+   uint32_t driver_duration;
75
+
76
    unsigned int alsa_started:1;
77
    unsigned int alsa_sync:1;
78
    unsigned int alsa_sync_warning:1;
79
@@ -200,6 +213,8 @@
80
    unsigned int multi_rate:1;
81
    unsigned int htimestamp:1;
82
    unsigned int is_pro:1;
83
+   unsigned int sources_added:1;
84
+   unsigned int linked:1;
85
 
86
    uint64_t iec958_codecs;
87
 
88
@@ -222,6 +237,14 @@
89
    snd_ctl_t *ctl;
90
    snd_ctl_elem_value_t *pitch_elem;
91
    double last_rate;
92
+
93
+   struct spa_list link;
94
+
95
+   struct spa_list followers;
96
+   struct state *driver;
97
+   struct spa_list driver_link;
98
+
99
+   struct rt_state rt;
100
 };
101
 
102
 struct spa_pod *spa_alsa_enum_propinfo(struct state *state,
103
@@ -240,6 +263,7 @@
104
 int spa_alsa_clear(struct state *state);
105
 
106
 int spa_alsa_open(struct state *state, const char *params);
107
+int spa_alsa_prepare(struct state *state);
108
 int spa_alsa_start(struct state *state);
109
 int spa_alsa_reassign_follower(struct state *state);
110
 int spa_alsa_pause(struct state *state);
111
pipewire-0.3.80.tar.gz/spa/plugins/audioconvert/audioadapter.c -> pipewire-0.3.81.tar.gz/spa/plugins/audioconvert/audioadapter.c Changed
79
 
1
@@ -373,9 +373,6 @@
2
    struct spa_data *datas;
3
    uint64_t follower_flags, conv_flags;
4
 
5
-   if (this->target == this->follower)
6
-       return 0;
7
-
8
    spa_log_debug(this->log, "%p: n_buffers:%d", this, this->n_buffers);
9
 
10
    if (this->n_buffers > 0)
11
@@ -516,10 +513,9 @@
12
    this->have_format = format != NULL;
13
    if (format == NULL) {
14
        this->n_buffers = 0;
15
-   } else {
16
+   } else if (this->target != this->follower) {
17
        res = negotiate_buffers(this);
18
    }
19
-
20
    return res;
21
 }
22
 
23
@@ -555,7 +551,7 @@
24
    struct spa_latency_info latency;
25
    int res;
26
 
27
-   spa_log_info(this->log, "%p: %d:%d", this, direction, port_id);
28
+   spa_log_debug(this->log, "%p: %d:%d", this, direction, port_id);
29
 
30
    if (this->target == this->follower)
31
        return 0;
32
@@ -625,7 +621,7 @@
33
    int res = 0;
34
    struct spa_hook l;
35
 
36
-   spa_log_info(this->log, "%p: passthrough mode %d", this, passthrough);
37
+   spa_log_debug(this->log, "%p: passthrough mode %d", this, passthrough);
38
 
39
    if (this->passthrough != passthrough) {
40
        if (passthrough) {
41
@@ -846,9 +842,6 @@
42
    struct spa_pod_builder b = { 0 };
43
    int res;
44
 
45
-   if (this->target == this->follower)
46
-       return 0;
47
-
48
    spa_log_debug(this->log, "%p: have_format:%d", this, this->have_format);
49
 
50
    if (this->have_format)
51
@@ -920,12 +913,12 @@
52
    switch (SPA_NODE_COMMAND_ID(command)) {
53
    case SPA_NODE_COMMAND_Start:
54
        spa_log_debug(this->log, "%p: starting %d", this, this->started);
55
-       if (this->started)
56
-           return 0;
57
-       if ((res = negotiate_format(this)) < 0)
58
-           return res;
59
-       if ((res = negotiate_buffers(this)) < 0)
60
-           return res;
61
+       if (this->target != this->follower) {
62
+           if (this->started)
63
+               return 0;
64
+           if ((res = negotiate_format(this)) < 0)
65
+               return res;
66
+       }
67
        this->ready = true;
68
        this->warned = false;
69
        break;
70
@@ -1043,7 +1036,7 @@
71
    uint32_t i;
72
    int res;
73
 
74
-   spa_log_info(this->log, "%p: convert port info %s %p %08"PRIx64, this,
75
+   spa_log_debug(this->log, "%p: convert port info %s %p %08"PRIx64, this,
76
            this->direction == SPA_DIRECTION_INPUT ?
77
                "Input" : "Output", info, info->change_mask);
78
 
79
pipewire-0.3.80.tar.gz/spa/plugins/audioconvert/audioconvert.c -> pipewire-0.3.81.tar.gz/spa/plugins/audioconvert/audioconvert.c Changed
120
 
1
@@ -370,8 +370,9 @@
2
    }
3
    spa_list_init(&port->queue);
4
 
5
-   spa_log_info(this->log, "%p: add port %d:%d position:%s %d %d %d",
6
-           this, direction, port_id, port->position, is_dsp, is_monitor, is_control);
7
+   spa_log_debug(this->log, "%p: add port %d:%d position:%s %d %d %d",
8
+           this, direction, port_id, port->position, is_dsp,
9
+           is_monitor, is_control);
10
    emit_port_info(this, port, true);
11
 
12
    return 0;
13
@@ -1266,8 +1267,9 @@
14
        (info == NULL || memcmp(&dir->format, info, sizeof(*info)) == 0))
15
        return 0;
16
 
17
-   spa_log_info(this->log, "%p: port config direction:%d monitor:%d control:%d mode:%d %d", this,
18
-           direction, monitor, control, mode, dir->n_ports);
19
+   spa_log_debug(this->log, "%p: port config direction:%d monitor:%d "
20
+           "control:%d mode:%d %d", this, direction, monitor,
21
+           control, mode, dir->n_ports);
22
 
23
    for (i = 0; i < dir->n_ports; i++) {
24
        spa_node_emit_port_info(&this->hooks, direction, i, NULL);
25
@@ -2443,6 +2445,53 @@
26
    SPA_FLAG_CLEAR(b->flags, BUFFER_FLAG_QUEUED);
27
 }
28
 
29
+static void free_tmp(struct impl *this)
30
+{
31
+   uint32_t i;
32
+
33
+   spa_log_debug(this->log, "free tmp %d", this->empty_size);
34
+
35
+   free(this->empty);
36
+   this->empty = NULL;
37
+   this->empty_size = 0;
38
+   free(this->scratch);
39
+   this->scratch = NULL;
40
+   free(this->tmp0);
41
+   this->tmp0 = NULL;
42
+   free(this->tmp1);
43
+   this->tmp1 = NULL;
44
+   for (i = 0; i < MAX_PORTS; i++) {
45
+       this->tmp_datas0i = NULL;
46
+       this->tmp_datas1i = NULL;
47
+   }
48
+}
49
+
50
+static int ensure_tmp(struct impl *this, uint32_t maxsize)
51
+{
52
+   if (maxsize > this->empty_size) {
53
+       float *empty, *scratch, *tmp2;
54
+
55
+       spa_log_debug(this->log, "resize tmp %d -> %d", this->empty_size, maxsize);
56
+
57
+       if ((empty = realloc(this->empty, maxsize + MAX_ALIGN)) != NULL)
58
+           this->empty = empty;
59
+       if ((scratch = realloc(this->scratch, maxsize + MAX_ALIGN)) != NULL)
60
+           this->scratch = scratch;
61
+       if ((tmp0 = realloc(this->tmp0, (maxsize + MAX_ALIGN) * MAX_PORTS)) != NULL)
62
+           this->tmp0 = tmp0;
63
+       if ((tmp1 = realloc(this->tmp1, (maxsize + MAX_ALIGN) * MAX_PORTS)) != NULL)
64
+           this->tmp1 = tmp1;
65
+
66
+       if (empty == NULL || scratch == NULL || tmp0 == NULL || tmp1 == NULL) {
67
+           free_tmp(this);
68
+           return -ENOMEM;
69
+       }
70
+       memset(this->empty, 0, maxsize + MAX_ALIGN);
71
+       this->empty_size = maxsize;
72
+   }
73
+   return 0;
74
+}
75
+
76
 static int
77
 impl_node_port_use_buffers(void *object,
78
               enum spa_direction direction,
79
@@ -2454,6 +2503,7 @@
80
    struct impl *this = object;
81
    struct port *port;
82
    uint32_t i, j, maxsize;
83
+   int res;
84
 
85
    spa_return_val_if_fail(this != NULL, -EINVAL);
86
 
87
@@ -2510,17 +2560,9 @@
88
        if (direction == SPA_DIRECTION_OUTPUT)
89
            queue_buffer(this, port, i);
90
    }
91
-   if (maxsize > this->empty_size) {
92
-       this->empty = realloc(this->empty, maxsize + MAX_ALIGN);
93
-       this->scratch = realloc(this->scratch, maxsize + MAX_ALIGN);
94
-       this->tmp0 = realloc(this->tmp0, (maxsize + MAX_ALIGN) * MAX_PORTS);
95
-       this->tmp1 = realloc(this->tmp1, (maxsize + MAX_ALIGN) * MAX_PORTS);
96
-       if (this->empty == NULL || this->scratch == NULL ||
97
-           this->tmp0 == NULL || this->tmp1 == NULL)
98
-           return -errno;
99
-       memset(this->empty, 0, maxsize + MAX_ALIGN);
100
-       this->empty_size = maxsize;
101
-   }
102
+   if ((res = ensure_tmp(this, maxsize)) < 0)
103
+       return res;
104
+
105
    port->n_buffers = n_buffers;
106
 
107
    return 0;
108
@@ -3248,10 +3290,7 @@
109
    free_dir(&this->dirSPA_DIRECTION_INPUT);
110
    free_dir(&this->dirSPA_DIRECTION_OUTPUT);
111
 
112
-   free(this->empty);
113
-   free(this->scratch);
114
-   free(this->tmp0);
115
-   free(this->tmp1);
116
+   free_tmp(this);
117
 
118
    if (this->resample.free)
119
        resample_free(&this->resample);
120
pipewire-0.3.80.tar.gz/spa/plugins/audiomixer/audiomixer.c -> pipewire-0.3.81.tar.gz/spa/plugins/audiomixer/audiomixer.c Changed
14
 
1
@@ -920,6 +920,12 @@
2
    this->log = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_Log);
3
    spa_log_topic_init(this->log, log_topic);
4
 
5
+   this->data_loop = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_DataLoop);
6
+   if (this->data_loop == NULL) {
7
+       spa_log_error(this->log, "a data loop is needed");
8
+       return -EINVAL;
9
+   }
10
+
11
    this->cpu = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_CPU);
12
    if (this->cpu) {
13
        this->cpu_flags = spa_cpu_get_flags(this->cpu);
14
pipewire-0.3.80.tar.gz/spa/plugins/bluez5/a2dp-codec-opus.c -> pipewire-0.3.81.tar.gz/spa/plugins/bluez5/a2dp-codec-opus.c Changed
10
 
1
@@ -443,7 +443,7 @@
2
        OPUS_05_SET_LOCATION(caps->bidi, props->bidi_location);
3
        break;
4
    default:
5
-       spa_assert(false);
6
+       spa_assert_not_reached();
7
    };
8
 
9
    return 0;
10
pipewire-0.3.80.tar.gz/spa/plugins/bluez5/backend-native.c -> pipewire-0.3.81.tar.gz/spa/plugins/bluez5/backend-native.c Changed
51
 
1
@@ -1915,11 +1915,12 @@
2
    .destroy = sco_destroy_cb,
3
 };
4
 
5
-static struct rfcomm *device_find_rfcomm(struct impl *backend, struct spa_bt_device *device)
6
+static struct rfcomm *device_find_rfcomm(struct impl *backend, struct spa_bt_device *device,
7
+       enum spa_bt_profile profile)
8
 {
9
    struct rfcomm *rfcomm;
10
    spa_list_for_each(rfcomm, &backend->rfcomm_list, link) {
11
-       if (rfcomm->device == device)
12
+       if (rfcomm->device == device && (rfcomm->profile & profile))
13
            return rfcomm;
14
    }
15
    return NULL;
16
@@ -1931,8 +1932,8 @@
17
    struct impl *backend = data;
18
    struct rfcomm *rfcomm;
19
 
20
-   rfcomm = device_find_rfcomm(backend, device);
21
-   if (rfcomm == NULL || rfcomm->profile != SPA_BT_PROFILE_HFP_HF)
22
+   rfcomm = device_find_rfcomm(backend, device, SPA_BT_PROFILE_HFP_HF);
23
+   if (rfcomm == NULL)
24
        return -ENOTSUP;
25
 
26
    if (codec == HFP_AUDIO_CODEC_CVSD)
27
@@ -2097,10 +2098,12 @@
28
    int res;
29
 
30
    res = backend_native_supports_codec(data, device, codec);
31
-   if (res <= 0)
32
+   if (res < 0)
33
+       return res;
34
+   else if (!res)
35
        return -EINVAL;
36
 
37
-   rfcomm = device_find_rfcomm(backend, device);
38
+   rfcomm = device_find_rfcomm(backend, device, SPA_BT_PROFILE_HFP_HF);
39
    if (rfcomm == NULL)
40
        return -ENOTSUP;
41
 
42
@@ -2399,7 +2402,7 @@
43
    DBusMessageIter it4;
44
    dbus_bool_t autoconnect;
45
    dbus_uint16_t version, chan, features;
46
-   char *str;
47
+   const char *str;
48
 
49
    if (!(backend->enabled_profiles & spa_bt_profile_from_uuid(uuid)))
50
        return -ECANCELED;
51
pipewire-0.3.80.tar.gz/spa/plugins/bluez5/bap-codec-caps.h -> pipewire-0.3.81.tar.gz/spa/plugins/bluez5/bap-codec-caps.h Changed
40
 
1
@@ -97,14 +97,17 @@
2
 };
3
 
4
 struct bap_endpoint_qos {
5
-        uint8_t  framing;
6
-        uint8_t  phy;
7
-        uint8_t  retransmission;
8
-        uint16_t latency;
9
-        uint32_t delay_min;
10
-        uint32_t delay_max;
11
-        uint32_t preferred_delay_min;
12
-        uint32_t preferred_delay_max;
13
+   uint8_t framing;
14
+   uint8_t phy;
15
+   uint8_t retransmission;
16
+   uint16_t latency;
17
+   uint32_t delay_min;
18
+   uint32_t delay_max;
19
+   uint32_t preferred_delay_min;
20
+   uint32_t preferred_delay_max;
21
+   uint32_t locations;
22
+   uint16_t supported_context;
23
+   uint16_t context;
24
 };
25
 
26
 struct bap_codec_qos {
27
@@ -118,4 +121,12 @@
28
    uint8_t target_latency;
29
 };
30
 
31
+struct bap_codec_qos_full {
32
+   uint8_t cig;
33
+   uint8_t cis;
34
+   uint8_t big;
35
+   uint8_t bis;
36
+   struct bap_codec_qos qos;
37
+};
38
+
39
 #endif
40
pipewire-0.3.80.tar.gz/spa/plugins/bluez5/bluez5-dbus.c -> pipewire-0.3.81.tar.gz/spa/plugins/bluez5/bluez5-dbus.c Changed
606
 
1
@@ -627,6 +627,242 @@
2
 static void append_basic_array_variant_dict_entry(DBusMessageIter *dict, const char* key, const char* variant_type_str, const char* array_type_str, int array_type_int, void* data, int data_size);
3
 static struct spa_bt_remote_endpoint *remote_endpoint_find(struct spa_bt_monitor *monitor, const char *path);
4
 
5
+static bool check_iter_signature(DBusMessageIter *it, const char *sig)
6
+{
7
+   char *v;
8
+   bool res;
9
+   v = dbus_message_iter_get_signature(it);
10
+   res = spa_streq(v, sig);
11
+   dbus_free(v);
12
+   return res;
13
+}
14
+
15
+static void parse_codec_qos(struct spa_bt_monitor *monitor, DBusMessageIter *iter, struct bap_codec_qos_full *qos)
16
+{
17
+   DBusMessageIter dict_iter = *iter;
18
+
19
+   memset(qos, 0, sizeof(*qos));
20
+   qos->cig = 0xff;
21
+   qos->cis = 0xff;
22
+   qos->big = 0xff;
23
+   qos->bis = 0xff;
24
+
25
+   if (!check_iter_signature(&dict_iter, "{sv}")) {
26
+       spa_log_warn(monitor->log, "Invalid BAP QoS in DBus");
27
+       return;
28
+   }
29
+
30
+   while (dbus_message_iter_get_arg_type(&dict_iter) != DBUS_TYPE_INVALID) {
31
+       DBusMessageIter it2;
32
+       const char *key;
33
+       int type;
34
+
35
+       dbus_message_iter_recurse(&dict_iter, &it0);
36
+       dbus_message_iter_get_basic(&it0, &key);
37
+       dbus_message_iter_next(&it0);
38
+       dbus_message_iter_recurse(&it0, &it1);
39
+
40
+       type = dbus_message_iter_get_arg_type(&it1);
41
+
42
+       if (type == DBUS_TYPE_BYTE) {
43
+           uint8_t value;
44
+
45
+           dbus_message_iter_get_basic(&it1, &value);
46
+           spa_log_debug(monitor->log, "qos: %s=%d", key, (int)value);
47
+
48
+           if (spa_streq(key, "PHY"))
49
+               qos->qos.phy = value;
50
+           else if (spa_streq(key, "Retransmissions"))
51
+               qos->qos.retransmission = value;
52
+           else if (spa_streq(key, "CIG"))
53
+               qos->cig = value;
54
+           else if (spa_streq(key, "CIS"))
55
+               qos->cis = value;
56
+           else if (spa_streq(key, "BIG"))
57
+               qos->big = value;
58
+           else if (spa_streq(key, "BIS"))
59
+               qos->bis = value;
60
+           else if (spa_streq(key, "TargetLatency"))
61
+               qos->qos.target_latency = value;
62
+           else if (spa_streq(key, "Framing"))
63
+               qos->qos.framing = value;
64
+       }
65
+       else if (type == DBUS_TYPE_UINT16) {
66
+           dbus_uint16_t value;
67
+
68
+           dbus_message_iter_get_basic(&it1, &value);
69
+           spa_log_debug(monitor->log, "qos: %s=%d", key, (int)value);
70
+
71
+           if (spa_streq(key, "SDU"))
72
+               qos->qos.sdu = value;
73
+           else if (spa_streq(key, "Latency") || spa_streq(key, "MaximumLatency"))
74
+               qos->qos.latency = value;
75
+       }
76
+       else if (type == DBUS_TYPE_UINT32) {
77
+           dbus_uint32_t value;
78
+
79
+           dbus_message_iter_get_basic(&it1, &value);
80
+           spa_log_debug(monitor->log, "qos: %s=%d", key, (int)value);
81
+
82
+           if (spa_streq(key, "Interval"))
83
+               qos->qos.interval = value;
84
+           else if (spa_streq(key, "PresentationDelay"))
85
+               qos->qos.delay = value;
86
+       }
87
+
88
+       dbus_message_iter_next(&dict_iter);
89
+   }
90
+}
91
+
92
+static void parse_endpoint_qos(struct spa_bt_monitor *monitor, DBusMessageIter *iter,
93
+       struct bap_endpoint_qos *qos)
94
+{
95
+   DBusMessageIter dict_iter = *iter;
96
+
97
+   if (!check_iter_signature(&dict_iter, "{sv}")) {
98
+       spa_log_warn(monitor->log, "Invalid BAP Endpoint QoS in DBus");
99
+       return;
100
+   }
101
+
102
+   while (dbus_message_iter_get_arg_type(&dict_iter) != DBUS_TYPE_INVALID) {
103
+       DBusMessageIter it2;
104
+       const char *key;
105
+       int type;
106
+
107
+       dbus_message_iter_recurse(&dict_iter, &it0);
108
+       dbus_message_iter_get_basic(&it0, &key);
109
+       dbus_message_iter_next(&it0);
110
+       dbus_message_iter_recurse(&it0, &it1);
111
+
112
+       type = dbus_message_iter_get_arg_type(&it1);
113
+
114
+       if (type == DBUS_TYPE_BYTE) {
115
+           uint8_t value;
116
+
117
+           dbus_message_iter_get_basic(&it1, &value);
118
+           spa_log_debug(monitor->log, "ep qos: %s=%d", key, (int)value);
119
+
120
+           if (spa_streq(key, "Framing"))
121
+               qos->framing = value;
122
+           else if (spa_streq(key, "PHY"))
123
+               qos->phy = value;
124
+           else if (spa_streq(key, "Retransmissions"))
125
+               qos->retransmission = value;
126
+       } else if (type == DBUS_TYPE_UINT16) {
127
+           dbus_uint16_t value;
128
+
129
+           dbus_message_iter_get_basic(&it1, &value);
130
+           spa_log_debug(monitor->log, "ep qos: %s=%d", key, (int)value);
131
+
132
+           if (spa_streq(key, "Latency") || spa_streq(key, "MaximumLatency"))
133
+               qos->latency = value;
134
+           else if (spa_streq(key, "Context"))
135
+               qos->context = value;
136
+           else if (spa_streq(key, "SupportedContext"))
137
+               qos->supported_context = value;
138
+       } else if (type == DBUS_TYPE_UINT32) {
139
+           dbus_uint32_t value;
140
+
141
+           dbus_message_iter_get_basic(&it1, &value);
142
+           spa_log_debug(monitor->log, "ep qos: %s=%d", key, (int)value);
143
+
144
+           if (spa_streq(key, "MinimumDelay"))
145
+               qos->delay_min = value;
146
+           else if (spa_streq(key, "MaximumDelay"))
147
+               qos->delay_max = value;
148
+           else if (spa_streq(key, "PreferredMinimumDelay"))
149
+               qos->preferred_delay_min = value;
150
+           else if (spa_streq(key, "PreferredMaximumDelay"))
151
+               qos->preferred_delay_max = value;
152
+           else if (spa_streq(key, "Locations") || spa_streq(key, "Location"))
153
+               qos->locations = value;
154
+       }
155
+
156
+       dbus_message_iter_next(&dict_iter);
157
+   }
158
+}
159
+
160
+static int parse_endpoint_props(struct spa_bt_monitor *monitor, DBusMessageIter *iter,
161
+       uint8_t capsA2DP_MAX_CAPS_SIZE, int *caps_size, const char **endpoint_path,
162
+       struct bap_endpoint_qos *qos)
163
+{
164
+   DBusMessageIter dict_iter = *iter;
165
+   const char *key = NULL;
166
+   int type = 0;
167
+
168
+   memset(caps, 0, A2DP_MAX_CAPS_SIZE);
169
+   *endpoint_path = NULL;
170
+   memset(qos, 0, sizeof(*qos));
171
+
172
+   if (!check_iter_signature(&dict_iter, "{sv}")) {
173
+       spa_log_warn(monitor->log, "Invalid BAP Endpoint QoS in DBus");
174
+       return -EINVAL;
175
+   }
176
+
177
+   while (dbus_message_iter_get_arg_type(&dict_iter) != DBUS_TYPE_INVALID) {
178
+       DBusMessageIter it3;
179
+
180
+       dbus_message_iter_recurse(&dict_iter, &it0);
181
+       dbus_message_iter_get_basic(&it0, &key);
182
+       dbus_message_iter_next(&it0);
183
+       dbus_message_iter_recurse(&it0, &it1);
184
+
185
+       type = dbus_message_iter_get_arg_type(&it1);
186
+
187
+       if (spa_streq(key, "Capabilities")) {
188
+           uint8_t *buf;
189
+
190
+           if (type != DBUS_TYPE_ARRAY)
191
+               goto bad_property;
192
+
193
+           dbus_message_iter_recurse(&it1, &it2);
194
+           type = dbus_message_iter_get_arg_type(&it2);
195
+           if (type != DBUS_TYPE_BYTE)
196
+               goto bad_property;
197
+
198
+           dbus_message_iter_get_fixed_array(&it2, &buf, caps_size);
199
+           if (*caps_size > A2DP_MAX_CAPS_SIZE) {
200
+               spa_log_error(monitor->log, "%s size:%d too large", key, (int)*caps_size);
201
+               return -EINVAL;
202
+           }
203
+           memcpy(caps, buf, *caps_size);
204
+
205
+           spa_log_info(monitor->log, "%p: %s size:%d", monitor, key, *caps_size);
206
+           spa_debug_log_mem(monitor->log, SPA_LOG_LEVEL_DEBUG, ' ', caps, (size_t)*caps_size);
207
+       } else if (spa_streq(key, "Endpoint")) {
208
+           if (type != DBUS_TYPE_OBJECT_PATH)
209
+               goto bad_property;
210
+
211
+           dbus_message_iter_get_basic(&it1, endpoint_path);
212
+
213
+           spa_log_info(monitor->log, "%p: %s %s", monitor, key, *endpoint_path);
214
+       } else if (spa_streq(key, "QoS")) {
215
+           if (!check_iter_signature(&it1, "a{sv}"))
216
+               goto bad_property;
217
+
218
+           dbus_message_iter_recurse(&it1, &it2);
219
+           parse_endpoint_qos(monitor, &it2, qos);
220
+       } else if (spa_streq(key, "Locations")) {
221
+           dbus_uint32_t value;
222
+
223
+           if (type != DBUS_TYPE_UINT32)
224
+               goto bad_property;
225
+
226
+           dbus_message_iter_get_basic(&it1, &value);
227
+           spa_log_debug(monitor->log, "ep qos: %s=%d", key, (int)value);
228
+           qos->locations = value;
229
+       }
230
+
231
+       dbus_message_iter_next(&dict_iter);
232
+   }
233
+
234
+   return 0;
235
+
236
+bad_property:
237
+   spa_log_error(monitor->log, "Property %s of wrong type %c", key, (char)type);
238
+   return -EINVAL;
239
+}
240
+
241
 static DBusHandlerResult endpoint_select_properties(DBusConnection *conn, DBusMessage *m, void *userdata)
242
 {
243
    struct spa_bt_monitor *monitor = userdata;
244
@@ -635,6 +871,7 @@
245
    spa_autoptr(DBusMessage) r = NULL;
246
    int res;
247
    const struct media_codec *codec;
248
+   struct spa_bt_remote_endpoint *ep;
249
    bool sink;
250
    const char *err_msg = "Unknown error";
251
    struct spa_dict settings;
252
@@ -670,121 +907,28 @@
253
     */
254
    codec = media_endpoint_to_codec(monitor, path, &sink, NULL);
255
    spa_log_debug(monitor->log, "%p: %s codec:%s", monitor, path, codec ? codec->name : "<null>");
256
-   if (!codec) {
257
+   if (!codec || !codec->bap || !codec->get_qos) {
258
        spa_log_error(monitor->log, "Unsupported codec");
259
        err_msg = "Unsupported codec";
260
        goto error;
261
    }
262
 
263
-   /* Parse transport properties */
264
-   while (dbus_message_iter_get_arg_type(&props) == DBUS_TYPE_DICT_ENTRY) {
265
-       const char *key;
266
-       DBusMessageIter value, entry;
267
-       int type;
268
-
269
-       dbus_message_iter_recurse(&props, &entry);
270
-       dbus_message_iter_get_basic(&entry, &key);
271
-
272
-       dbus_message_iter_next(&entry);
273
-       dbus_message_iter_recurse(&entry, &value);
274
-
275
-       type = dbus_message_iter_get_arg_type(&value);
276
-
277
-       if (spa_streq(key, "Capabilities")) {
278
-           DBusMessageIter array;
279
-           uint8_t *buf;
280
-
281
-           if (type != DBUS_TYPE_ARRAY) {
282
-               spa_log_error(monitor->log, "Property %s of wrong type %c", key, (char)type);
283
-               goto error_invalid;
284
-           }
285
-
286
-           dbus_message_iter_recurse(&value, &array);
287
-           type = dbus_message_iter_get_arg_type(&array);
288
-           if (type != DBUS_TYPE_BYTE) {
289
-               spa_log_error(monitor->log, "%s is an array of wrong type %c", key, (char)type);
290
-               goto error_invalid;
291
-           }
292
-
293
-           dbus_message_iter_get_fixed_array(&array, &buf, &caps_size);
294
-           if (caps_size > (int)sizeof(caps)) {
295
-               spa_log_error(monitor->log, "%s size:%d too large", key, (int)caps_size);
296
-               goto error_invalid;
297
-           }
298
-           memcpy(caps, buf, caps_size);
299
-
300
-           spa_log_info(monitor->log, "%p: %s %s size:%d", monitor, path, key, caps_size);
301
-           spa_debug_log_mem(monitor->log, SPA_LOG_LEVEL_DEBUG, ' ', caps, (size_t)caps_size);
302
-       } else if (spa_streq(key, "Endpoint")) {
303
-           if (type != DBUS_TYPE_OBJECT_PATH) {
304
-               spa_log_error(monitor->log, "Property %s of wrong type %c", key, (char)type);
305
-               goto error_invalid;
306
-           }
307
-
308
-           dbus_message_iter_get_basic(&value, &endpoint_path);
309
-
310
-           spa_log_info(monitor->log, "%p: %s %s %s", monitor, path, key, endpoint_path);
311
-       } else if (type == DBUS_TYPE_BYTE) {
312
-           uint8_t v;
313
-           dbus_message_iter_get_basic(&value, &v);
314
-
315
-           spa_log_info(monitor->log, "%p: %s %s 0x%x", monitor, path, key, (unsigned int)v);
316
-
317
-           if (spa_streq(key, "Framing"))
318
-               endpoint_qos.framing = v;
319
-           else if (spa_streq(key, "PHY"))
320
-               endpoint_qos.phy = v;
321
-           else
322
-               spa_log_info(monitor->log, "Unknown property %s", key);
323
-       } else if (type == DBUS_TYPE_UINT16) {
324
-           dbus_uint16_t v;
325
-           dbus_message_iter_get_basic(&value, &v);
326
-
327
-           spa_log_info(monitor->log, "%p: %s %s 0x%x", monitor, path, key, (unsigned int)v);
328
-
329
-           if (spa_streq(key, "Latency"))
330
-               endpoint_qos.latency = v;
331
-           else
332
-               spa_log_info(monitor->log, "Unknown property %s", key);
333
-       } else if (type == DBUS_TYPE_UINT32) {
334
-           dbus_uint32_t v;
335
-           dbus_message_iter_get_basic(&value, &v);
336
-
337
-           spa_log_info(monitor->log, "%p: %s %s 0x%x", monitor, path, key, (unsigned int)v);
338
-
339
-           if (spa_streq(key, "MinimumDelay"))
340
-               endpoint_qos.delay_min = v;
341
-           else if (spa_streq(key, "MaximumDelay"))
342
-               endpoint_qos.delay_max = v;
343
-           else if (spa_streq(key, "PreferredMinimumDelay"))
344
-               endpoint_qos.preferred_delay_min = v;
345
-           else if (spa_streq(key, "PreferredMaximumDelay"))
346
-               endpoint_qos.preferred_delay_max = v;
347
-           else if (spa_streq(key, "Location"))
348
-               spa_scnprintf(locations, sizeof(locations), "%"PRIu32, v);
349
-           else
350
-               spa_log_info(monitor->log, "Unknown property %s", key);
351
-       } else {
352
-           spa_log_info(monitor->log, "Unknown property %s", key);
353
-       }
354
+   /* Parse endpoint properties */
355
+   if (parse_endpoint_props(monitor, &props, caps, &caps_size, &endpoint_path, &endpoint_qos) < 0)
356
+       goto error_invalid;
357
+   if (endpoint_qos.locations)
358
+       spa_scnprintf(locations, sizeof(locations), "%"PRIu32, endpoint_qos.locations);
359
 
360
-       dbus_message_iter_next(&props);
361
+   ep = remote_endpoint_find(monitor, endpoint_path);
362
+   if (!ep) {
363
+       spa_log_warn(monitor->log, "Unable to find remote endpoint for %s", endpoint_path);
364
+       goto error_invalid;
365
    }
366
 
367
-   if (codec->bap) {
368
-       struct spa_bt_remote_endpoint *ep;
369
-
370
-       ep = remote_endpoint_find(monitor, endpoint_path);
371
-       if (!ep) {
372
-           spa_log_warn(monitor->log, "Unable to find remote endpoint for %s", endpoint_path);
373
-           goto error_invalid;
374
-       }
375
-
376
-       /* Call of SelectProperties means that local device acts as an initiator
377
-        * and therefor remote endpoint is an acceptor
378
-        */
379
-       ep->acceptor = true;
380
-   }
381
+   /* Call of SelectProperties means that local device acts as an initiator
382
+    * and therefor remote endpoint is an acceptor
383
+    */
384
+   ep->acceptor = true;
385
 
386
    for (i = 0; i < (int)monitor->global_settings.n_items; ++i)
387
        setting_itemsi = monitor->global_settings.itemsi;
388
@@ -812,10 +956,10 @@
389
            &dict);
390
    append_basic_array_variant_dict_entry(&dict, "Capabilities", "ay", "y", DBUS_TYPE_BYTE, &config, conf_size);
391
 
392
-   if (codec->get_qos) {
393
+   {
394
        struct bap_codec_qos qos;
395
-       dbus_bool_t framing;
396
-       const char *phy_str;
397
+       DBusMessageIter entry, variant, qos_dict;
398
+       const char *entry_key = "QoS";
399
 
400
        spa_zero(qos);
401
 
402
@@ -826,21 +970,29 @@
403
            goto error_invalid;
404
        }
405
 
406
-       append_basic_variant_dict_entry(&dict, "Interval", DBUS_TYPE_UINT32, "u", &qos.interval);
407
-       framing = (qos.framing ? TRUE : FALSE);
408
-       append_basic_variant_dict_entry(&dict, "Framing", DBUS_TYPE_BOOLEAN, "b", &framing);
409
-       if (qos.phy == 0x1)
410
-           phy_str = "1M";
411
-       else if (qos.phy == 0x2)
412
-           phy_str = "2M";
413
-       else
414
-           spa_assert_not_reached();
415
-       append_basic_variant_dict_entry(&dict, "PHY", DBUS_TYPE_STRING, "s", &phy_str);
416
-       append_basic_variant_dict_entry(&dict, "SDU", DBUS_TYPE_UINT16, "q", &qos.sdu);
417
-       append_basic_variant_dict_entry(&dict, "Retransmissions", DBUS_TYPE_BYTE, "y", &qos.retransmission);
418
-       append_basic_variant_dict_entry(&dict, "Latency", DBUS_TYPE_UINT16, "q", &qos.latency);
419
-       append_basic_variant_dict_entry(&dict, "Delay", DBUS_TYPE_UINT32, "u", &qos.delay);
420
-       append_basic_variant_dict_entry(&dict, "TargetLatency", DBUS_TYPE_BYTE, "y", &qos.target_latency);
421
+       dbus_message_iter_open_container(&dict, DBUS_TYPE_DICT_ENTRY, NULL, &entry);
422
+       dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &entry_key);
423
+       dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT, "a{sv}", &variant);
424
+
425
+       dbus_message_iter_open_container(&variant, DBUS_TYPE_ARRAY,
426
+           DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
427
+           DBUS_TYPE_STRING_AS_STRING
428
+           DBUS_TYPE_VARIANT_AS_STRING
429
+           DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
430
+           &qos_dict);
431
+
432
+       append_basic_variant_dict_entry(&qos_dict, "Interval", DBUS_TYPE_UINT32, "u", &qos.interval);
433
+       append_basic_variant_dict_entry(&qos_dict, "Framing", DBUS_TYPE_BYTE, "y", &qos.framing);
434
+       append_basic_variant_dict_entry(&qos_dict, "PHY", DBUS_TYPE_BYTE, "y", &qos.phy);
435
+       append_basic_variant_dict_entry(&qos_dict, "SDU", DBUS_TYPE_UINT16, "q", &qos.sdu);
436
+       append_basic_variant_dict_entry(&qos_dict, "Retransmissions", DBUS_TYPE_BYTE, "y", &qos.retransmission);
437
+       append_basic_variant_dict_entry(&qos_dict, "Latency", DBUS_TYPE_UINT16, "q", &qos.latency);
438
+       append_basic_variant_dict_entry(&qos_dict, "PresentationDelay", DBUS_TYPE_UINT32, "u", &qos.delay);
439
+       append_basic_variant_dict_entry(&qos_dict, "TargetLatency", DBUS_TYPE_BYTE, "y", &qos.target_latency);
440
+
441
+       dbus_message_iter_close_container(&variant, &qos_dict);
442
+       dbus_message_iter_close_container(&entry, &variant);
443
+       dbus_message_iter_close_container(&dict, &entry);
444
    }
445
 
446
    dbus_message_iter_close_container(&iter, &dict);
447
@@ -869,16 +1021,6 @@
448
    return NULL;
449
 }
450
 
451
-static bool check_iter_signature(DBusMessageIter *it, const char *sig)
452
-{
453
-   char *v;
454
-   bool res;
455
-   v = dbus_message_iter_get_signature(it);
456
-   res = spa_streq(v, sig);
457
-   dbus_free(v);
458
-   return res;
459
-}
460
-
461
 static int parse_modalias(const char *modalias, uint16_t *source, uint16_t *vendor,
462
        uint16_t *product, uint16_t *version)
463
 {
464
@@ -1435,7 +1577,7 @@
465
 int spa_bt_format_vendor_product_id(uint16_t source_id, uint16_t vendor_id, uint16_t product_id,
466
        char *vendor_str, int vendor_str_size, char *product_str, int product_str_size)
467
 {
468
-   char *source_str;
469
+   const char *source_str;
470
 
471
    switch (source_id) {
472
    case SOURCE_ID_USB:
473
@@ -3149,40 +3291,36 @@
474
                spa_bt_transport_volume_changed(transport);
475
        }
476
        else if (spa_streq(key, "Delay")) {
477
-           if (transport->profile & (SPA_BT_PROFILE_BAP_SINK | SPA_BT_PROFILE_BAP_SOURCE)) {
478
-               uint32_t value;
479
-
480
-               if (type != DBUS_TYPE_UINT32)
481
-                   goto next;
482
-               dbus_message_iter_get_basic(&it1, &value);
483
-
484
-               spa_log_debug(monitor->log, "transport %p: %s=%d", transport, key, (int)value);
485
-
486
-               transport->delay_us = value;
487
-           } else {
488
-               uint16_t value;
489
+           uint16_t value;
490
 
491
-               if (type != DBUS_TYPE_UINT16)
492
-                   goto next;
493
-               dbus_message_iter_get_basic(&it1, &value);
494
+           if (type != DBUS_TYPE_UINT16)
495
+               goto next;
496
+           dbus_message_iter_get_basic(&it1, &value);
497
 
498
-               spa_log_debug(monitor->log, "transport %p: %s=%d", transport, key, (int)value);
499
+           spa_log_debug(monitor->log, "transport %p: %s=%d", transport, key, (int)value);
500
 
501
-               transport->delay_us = value * 100;
502
-           }
503
+           transport->delay_us = value * 100;
504
 
505
            spa_bt_transport_emit_delay_changed(transport);
506
        }
507
-       else if (spa_streq(key, "Latency")) {
508
-           uint16_t value;
509
+       else if (spa_streq(key, "QoS")) {
510
+           struct bap_codec_qos_full qos;
511
+           DBusMessageIter value;
512
 
513
-           if (type != DBUS_TYPE_UINT16)
514
+           if (!check_iter_signature(&it1, "a{sv}"))
515
                goto next;
516
-           dbus_message_iter_get_basic(&it1, &value);
517
 
518
-           spa_log_debug(monitor->log, "transport %p: %s=%d", transport, key, (int)value);
519
+           dbus_message_iter_recurse(&it1, &value);
520
+           parse_codec_qos(monitor, &value, &qos);
521
+
522
+           transport->bap_cig = qos.cig;
523
+           transport->bap_cis = qos.cis;
524
+           transport->bap_big = qos.big;
525
+           transport->bap_bis = qos.bis;
526
+           transport->delay_us = qos.qos.delay;
527
+           transport->latency_us = (unsigned int)qos.qos.latency * 1000;
528
+           transport->bap_interval = qos.qos.interval;
529
 
530
-           transport->latency_us = value * 1000;
531
            spa_bt_transport_emit_delay_changed(transport);
532
        }
533
        else if (spa_streq(key, "Links")) {
534
@@ -3214,71 +3352,7 @@
535
                dbus_message_iter_next(&iter);
536
            }
537
        }
538
-       else if (spa_streq(key, "Interval")) {
539
-           uint32_t value;
540
-
541
-           if (type != DBUS_TYPE_UINT32)
542
-               goto next;
543
-           dbus_message_iter_get_basic(&it1, &value);
544
-
545
-           spa_log_debug(monitor->log, "transport %p: %s=%d", transport, key, (int)value);
546
-           transport->bap_interval = value;
547
-       }
548
-       else if (spa_streq(key, "Framing")) {
549
-           dbus_bool_t value;
550
-
551
-           if (type != DBUS_TYPE_BOOLEAN)
552
-               goto next;
553
-           dbus_message_iter_get_basic(&it1, &value);
554
-
555
-           spa_log_debug(monitor->log, "transport %p: %s=%d", transport, key, (int)value);
556
-       }
557
-       else if (spa_streq(key, "SDU")) {
558
-           uint16_t value;
559
-
560
-           if (type != DBUS_TYPE_UINT16)
561
-               goto next;
562
-           dbus_message_iter_get_basic(&it1, &value);
563
-
564
-           spa_log_debug(monitor->log, "transport %p: %s=%d", transport, key, (int)value);
565
-       }
566
-       else if (spa_streq(key, "Retransmissions")) {
567
-           uint8_t value;
568
-
569
-           if (type != DBUS_TYPE_BYTE)
570
-               goto next;
571
-           dbus_message_iter_get_basic(&it1, &value);
572
-
573
-           spa_log_debug(monitor->log, "transport %p: %s=%d", transport, key, (int)value);
574
-       }
575
-       else if (spa_streq(key, "CIG") || spa_streq(key, "CIS")) {
576
-           uint8_t value;
577
 
578
-           if (type != DBUS_TYPE_BYTE)
579
-               goto next;
580
-           dbus_message_iter_get_basic(&it1, &value);
581
-
582
-           spa_log_debug(monitor->log, "transport %p: %s=%d", transport, key, (int)value);
583
-
584
-           if (spa_streq(key, "CIG"))
585
-               transport->bap_cig = value;
586
-           else
587
-               transport->bap_cis = value;
588
-       }
589
-       else if (spa_streq(key, "BIG") || spa_streq(key, "BIS")) {
590
-           uint8_t value;
591
-
592
-           if (type != DBUS_TYPE_BYTE)
593
-               goto next;
594
-           dbus_message_iter_get_basic(&it1, &value);
595
-
596
-           spa_log_debug(monitor->log, "transport %p: %s=%d", transport, key, (int)value);
597
-
598
-           if (spa_streq(key, "BIG"))
599
-               transport->bap_big = value;
600
-           else
601
-               transport->bap_bis = value;
602
-       }
603
 next:
604
        dbus_message_iter_next(props_iter);
605
    }
606
pipewire-0.3.80.tar.gz/spa/plugins/bluez5/bluez5-device.c -> pipewire-0.3.81.tar.gz/spa/plugins/bluez5/bluez5-device.c Changed
20
 
1
@@ -1608,7 +1608,17 @@
2
            name = "audio-gateway";
3
            desc = _("Audio Gateway (A2DP Source & HSP/HFP AG)");
4
        }
5
-       priority = 256;
6
+
7
+       /*
8
+        * If the remote is A2DP sink and HF, we likely should prioritize being
9
+        * A2DP sender, not gateway. This can occur in PW<->PW if RFCOMM gets
10
+        * connected both as AG and HF.
11
+        */
12
+       if ((device->connected_profiles & SPA_BT_PROFILE_A2DP_SINK) &&
13
+               (device->connected_profiles & SPA_BT_PROFILE_HEADSET_HEAD_UNIT))
14
+           priority = 15;
15
+       else
16
+           priority = 256;
17
        break;
18
    }
19
    case DEVICE_PROFILE_A2DP:
20
pipewire-0.3.80.tar.gz/spa/plugins/support/loop.c -> pipewire-0.3.81.tar.gz/spa/plugins/support/loop.c Changed
55
 
1
@@ -16,6 +16,7 @@
2
 #include <spa/support/plugin.h>
3
 #include <spa/utils/list.h>
4
 #include <spa/utils/names.h>
5
+#include <spa/utils/ratelimit.h>
6
 #include <spa/utils/result.h>
7
 #include <spa/utils/type.h>
8
 #include <spa/utils/ringbuffer.h>
9
@@ -64,6 +65,7 @@
10
 
11
    struct spa_source *wakeup;
12
    int ack_fd;
13
+   struct spa_ratelimit rate_limit;
14
 
15
    struct spa_ringbuffer buffer;
16
    uint8_t *buffer_data;
17
@@ -94,6 +96,13 @@
18
 };
19
 /** \endcond */
20
 
21
+static inline uint64_t get_time_ns(struct spa_system *system)
22
+{
23
+   struct timespec ts;
24
+   spa_system_clock_gettime(system, CLOCK_MONOTONIC, &ts);
25
+   return SPA_TIMESPEC_TO_NSEC(&ts);
26
+}
27
+
28
 static int loop_add_source(void *object, struct spa_source *source)
29
 {
30
    struct impl *impl = object;
31
@@ -267,8 +276,12 @@
32
        item->item_size = SPA_ROUND_UP_N(l0 + size, ITEM_ALIGN);
33
    }
34
    if (avail < item->item_size) {
35
-       spa_log_warn(impl->log, "%p: queue full %d, need %zd", impl, avail,
36
-               item->item_size);
37
+       int suppressed;
38
+       uint64_t nsec = get_time_ns(impl->system);
39
+       if ((suppressed = spa_ratelimit_test(&impl->rate_limit, nsec)) >= 0) {
40
+           spa_log_warn(impl->log, "%p: queue full %d, need %zd (%d suppressed)",
41
+                   impl, avail, item->item_size, suppressed);
42
+       }
43
        return -EPIPE;
44
    }
45
    if (data && size > 0)
46
@@ -1004,6 +1017,8 @@
47
        res = -EINVAL;
48
        goto error_exit;
49
    }
50
+   impl->rate_limit.interval = 2 * SPA_NSEC_PER_SEC;
51
+   impl->rate_limit.burst = 1;
52
 
53
    if ((res = spa_system_pollfd_create(impl->system, SPA_FD_CLOEXEC)) < 0) {
54
        spa_log_error(impl->log, "%p: can't create pollfd: %s",
55
pipewire-0.3.80.tar.gz/spa/plugins/support/node-driver.c -> pipewire-0.3.81.tar.gz/spa/plugins/support/node-driver.c Changed
22
 
1
@@ -95,14 +95,18 @@
2
 #ifdef CLOCK_MONOTONIC_RAW
3
    { "monotonic-raw", CLOCK_MONOTONIC_RAW },
4
 #endif
5
+#ifdef CLOCK_BOOTTIME
6
    { "boottime", CLOCK_BOOTTIME },
7
+#endif
8
 };
9
 
10
 static bool clock_for_timerfd(clockid_t id)
11
 {
12
    return id == CLOCK_REALTIME ||
13
-       id == CLOCK_MONOTONIC ||
14
-       id == CLOCK_BOOTTIME;
15
+#ifdef CLOCK_BOOTTIME
16
+       id == CLOCK_BOOTTIME ||
17
+#endif
18
+       id == CLOCK_MONOTONIC;
19
 }
20
 
21
 static clockid_t clock_name_to_id(const char *name)
22
pipewire-0.3.80.tar.gz/spa/tests/stress-ringbuffer.c -> pipewire-0.3.81.tar.gz/spa/tests/stress-ringbuffer.c Changed
15
 
1
@@ -11,10 +11,11 @@
2
 #define ARRAY_SIZE 63
3
 #define MAX_VALUE 0x10000
4
 
5
-#if defined(__FreeBSD__) || defined(__MidnightBSD__)
6
+#if defined(__FreeBSD__) || defined(__MidnightBSD__) || defined (__GNU__)
7
 #include <sys/param.h>
8
 #if (__FreeBSD_version >= 1400000 && __FreeBSD_version < 1400043) \
9
-    || (__FreeBSD_version < 1300523) || defined(__MidnightBSD__)
10
+    || (__FreeBSD_version < 1300523) || defined(__MidnightBSD__) \
11
+    || defined (__GNU__)
12
 static int sched_getcpu(void) { return -1; };
13
 #endif
14
 #endif
15
pipewire-0.3.80.tar.gz/src/daemon/filter-chain/demonic.conf -> pipewire-0.3.81.tar.gz/src/daemon/filter-chain/demonic.conf Changed
13
 
1
@@ -4,8 +4,9 @@
2
 # ~/.config/pipewire/filter-chain.conf.d/
3
 #
4
 context.modules = 
5
-    { name = libpipewire-module-filter-chain
6
-        args = {
7
+  { name = libpipewire-module-filter-chain
8
+    flags =  nofail 
9
+    args = {
10
             #audio.format    = F32
11
             #audio.rate      = 48000
12
             audio.channels   = 2
13
pipewire-0.3.80.tar.gz/src/daemon/filter-chain/sink-dolby-surround.conf -> pipewire-0.3.81.tar.gz/src/daemon/filter-chain/sink-dolby-surround.conf Changed
9
 
1
@@ -5,6 +5,7 @@
2
 #
3
 context.modules = 
4
     { name = libpipewire-module-filter-chain
5
+        flags =  nofail 
6
         args = {
7
             node.description = "Dolby Surround Sink"
8
             media.name       = "Dolby Surround Sink"
9
pipewire-0.3.80.tar.gz/src/daemon/filter-chain/sink-matrix-spatialiser.conf -> pipewire-0.3.81.tar.gz/src/daemon/filter-chain/sink-matrix-spatialiser.conf Changed
9
 
1
@@ -8,6 +8,7 @@
2
 
3
 context.modules = 
4
     { name = libpipewire-module-filter-chain
5
+        flags =  nofail 
6
         args = {
7
             node.description = "Matrix Spatialiser"
8
             media.name       = "Matrix Spatialiser"
9
pipewire-0.3.80.tar.gz/src/daemon/filter-chain/sink-virtual-surround-5.1-kemar.conf -> pipewire-0.3.81.tar.gz/src/daemon/filter-chain/sink-virtual-surround-5.1-kemar.conf Changed
13
 
1
@@ -3,8 +3,11 @@
2
 # Copy this file into a conf.d/ directory such as
3
 # ~/.config/pipewire/filter-chain.conf.d/
4
 #
5
+# Adjust the paths to the convolver files to match your system
6
+#
7
 context.modules = 
8
     { name = libpipewire-module-filter-chain
9
+        flags =  nofail 
10
         args = {
11
             node.description = "Virtual Surround Sink"
12
             media.name       = "Virtual Surround Sink"
13
pipewire-0.3.80.tar.gz/src/daemon/filter-chain/sink-virtual-surround-7.1-hesuvi.conf -> pipewire-0.3.81.tar.gz/src/daemon/filter-chain/sink-virtual-surround-7.1-hesuvi.conf Changed
13
 
1
@@ -3,8 +3,11 @@
2
 # Copy this file into a conf.d/ directory such as
3
 # ~/.config/pipewire/filter-chain.conf.d/
4
 #
5
+# Adjust the paths to the convolver files to match your system
6
+#
7
 context.modules = 
8
     { name = libpipewire-module-filter-chain
9
+        flags =  nofail 
10
         args = {
11
             node.description = "Virtual Surround Sink"
12
             media.name       = "Virtual Surround Sink"
13
pipewire-0.3.80.tar.gz/src/daemon/filter-chain/source-rnnoise.conf -> pipewire-0.3.81.tar.gz/src/daemon/filter-chain/source-rnnoise.conf Changed
28
 
1
@@ -3,8 +3,11 @@
2
 # Copy this file into a conf.d/ directory such as
3
 # ~/.config/pipewire/filter-chain.conf.d/
4
 #
5
+# Adjust the paths to the rnnoise plugin to match your system
6
+#
7
 context.modules = 
8
     { name = libpipewire-module-filter-chain
9
+        flags =  nofail 
10
         args = {
11
             node.description = "Noise Canceling source"
12
             media.name       = "Noise Canceling source"
13
@@ -13,7 +16,13 @@
14
                     {
15
                         type   = ladspa
16
                         name   = rnnoise
17
-                        plugin = librnnoise_ladspa
18
+           # The path to the plugin. The suffix .so is appended to
19
+           # this string and then the file is then located in the directories
20
+           # listed in the environment variable LADSPA_PATH or 
21
+           # /usr/lib64/ladspa, /usr/lib/ladspa or the system library directory
22
+           # as a fallback.
23
+           # You might want to use an absolute path here to avoid problems.
24
+                        plugin = "librnnoise_ladspa"
25
                         label  = noise_suppressor_stereo
26
                         control = {
27
                             "VAD Threshold (%)" 50.0
28
pipewire-0.3.80.tar.gz/src/daemon/filter-chain/spatializer-7.1.conf -> pipewire-0.3.81.tar.gz/src/daemon/filter-chain/spatializer-7.1.conf Changed
13
 
1
@@ -3,8 +3,11 @@
2
 # Copy this file into a conf.d/ directory such as
3
 # ~/.config/pipewire/filter-chain.conf.d/
4
 #
5
+# Adjust the paths to the sofa file to match your system.
6
+#
7
 context.modules = 
8
     { name = libpipewire-module-filter-chain
9
+        flags =  nofail 
10
         args = {
11
             node.description = "Spatial Sink"
12
             media.name       = "Spatial Sink"
13
pipewire-0.3.80.tar.gz/src/daemon/filter-chain/spatializer-single.conf -> pipewire-0.3.81.tar.gz/src/daemon/filter-chain/spatializer-single.conf Changed
13
 
1
@@ -4,8 +4,11 @@
2
 # Copy this file into a conf.d/ directory such as
3
 # ~/.config/pipewire/filter-chain.conf.d/
4
 #
5
+# Adjust the paths to the sofa files to match your system
6
+#
7
 context.modules = 
8
     { name = libpipewire-module-filter-chain
9
+        flags =  nofail 
10
         args = {
11
             node.description = "3D Sink"
12
             media.name       = "3D Sink"
13
pipewire-0.3.80.tar.gz/src/daemon/pipewire-pulse.conf.in -> pipewire-0.3.81.tar.gz/src/daemon/pipewire-pulse.conf.in Changed
9
 
1
@@ -28,6 +28,7 @@
2
         args = {
3
             nice.level   = -11
4
             #rt.prio      = 88
5
+            rt.prio      = 65
6
             #rt.time.soft = -1
7
             #rt.time.hard = -1
8
         }
9
pipewire-0.3.80.tar.gz/src/daemon/pipewire.conf.in -> pipewire-0.3.81.tar.gz/src/daemon/pipewire.conf.in Changed
40
 
1
@@ -50,6 +50,8 @@
2
     # enables autoloading of access module, when disabled an alternative
3
     # access module needs to be loaded.
4
     module.access = true
5
+    # enables autoloading of module-jackdbus-detect
6
+    module.jackdbus-detect = true
7
 }
8
 
9
 context.spa-libs = {
10
@@ -179,6 +181,29 @@
11
         flags =  ifexists nofail 
12
         condition =  { module.x11.bell = true } 
13
     }
14
+    { name = libpipewire-module-jackdbus-detect
15
+        args {
16
+            #jack.library     = libjack.so.0
17
+            #jack.server      = null
18
+            #jack.client-name = PipeWire
19
+            #jack.connect     = true
20
+            #tunnel.mode      = duplex  # source|sink|duplex
21
+            source.props = {
22
+                #audio.channels = 2
23
+       #midi.ports = 1
24
+                #audio.position =  FL FR 
25
+                # extra sink properties
26
+            }
27
+            sink.props = {
28
+                #audio.channels = 2
29
+       #midi.ports = 1
30
+                #audio.position =  FL FR 
31
+                # extra sink properties
32
+            }
33
+        }
34
+        flags =  ifexists nofail 
35
+        condition =  { module.jackdbus-detect = true } 
36
+    }
37
 
38
 
39
 context.objects = 
40
pipewire-0.3.80.tar.gz/src/modules/module-access.c -> pipewire-0.3.81.tar.gz/src/modules/module-access.c Changed
10
 
1
@@ -143,7 +143,7 @@
2
     * (in the current namespace).
3
     */
4
 
5
-#if defined(__linux__)
6
+#if defined(__linux__) || defined(__GNU__)
7
    spa_scnprintf(path, sizeof(path), "/proc/%u/exe", pid);
8
 #elif defined(__FreeBSD__) || defined(__MidnightBSD__)
9
    spa_scnprintf(path, sizeof(path), "/proc/%u/file", pid);
10
pipewire-0.3.80.tar.gz/src/modules/module-ffado-driver.c -> pipewire-0.3.81.tar.gz/src/modules/module-ffado-driver.c Changed
37
 
1
@@ -39,7 +39,7 @@
2
  * ## Module Options
3
  *
4
  * - `driver.mode`: the driver mode, sink|source|duplex, default duplex
5
- * - `ffado.devices`: array of devices to open, default hw:0
6
+ * - `ffado.devices`: array of devices to open, default "hw:0"
7
  * - `ffado.period-size`: period size,default 1024
8
  * - `ffado.period-num`: period number,default 3
9
  * - `ffado.sample-rate`: sample-rate, default 48000
10
@@ -71,7 +71,7 @@
11
  * {   name = libpipewire-module-ffado-driver
12
  *     args = {
13
  *         #driver.mode       = duplex
14
- *         #ffado.devices     =  hw:0 
15
+ *         #ffado.devices     =  "hw:0" 
16
  *         #ffado.period-size = 1024
17
  *         #ffado.period-num  = 3
18
  *         #ffado.sample-rate = 48000
19
@@ -100,7 +100,7 @@
20
 
21
 #define MAX_PORTS  128
22
 
23
-#define DEFAULT_DEVICES        " hw:0 "
24
+#define DEFAULT_DEVICES        " \"hw:0\" "
25
 #define DEFAULT_PERIOD_SIZE    1024
26
 #define DEFAULT_PERIOD_NUM 3
27
 #define DEFAULT_SAMPLE_RATE    48000
28
@@ -113,7 +113,7 @@
29
 
30
 #define MODULE_USAGE   "( remote.name=<remote> ) "             \
31
            "( driver.mode=<sink|source|duplex> ) "         \
32
-           "( ffado.devices=<devices array size, default hw:0> ) " \
33
+           "( ffado.devices=<devices array size, default \"hw:0\"> ) " \
34
            "( ffado.period-size=<period size, default 1024> ) "    \
35
            "( ffado.period-num=<period num, default 3> ) "     \
36
            "( ffado.sample-rate=<sampe rate, default 48000> ) "    \
37
pipewire-0.3.80.tar.gz/src/modules/module-filter-chain.c -> pipewire-0.3.81.tar.gz/src/modules/module-filter-chain.c Changed
530
 
1
@@ -82,6 +82,12 @@
2
  *         
3
  *         inputs =  <portname> ... 
4
  *         outputs =  <portname> ... 
5
+ *         capture.volumes = 
6
+ *             { control = <portname>  min = <value>  max = <value>  scale = <scale> } ...
7
+ *         
8
+ *         playback.volumes = 
9
+ *             { control = <portname>  min = <value>  max = <value>  scale = <scale> } ...
10
+ *         
11
  *    }
12
  *\endcode
13
  *
14
@@ -137,6 +143,20 @@
15
  * graph will then be duplicated as many times to match the number of input/output
16
  * channels of the streams.
17
  *
18
+ * ### Volumes
19
+ *
20
+ * Normally the volume of the sink/source is handled by the stream software volume.
21
+ * With the capture.volumes and playback.volumes properties this can be handled
22
+ * by a control port in the graph instead.
23
+ *
24
+ * The min and max values (defaults 0.0 and 1.0) respectively can be used to scale
25
+ * and translate the volume min and max values.
26
+ *
27
+ * Normally the control values are linear and it is assumed that the plugin does not
28
+ * perform any scaling to the values. This can be changed with the scale property. By
29
+ * default this is linear but it can be set to cubic when the control applies a
30
+ * cubic transformation.
31
+ *
32
  * ## Builtin filters
33
  *
34
  * There are some useful builtin filters available. You select them with the label
35
@@ -576,7 +596,7 @@
36
    uint32_t n_links;
37
    uint32_t external;
38
 
39
-   float control_data;
40
+   float control_dataMAX_HNDL;
41
    float *audio_dataMAX_HNDL;
42
 };
43
 
44
@@ -625,6 +645,20 @@
45
    void **hndl;
46
 };
47
 
48
+struct volume {
49
+   bool mute;
50
+   uint32_t n_volumes;
51
+   float volumesSPA_AUDIO_MAX_CHANNELS;
52
+
53
+   uint32_t n_ports;
54
+   struct port *portsSPA_AUDIO_MAX_CHANNELS;
55
+   float minSPA_AUDIO_MAX_CHANNELS;
56
+   float maxSPA_AUDIO_MAX_CHANNELS;
57
+#define SCALE_LINEAR   0
58
+#define SCALE_CUBIC    1
59
+   int scaleSPA_AUDIO_MAX_CHANNELS;
60
+};
61
+
62
 struct graph {
63
    struct impl *impl;
64
 
65
@@ -643,6 +677,9 @@
66
    uint32_t n_control;
67
    struct port **control_port;
68
 
69
+   struct volume capture_volume;
70
+   struct volume playback_volume;
71
+
72
    unsigned instantiated:1;
73
 };
74
 
75
@@ -962,35 +999,48 @@
76
 
77
        spa_pod_builder_string(b, name);
78
        if (p->hint & FC_HINT_BOOLEAN) {
79
-           spa_pod_builder_bool(b, port->control_data <= 0.0f ? false : true);
80
+           spa_pod_builder_bool(b, port->control_data0 <= 0.0f ? false : true);
81
        } else if (p->hint & FC_HINT_INTEGER) {
82
-           spa_pod_builder_int(b, port->control_data);
83
+           spa_pod_builder_int(b, port->control_data0);
84
        } else {
85
-           spa_pod_builder_float(b, port->control_data);
86
+           spa_pod_builder_float(b, port->control_data0);
87
        }
88
    }
89
    spa_pod_builder_pop(b, &f1);
90
    return spa_pod_builder_pop(b, &f0);
91
 }
92
 
93
+static int port_set_control_value(struct port *port, float *value, uint32_t id)
94
+{
95
+   struct node *node = port->node;
96
+   struct descriptor *desc = node->desc;
97
+   float old;
98
+
99
+   old = port->control_dataid;
100
+   port->control_dataid = value ? *value : desc->default_controlport->idx;
101
+   pw_log_info("control %d %d ('%s') from %f to %f", port->idx, id,
102
+           desc->desc->portsport->p.name, old, port->control_dataid);
103
+   node->control_changed = old != port->control_dataid;
104
+   return node->control_changed ? 1 : 0;
105
+}
106
+
107
 static int set_control_value(struct node *node, const char *name, float *value)
108
 {
109
-   struct descriptor *desc;
110
    struct port *port;
111
-   float old;
112
+   int count = 0;
113
+   uint32_t i, n_hndl;
114
 
115
    port = find_port(node, name, FC_PORT_INPUT | FC_PORT_CONTROL);
116
    if (port == NULL)
117
        return -ENOENT;
118
 
119
-   node = port->node;
120
-   desc = node->desc;
121
+   /* if we don't have any instances yet, set the first control value, we will
122
+    * copy to other instances later */
123
+   n_hndl = SPA_MAX(1u, port->node->n_hndl);
124
+   for (i = 0; i < n_hndl; i++)
125
+       count += port_set_control_value(port, value, i);
126
 
127
-   old = port->control_data;
128
-   port->control_data = value ? *value : desc->default_controlport->idx;
129
-   pw_log_info("control %d ('%s') from %f to %f", port->idx, name, old, port->control_data);
130
-   node->control_changed = old != port->control_data;
131
-   return node->control_changed ? 1 : 0;
132
+   return count;
133
 }
134
 
135
 static int parse_params(struct graph *graph, const struct spa_pod *pod)
136
@@ -1082,17 +1132,115 @@
137
    spa_pod_dynamic_builder_clean(&b);
138
 }
139
 
140
-static void param_props_changed(struct impl *impl, const struct spa_pod *param)
141
+static int sync_volume(struct graph *graph, struct volume *vol)
142
+{
143
+   uint32_t i;
144
+   int res = 0;
145
+
146
+   if (vol->n_ports == 0)
147
+       return 0;
148
+   for (i = 0; i < vol->n_volumes; i++) {
149
+       uint32_t n_port = i % vol->n_ports, n_hndl;
150
+       struct port *p = vol->portsn_port;
151
+       float v = vol->mute ? 0.0f : vol->volumesi;
152
+       switch (vol->scalen_port) {
153
+       case SCALE_CUBIC:
154
+           v = cbrt(v);
155
+           break;
156
+       }
157
+       v = v * (vol->maxn_port - vol->minn_port) + vol->minn_port;
158
+
159
+       n_hndl = SPA_MAX(1u, p->node->n_hndl);
160
+       res += port_set_control_value(p, &v, i % n_hndl);
161
+   }
162
+   return res;
163
+}
164
+
165
+static void param_props_changed(struct impl *impl, const struct spa_pod *param,
166
+       bool capture)
167
 {
168
    struct spa_pod_object *obj = (struct spa_pod_object *) param;
169
+   struct spa_pod_frame f1;
170
    const struct spa_pod_prop *prop;
171
    struct graph *graph = &impl->graph;
172
    int changed = 0;
173
+   char buf1024;
174
+   struct spa_pod_dynamic_builder b;
175
+   struct volume *vol = capture ? &graph->capture_volume :
176
+       &graph->playback_volume;
177
+   bool do_volume = false;
178
+
179
+   spa_pod_dynamic_builder_init(&b, buf, sizeof(buf), 1024);
180
+   spa_pod_builder_push_object(&b.b, &f0, SPA_TYPE_OBJECT_Props, SPA_PARAM_Props);
181
 
182
    SPA_POD_OBJECT_FOREACH(obj, prop) {
183
-       if (prop->key == SPA_PROP_params)
184
+       switch (prop->key) {
185
+       case SPA_PROP_params:
186
            changed += parse_params(graph, &prop->value);
187
+           spa_pod_builder_raw_padded(&b.b, prop, SPA_POD_PROP_SIZE(prop));
188
+           break;
189
+       case SPA_PROP_mute:
190
+       {
191
+           bool mute;
192
+           if (spa_pod_get_bool(&prop->value, &mute) == 0) {
193
+               if (vol->mute != mute) {
194
+                   vol->mute = mute;
195
+                   do_volume = true;
196
+               }
197
+           }
198
+           spa_pod_builder_raw_padded(&b.b, prop, SPA_POD_PROP_SIZE(prop));
199
+           break;
200
+       }
201
+       case SPA_PROP_channelVolumes:
202
+       {
203
+           uint32_t i, n_vols;
204
+           float volsSPA_AUDIO_MAX_CHANNELS;
205
+
206
+           if ((n_vols = spa_pod_copy_array(&prop->value, SPA_TYPE_Float, vols,
207
+                   SPA_AUDIO_MAX_CHANNELS)) > 0) {
208
+               if (vol->n_volumes != n_vols)
209
+                   do_volume = true;
210
+               vol->n_volumes = n_vols;
211
+               for (i = 0; i < n_vols; i++) {
212
+                   float v = volsi;
213
+                   if (v != vol->volumesi) {
214
+                       vol->volumesi = v;
215
+                       do_volume = true;
216
+                   }
217
+               }
218
+           }
219
+           spa_pod_builder_raw_padded(&b.b, prop, SPA_POD_PROP_SIZE(prop));
220
+           break;
221
+       }
222
+       case SPA_PROP_softVolumes:
223
+       case SPA_PROP_softMute:
224
+           break;
225
+       default:
226
+           spa_pod_builder_raw_padded(&b.b, prop, SPA_POD_PROP_SIZE(prop));
227
+           break;
228
+       }
229
    }
230
+   if (do_volume && vol->n_ports != 0) {
231
+       float soft_volsSPA_AUDIO_MAX_CHANNELS;
232
+       uint32_t i;
233
+
234
+       for (i = 0; i < vol->n_volumes; i++)
235
+           soft_volsi = (vol->mute || vol->volumesi == 0.0f) ? 0.0f : 1.0f;
236
+
237
+       spa_pod_builder_prop(&b.b, SPA_PROP_softMute, 0);
238
+       spa_pod_builder_bool(&b.b, vol->mute);
239
+       spa_pod_builder_prop(&b.b, SPA_PROP_softVolumes, 0);
240
+       spa_pod_builder_array(&b.b, sizeof(float), SPA_TYPE_Float,
241
+               vol->n_volumes, soft_vols);
242
+       param = spa_pod_builder_pop(&b.b, &f0);
243
+
244
+       sync_volume(graph, vol);
245
+       pw_stream_set_param(capture ? impl->capture :
246
+               impl->playback, SPA_PARAM_Props, param);
247
+   }
248
+
249
+   spa_pod_dynamic_builder_clean(&b);
250
+
251
    if (changed > 0) {
252
        struct node *node;
253
 
254
@@ -1101,6 +1249,7 @@
255
 
256
        update_props_param(impl);
257
    }
258
+
259
 }
260
 
261
 static void param_latency_changed(struct impl *impl, const struct spa_pod *param)
262
@@ -1196,7 +1345,8 @@
263
    }
264
 }
265
 
266
-static void param_changed(void *data, uint32_t id, const struct spa_pod *param)
267
+static void param_changed(void *data, uint32_t id, const struct spa_pod *param,
268
+       bool capture)
269
 {
270
    struct impl *impl = data;
271
    struct graph *graph = &impl->graph;
272
@@ -1219,7 +1369,7 @@
273
    }
274
    case SPA_PARAM_Props:
275
        if (param != NULL)
276
-           param_props_changed(impl, param);
277
+           param_props_changed(impl, param, capture);
278
        break;
279
    case SPA_PARAM_Latency:
280
        param_latency_changed(impl, param);
281
@@ -1231,8 +1381,13 @@
282
    return;
283
 
284
 error:
285
-   pw_stream_set_error(impl->capture, res, "can't start graph: %s",
286
-           spa_strerror(res));
287
+   pw_stream_set_error(capture ? impl->capture : impl->playback,
288
+           res, "can't start graph: %s", spa_strerror(res));
289
+}
290
+
291
+static void capture_param_changed(void *data, uint32_t id, const struct spa_pod *param)
292
+{
293
+   param_changed(data, id, param, true);
294
 }
295
 
296
 static const struct pw_stream_events in_stream_events = {
297
@@ -1241,9 +1396,14 @@
298
    .process = capture_process,
299
    .io_changed = io_changed,
300
    .state_changed = state_changed,
301
-   .param_changed = param_changed
302
+   .param_changed = capture_param_changed
303
 };
304
 
305
+static void playback_param_changed(void *data, uint32_t id, const struct spa_pod *param)
306
+{
307
+   param_changed(data, id, param, false);
308
+}
309
+
310
 static void playback_destroy(void *d)
311
 {
312
    struct impl *impl = d;
313
@@ -1257,7 +1417,7 @@
314
    .process = playback_process,
315
    .io_changed = io_changed,
316
    .state_changed = state_changed,
317
-   .param_changed = param_changed,
318
+   .param_changed = playback_param_changed,
319
 };
320
 
321
 static int setup_streams(struct impl *impl)
322
@@ -1785,6 +1945,91 @@
323
 }
324
 
325
 /**
326
+ * {
327
+ *   control = name:portname
328
+ *   min = <float, defaukt 0.0>
329
+ *   max = <float, default 1.0>
330
+ *   scale = <string, default "linear", options "linear","cubic">
331
+ * }
332
+ */
333
+static int parse_volume(struct graph *graph, struct spa_json *json, bool capture)
334
+{
335
+   char key256;
336
+   char control256 = "";
337
+   char scale64 = "linear";
338
+   float min = 0.0f, max = 1.0f;
339
+   const char *val;
340
+   struct node *def_control;
341
+   struct port *port;
342
+   struct volume *vol = capture ? &graph->capture_volume :
343
+       &graph->playback_volume;
344
+
345
+   if (spa_list_is_empty(&graph->node_list)) {
346
+       pw_log_error("can't set volume in graph without nodes");
347
+       return -EINVAL;
348
+   }
349
+   while (spa_json_get_string(json, key, sizeof(key)) > 0) {
350
+       if (spa_streq(key, "control")) {
351
+           if (spa_json_get_string(json, control, sizeof(control)) <= 0) {
352
+               pw_log_error("control expects a string");
353
+               return -EINVAL;
354
+           }
355
+       }
356
+       else if (spa_streq(key, "min")) {
357
+           if (spa_json_get_float(json, &min) <= 0) {
358
+               pw_log_error("min expects a float");
359
+               return -EINVAL;
360
+           }
361
+       }
362
+       else if (spa_streq(key, "max")) {
363
+           if (spa_json_get_float(json, &max) <= 0) {
364
+               pw_log_error("max expects a float");
365
+               return -EINVAL;
366
+           }
367
+       }
368
+       else if (spa_streq(key, "scale")) {
369
+           if (spa_json_get_string(json, scale, sizeof(scale)) <= 0) {
370
+               pw_log_error("scale expects a string");
371
+               return -EINVAL;
372
+           }
373
+       }
374
+       else if (spa_json_next(json, &val) < 0)
375
+           break;
376
+   }
377
+   if (capture)
378
+       def_control = spa_list_first(&graph->node_list, struct node, link);
379
+   else
380
+       def_control = spa_list_last(&graph->node_list, struct node, link);
381
+
382
+   port = find_port(def_control, control, FC_PORT_INPUT | FC_PORT_CONTROL);
383
+   if (port == NULL) {
384
+       pw_log_error("unknown control port %s", control);
385
+       return -ENOENT;
386
+   }
387
+   if (vol->n_ports >= SPA_AUDIO_MAX_CHANNELS) {
388
+       pw_log_error("too many volume controls");
389
+       return -ENOSPC;
390
+   }
391
+   if (spa_streq(scale, "linear")) {
392
+       vol->scalevol->n_ports = SCALE_LINEAR;
393
+   } else if (spa_streq(scale, "cubic")) {
394
+       vol->scalevol->n_ports = SCALE_CUBIC;
395
+   } else {
396
+       pw_log_error("Invalid scale value '%s', use one of linear or cubic", scale);
397
+       return -EINVAL;
398
+   }
399
+   pw_log_info("volume %d: \"%s:%s\" min:%f max:%f scale:%s", vol->n_ports, port->node->name,
400
+           port->node->desc->desc->portsport->p.name, min, max, scale);
401
+
402
+   vol->portsvol->n_ports = port;
403
+   vol->minvol->n_ports = min;
404
+   vol->maxvol->n_ports = max;
405
+   vol->n_ports++;
406
+
407
+   return 0;
408
+}
409
+
410
+/**
411
  * type = ladspa
412
  * name = rev
413
  * plugin = g2reverb
414
@@ -1896,7 +2141,7 @@
415
        port->external = SPA_ID_INVALID;
416
        port->p = desc->controli;
417
        spa_list_init(&port->link_list);
418
-       port->control_data = desc->default_controli;
419
+       port->control_data0 = desc->default_controli;
420
    }
421
    for (i = 0; i < desc->n_notify; i++) {
422
        struct port *port = &node->notify_porti;
423
@@ -2041,22 +2286,22 @@
424
            }
425
            for (j = 0; j < desc->n_control; j++) {
426
                port = &node->control_portj;
427
-               d->connect_port(node->hndli, port->p, &port->control_data);
428
+               d->connect_port(node->hndli, port->p, &port->control_datai);
429
 
430
                spa_list_for_each(link, &port->link_list, input_link) {
431
                    struct port *peer = link->output;
432
                    pw_log_info("connect control port %s%d:%s %p",
433
                            node->name, i, d->portsport->p.name,
434
-                           &peer->control_data);
435
-                   d->connect_port(node->hndli, port->p, &peer->control_data);
436
+                           &peer->control_datai);
437
+                   d->connect_port(node->hndli, port->p, &peer->control_datai);
438
                }
439
            }
440
            for (j = 0; j < desc->n_notify; j++) {
441
                port = &node->notify_portj;
442
                pw_log_info("connect notify port %s%d:%s %p",
443
                        node->name, i, d->portsport->p.name,
444
-                       &port->control_data);
445
-               d->connect_port(node->hndli, port->p, &port->control_data);
446
+                       &port->control_datai);
447
+               d->connect_port(node->hndli, port->p, &port->control_datai);
448
            }
449
            if (d->activate)
450
                d->activate(node->hndli);
451
@@ -2071,6 +2316,22 @@
452
    return res;
453
 }
454
 
455
+/* any default values for the controls are set in the first instance
456
+ * of the control data. Duplicate this to the other instances now. */
457
+static void setup_node_controls(struct node *node)
458
+{
459
+   uint32_t i, j;
460
+   uint32_t n_hndl = node->n_hndl;
461
+   uint32_t n_ports = node->desc->n_control;
462
+   struct port *ports = node->control_port;
463
+
464
+   for (i = 0; i < n_ports; i++) {
465
+       struct port *port = &portsi;
466
+       for (j = 1; j < n_hndl; j++)
467
+           port->control_dataj = port->control_data0;
468
+   }
469
+}
470
+
471
 static struct node *find_next_node(struct graph *graph)
472
 {
473
    struct node *node;
474
@@ -2167,6 +2428,7 @@
475
        desc = node->desc;
476
        n_control += desc->n_control;
477
        n_nodes++;
478
+       setup_node_controls(node);
479
    }
480
    graph->n_input = 0;
481
    graph->input = calloc(n_input * 16 * n_hndl, sizeof(struct graph_port));
482
@@ -2355,6 +2617,7 @@
483
 {
484
    struct spa_json it3;
485
    struct spa_json inputs, outputs, *pinputs = NULL, *poutputs = NULL;
486
+   struct spa_json cvolumes, pvolumes, *pcvolumes = NULL, *ppvolumes = NULL;
487
    struct spa_json nodes, *pnodes = NULL, links, *plinks = NULL;
488
    const char *json, *val;
489
    char key256;
490
@@ -2402,6 +2665,20 @@
491
                return -EINVAL;
492
            }
493
            poutputs = &outputs;
494
+       }
495
+       else if (spa_streq("capture.volumes", key)) {
496
+           if (spa_json_enter_array(&it1, &cvolumes) <= 0) {
497
+               pw_log_error("capture.volumes expects an array");
498
+               return -EINVAL;
499
+           }
500
+           pcvolumes = &cvolumes;
501
+       }
502
+       else if (spa_streq("playback.volumes", key)) {
503
+           if (spa_json_enter_array(&it1, &pvolumes) <= 0) {
504
+               pw_log_error("playback.volumes expects an array");
505
+               return -EINVAL;
506
+           }
507
+           ppvolumes = &pvolumes;
508
        } else if (spa_json_next(&it1, &val) < 0)
509
            break;
510
    }
511
@@ -2419,6 +2696,18 @@
512
                return res;
513
        }
514
    }
515
+   if (pcvolumes != NULL) {
516
+       while (spa_json_enter_object(pcvolumes, &it2) > 0) {
517
+           if ((res = parse_volume(graph, &it2, true)) < 0)
518
+               return res;
519
+       }
520
+   }
521
+   if (ppvolumes != NULL) {
522
+       while (spa_json_enter_object(ppvolumes, &it2) > 0) {
523
+           if ((res = parse_volume(graph, &it2, false)) < 0)
524
+               return res;
525
+       }
526
+   }
527
    return setup_graph(graph, pinputs, poutputs);
528
 }
529
 
530
pipewire-0.3.80.tar.gz/src/modules/module-filter-chain/dsp-ops-c.c -> pipewire-0.3.81.tar.gz/src/modules/module-filter-chain/dsp-ops-c.c Changed
11
 
1
@@ -63,9 +63,6 @@
2
    uint32_t i;
3
    if (n_src == 0) {
4
        dsp_clear_c(ops, dst, n_samples);
5
-   } else if (n_src == 1) {
6
-       if (dst != src0)
7
-           dsp_copy_c(ops, dst, src0, n_samples);
8
    } else {
9
        if (gain0 == 1.0f)
10
            dsp_copy_c(ops, dst, src0, n_samples);
11
pipewire-0.3.80.tar.gz/src/modules/module-filter-chain/dsp-ops-sse.c -> pipewire-0.3.81.tar.gz/src/modules/module-filter-chain/dsp-ops-sse.c Changed
10
 
1
@@ -19,7 +19,7 @@
2
 {
3
    if (n_src == 0) {
4
        memset(dst, 0, n_samples * sizeof(float));
5
-   } else if (n_src == 1) {
6
+   } else if (n_src == 1 && gain0 == 1.0f) {
7
        if (dst != src0)
8
            spa_memcpy(dst, src0, n_samples * sizeof(float));
9
    } else {
10
pipewire-0.3.80.tar.gz/src/modules/module-pipe-tunnel.c -> pipewire-0.3.81.tar.gz/src/modules/module-pipe-tunnel.c Changed
9
 
1
@@ -8,6 +8,7 @@
2
 #include <errno.h>
3
 #include <sys/types.h>
4
 #include <sys/stat.h>
5
+#include <sys/uio.h>
6
 #include <fcntl.h>
7
 #include <unistd.h>
8
 #include <stdlib.h>
9
pipewire-0.3.80.tar.gz/src/modules/module-profiler.c -> pipewire-0.3.81.tar.gz/src/modules/module-profiler.c Changed
10
 
1
@@ -269,7 +269,7 @@
2
    n->count++;
3
 }
4
 
5
-static struct pw_impl_node_rt_events node_rt_events = {
6
+static const struct pw_impl_node_rt_events node_rt_events = {
7
    PW_VERSION_IMPL_NODE_RT_EVENTS,
8
    .complete = context_do_profile,
9
    .incomplete = context_do_profile,
10
pipewire-0.3.80.tar.gz/src/modules/module-protocol-pulse/client.c -> pipewire-0.3.81.tar.gz/src/modules/module-protocol-pulse/client.c Changed
9
 
1
@@ -46,7 +46,6 @@
2
    spa_list_init(&client->out_messages);
3
    spa_list_init(&client->operations);
4
    spa_list_init(&client->pending_samples);
5
-   spa_list_init(&client->pending_streams);
6
    spa_hook_list_init(&client->listener_list);
7
 
8
    spa_list_append(&server->clients, &client->link);
9
pipewire-0.3.80.tar.gz/src/modules/module-protocol-pulse/client.h -> pipewire-0.3.81.tar.gz/src/modules/module-protocol-pulse/client.h Changed
10
 
1
@@ -74,8 +74,6 @@
2
 
3
    struct spa_list pending_samples;
4
 
5
-   struct spa_list pending_streams;
6
-
7
    unsigned int disconnect:1;
8
    unsigned int new_msg_since_last_flush:1;
9
    unsigned int authenticated:1;
10
pipewire-0.3.80.tar.gz/src/modules/module-protocol-pulse/manager.c -> pipewire-0.3.81.tar.gz/src/modules/module-protocol-pulse/manager.c Changed
12
 
1
@@ -440,6 +440,10 @@
2
            case SPA_PARAM_PropInfo:
3
            case SPA_PARAM_Format:
4
            case SPA_PARAM_EnumFormat:
5
+           /* also emit changed for the Latency param because the stream might
6
+            * now be linked. FIXME, we should check if a new link is made for
7
+            * a stream and only emit a changed event in that case. */
8
+           case SPA_PARAM_Latency:
9
                changed++;
10
                break;
11
            default:
12
pipewire-0.3.80.tar.gz/src/modules/module-protocol-pulse/modules/module-echo-cancel.c -> pipewire-0.3.81.tar.gz/src/modules/module-protocol-pulse/modules/module-echo-cancel.c Changed
13
 
1
@@ -52,6 +52,11 @@
2
    char *args;
3
    size_t size;
4
 
5
+   pw_properties_setf(data->capture_props, "pulse.module.id", "%u", module->index);
6
+   pw_properties_setf(data->source_props, "pulse.module.id", "%u", module->index);
7
+   pw_properties_setf(data->sink_props, "pulse.module.id", "%u", module->index);
8
+   pw_properties_setf(data->playback_props, "pulse.module.id", "%u", module->index);
9
+
10
    if ((f = open_memstream(&args, &size)) == NULL)
11
        return -errno;
12
 
13
pipewire-0.3.80.tar.gz/src/modules/module-protocol-pulse/pulse-server.c -> pipewire-0.3.81.tar.gz/src/modules/module-protocol-pulse/pulse-server.c Changed
160
 
1
@@ -832,42 +832,43 @@
2
    }
3
 
4
    if (spa_streq(o->type, PW_TYPE_INTERFACE_Link)) {
5
-       struct stream *s, *t;
6
        struct pw_manager_object *peer = NULL;
7
        union pw_map_item *item;
8
        pw_array_for_each(item, &client->streams.items) {
9
            struct stream *s = item->data;
10
            const char *peer_name;
11
 
12
-           if (pw_map_item_is_free(item) || s->pending)
13
+           if (pw_map_item_is_free(item))
14
                continue;
15
-           if (s->peer_index == SPA_ID_INVALID)
16
+
17
+           if (!s->pending && s->peer_index == SPA_ID_INVALID)
18
                continue;
19
 
20
            peer = find_peer_for_link(manager, o, s->id, s->direction);
21
-           if (peer == NULL || peer->props == NULL ||
22
-               peer->index == s->peer_index)
23
+           if (peer == NULL)
24
                continue;
25
 
26
-           s->peer_index = peer->index;
27
-
28
-           peer_name = pw_properties_get(peer->props, PW_KEY_NODE_NAME);
29
-           if (peer_name && s->direction == PW_DIRECTION_INPUT &&
30
-               pw_manager_object_is_monitor(peer)) {
31
-               int len = strlen(peer_name) + 10;
32
-               char *tmp = alloca(len);
33
-               snprintf(tmp, len, "%s.monitor", peer_name);
34
-               peer_name = tmp;
35
-           }
36
-           if (peer_name != NULL)
37
-               stream_send_moved(s, peer->index, peer_name);
38
-       }
39
-       spa_list_for_each_safe(s, t, &client->pending_streams, link) {
40
-           peer = find_peer_for_link(manager, o, s->id, s->direction);
41
-           if (peer) {
42
+           if (s->pending) {
43
                reply_create_stream(s, peer);
44
-               spa_list_remove(&s->link);
45
                s->pending = false;
46
+           } else {
47
+               if (s->peer_index == peer->index)
48
+                   continue;
49
+               if (peer->props == NULL)
50
+                   continue;
51
+
52
+               s->peer_index = peer->index;
53
+
54
+               peer_name = pw_properties_get(peer->props, PW_KEY_NODE_NAME);
55
+               if (peer_name && s->direction == PW_DIRECTION_INPUT &&
56
+                   pw_manager_object_is_monitor(peer)) {
57
+                   int len = strlen(peer_name) + 10;
58
+                   char *tmp = alloca(len);
59
+                   snprintf(tmp, len, "%s.monitor", peer_name);
60
+                   peer_name = tmp;
61
+               }
62
+               if (peer_name != NULL)
63
+                   stream_send_moved(s, peer->index, peer_name);
64
            }
65
        }
66
    }
67
@@ -1244,7 +1245,6 @@
68
        if (peer) {
69
            reply_create_stream(stream, peer);
70
        } else {
71
-           spa_list_append(&stream->client->pending_streams, &stream->link);
72
            stream->pending = true;
73
        }
74
    }
75
@@ -5483,9 +5483,23 @@
76
    const char *str;
77
    int res = 0;
78
 
79
+   debug_messages = pw_log_topic_enabled(SPA_LOG_LEVEL_INFO, pulse_conn);
80
+
81
    impl = calloc(1, sizeof(*impl) + user_data_size);
82
    if (impl == NULL)
83
-       goto error_exit;
84
+       goto error_free_props;
85
+
86
+   impl->rate_limit.interval = 2 * SPA_NSEC_PER_SEC;
87
+   impl->rate_limit.burst = 1;
88
+   spa_hook_list_init(&impl->hooks);
89
+   spa_list_init(&impl->servers);
90
+   pw_map_init(&impl->samples, 16, 16);
91
+   pw_map_init(&impl->modules, 16, 16);
92
+   spa_list_init(&impl->cleanup_clients);
93
+   spa_list_init(&impl->free_messages);
94
+
95
+   impl->loop = pw_context_get_main_loop(context);
96
+   impl->work_queue = pw_context_get_work_queue(context);
97
 
98
    if (props == NULL)
99
        props = pw_properties_new(NULL, NULL);
100
@@ -5503,25 +5517,6 @@
101
        pw_properties_set(props, "vm.overrides", NULL);
102
    }
103
 
104
-   load_defaults(&impl->defs, props);
105
-
106
-   debug_messages = pw_log_topic_enabled(SPA_LOG_LEVEL_INFO, pulse_conn);
107
-
108
-   impl->context = context;
109
-   impl->loop = pw_context_get_main_loop(context);
110
-   impl->props = props;
111
-
112
-   impl->work_queue = pw_context_get_work_queue(context);
113
-
114
-   spa_hook_list_init(&impl->hooks);
115
-   spa_list_init(&impl->servers);
116
-   impl->rate_limit.interval = 2 * SPA_NSEC_PER_SEC;
117
-   impl->rate_limit.burst = 1;
118
-   pw_map_init(&impl->samples, 16, 16);
119
-   pw_map_init(&impl->modules, 16, 16);
120
-   spa_list_init(&impl->cleanup_clients);
121
-   spa_list_init(&impl->free_messages);
122
-
123
    str = pw_properties_get(props, "server.address");
124
    if (str == NULL) {
125
        pw_properties_setf(props, "server.address",
126
@@ -5544,8 +5539,6 @@
127
        pw_log_warn("%p: can't create pid file: %s",
128
                impl, spa_strerror(res));
129
    }
130
-   pw_context_add_listener(context, &impl->context_listener,
131
-           &context_events, impl);
132
 
133
 #ifdef HAVE_DBUS
134
    str = pw_properties_get(props, "server.dbus-name");
135
@@ -5554,14 +5547,22 @@
136
    if (strlen(str) > 0)
137
        impl->dbus_name = dbus_request_name(context, str);
138
 #endif
139
+
140
+   load_defaults(&impl->defs, props);
141
+   impl->props = spa_steal_ptr(props);
142
+
143
+   pw_context_add_listener(context, &impl->context_listener,
144
+           &context_events, impl);
145
+   impl->context = context;
146
+
147
    cmd_run(impl);
148
 
149
    return (struct pw_protocol_pulse *) impl;
150
 
151
 error_free:
152
-   free(impl);
153
+   impl_free(impl);
154
 
155
-error_exit:
156
+error_free_props:
157
    pw_properties_free(props);
158
 
159
    if (res < 0)
160
pipewire-0.3.80.tar.gz/src/modules/module-protocol-pulse/stream.c -> pipewire-0.3.81.tar.gz/src/modules/module-protocol-pulse/stream.c Changed
11
 
1
@@ -103,9 +103,6 @@
2
 
3
    pw_log_debug("client %p: stream %p channel:%d", client, stream, stream->channel);
4
 
5
-   if (stream->pending)
6
-       spa_list_remove(&stream->link);
7
-
8
    if (stream->drain_tag)
9
        reply_error(client, -1, stream->drain_tag, -ENOENT);
10
 
11
pipewire-0.3.80.tar.gz/src/modules/module-protocol-pulse/stream.h -> pipewire-0.3.81.tar.gz/src/modules/module-protocol-pulse/stream.h Changed
9
 
1
@@ -34,7 +34,6 @@
2
 };
3
 
4
 struct stream {
5
-   struct spa_list link;
6
    uint32_t create_tag;
7
    uint32_t channel;   /* index in map */
8
    uint32_t id;        /* id of global */
9
pipewire-0.3.80.tar.gz/src/modules/module-raop-sink.c -> pipewire-0.3.81.tar.gz/src/modules/module-raop-sink.c Changed
623
 
1
@@ -44,6 +44,7 @@
2
 #include <pipewire/i18n.h>
3
 
4
 #include "module-raop/rtsp-client.h"
5
+#include "module-rtp/rtp.h"
6
 
7
 /** \page page_module_raop_sink PipeWire Module: AirPlay Sink
8
  *
9
@@ -137,8 +138,8 @@
10
 #endif
11
 #define MD5_HASH_LENGTH (2*MD5_DIGEST_LENGTH)
12
 
13
-#define DEFAULT_USER_AGENT "iTunes/11.0.4 (Windows; N)"
14
-#define DEFAULT_USER_NAME  "iTunes"
15
+#define DEFAULT_USER_NAME  "PipeWire"
16
+#define RAOP_AUTH_USER_NAME    "iTunes"
17
 
18
 #define MAX_PORT_RETRY 128
19
 
20
@@ -147,9 +148,9 @@
21
 #define DEFAULT_CHANNELS 2
22
 #define DEFAULT_POSITION " FL FR "
23
 
24
-#define VOLUME_MAX  0.0
25
-#define VOLUME_DEF -30.0
26
-#define VOLUME_MIN -144.0
27
+#define VOLUME_MAX 0.0
28
+#define VOLUME_MIN -30.0
29
+#define VOLUME_MUTE    -144.0
30
 
31
 #define MODULE_USAGE   "( raop.ip=<ip address of host> ) "                 \
32
            "( raop.port=<remote port> ) "                      \
33
@@ -228,13 +229,14 @@
34
 
35
    unsigned int do_disconnect:1;
36
 
37
-   uint8_t keyAES_CHUNK_SIZE; /* Key for aes-cbc */
38
-   uint8_t ivAES_CHUNK_SIZE;  /* Initialization vector for cbc */
39
+   uint8_t aes_keyAES_CHUNK_SIZE; /* Key for aes-cbc */
40
+   uint8_t aes_ivAES_CHUNK_SIZE;  /* Initialization vector for cbc */
41
    EVP_CIPHER_CTX *ctx;
42
 
43
    uint16_t control_port;
44
    int control_fd;
45
    struct spa_source *control_source;
46
+   struct spa_source *feedback_timer;
47
 
48
    uint16_t timing_port;
49
    int timing_fd;
50
@@ -247,7 +249,7 @@
51
    uint32_t block_size;
52
    uint32_t latency;
53
 
54
-   uint16_t seq;
55
+   uint16_t seq, cseq;
56
    uint32_t rtptime;
57
    uint32_t ssrc;
58
    uint32_t sync;
59
@@ -287,7 +289,7 @@
60
 static int aes_encrypt(struct impl *impl, uint8_t *data, int len)
61
 {
62
    int i = len & ~0xf, clen = i;
63
-   EVP_EncryptInit(impl->ctx, EVP_aes_128_cbc(), impl->key, impl->iv);
64
+   EVP_EncryptInit(impl->ctx, EVP_aes_128_cbc(), impl->aes_key, impl->aes_iv);
65
    EVP_EncryptUpdate(impl->ctx, data, &clen, data, i);
66
    return i;
67
 }
68
@@ -308,46 +310,104 @@
69
 static int send_udp_sync_packet(struct impl *impl,
70
        struct sockaddr *dest_addr, socklen_t addrlen)
71
 {
72
-   uint32_t pkt5;
73
+   uint32_t out3;
74
    uint32_t rtptime = impl->rtptime;
75
    uint32_t latency = impl->latency;
76
    uint64_t transmitted;
77
+   struct rtp_header header;
78
+   struct iovec iov2;
79
+   struct msghdr msg;
80
+   int res;
81
 
82
-   pkt0 = htonl(0x80d40007);
83
+   spa_zero(header);
84
+   header.v = 2;
85
    if (impl->first)
86
-       pkt0 |= htonl(0x10000000);
87
-   pkt1 = htonl(rtptime - latency);
88
+       header.x = 1;
89
+   header.m = 1;
90
+   header.pt = 84;
91
+   header.sequence_number = htons(impl->cseq);
92
+   header.timestamp = htonl(rtptime - latency);
93
+
94
+   iov0.iov_base = &header;
95
+   iov0.iov_len = 8;
96
+
97
    transmitted = ntp_now();
98
-   pkt2 = htonl(transmitted >> 32);
99
-   pkt3 = htonl(transmitted & 0xffffffff);
100
-   pkt4 = htonl(rtptime);
101
+   out0 = htonl(transmitted >> 32);
102
+   out1 = htonl(transmitted & 0xffffffff);
103
+   out2 = htonl(rtptime);
104
+
105
+   iov1.iov_base = out;
106
+   iov1.iov_len = sizeof(out);
107
+
108
+   msg.msg_name = dest_addr;
109
+   msg.msg_namelen = addrlen;
110
+   msg.msg_iov = iov;
111
+   msg.msg_iovlen = 2;
112
+   msg.msg_control = NULL;
113
+   msg.msg_controllen = 0;
114
+   msg.msg_flags = 0;
115
+
116
+   res = sendmsg(impl->control_fd, &msg, MSG_NOSIGNAL);
117
+   if (res < 0) {
118
+       res = -errno;
119
+       pw_log_warn("error sending control packet: %d", res);
120
+   }
121
+
122
+   impl->cseq = (impl->cseq + 1) & 0xffff;
123
 
124
-   pw_log_debug("sync: first:%d latency:%u now:%"PRIx64" rtptime:%u",
125
-           impl->first, latency, transmitted, rtptime);
126
+   pw_log_debug("raop control sync: cseq:%d first:%d latency:%u now:%"PRIx64" rtptime:%u",
127
+           impl->cseq, impl->first, latency, transmitted, rtptime);
128
 
129
-   return sendto(impl->control_fd, pkt, sizeof(pkt), 0, dest_addr, addrlen);
130
+   return res;
131
 }
132
 
133
 static int send_udp_timing_packet(struct impl *impl, uint64_t remote, uint64_t received,
134
        struct sockaddr *dest_addr, socklen_t addrlen)
135
 {
136
-   uint32_t pkt8;
137
+   uint32_t out6;
138
    uint64_t transmitted;
139
+   struct rtp_header header;
140
+   struct iovec iov2;
141
+   struct msghdr msg;
142
+   int res;
143
 
144
-   pkt0 = htonl(0x80d30007);
145
-   pkt1 = 0x00000000;
146
-   pkt2 = htonl(remote >> 32);
147
-   pkt3 = htonl(remote & 0xffffffff);
148
-   pkt4 = htonl(received >> 32);
149
-   pkt5 = htonl(received & 0xffffffff);
150
-   transmitted = ntp_now();
151
-   pkt6 = htonl(transmitted >> 32);
152
-   pkt7 = htonl(transmitted & 0xffffffff);
153
+   spa_zero(header);
154
+   header.v = 2;
155
+   header.pt = 83;
156
+   header.m = 1;
157
+
158
+   iov0.iov_base = &header;
159
+   iov0.iov_len = 8;
160
+
161
+   out0 = htonl(remote >> 32);
162
+   out1 = htonl(remote & 0xffffffff);
163
 
164
-   pw_log_debug("sync: remote:%"PRIx64" received:%"PRIx64" transmitted:%"PRIx64,
165
+   out2 = htonl(received >> 32);
166
+   out3 = htonl(received & 0xffffffff);
167
+   transmitted = ntp_now();
168
+   out4 = htonl(transmitted >> 32);
169
+   out5 = htonl(transmitted & 0xffffffff);
170
+
171
+   iov1.iov_base = out;
172
+   iov1.iov_len = sizeof(out);
173
+
174
+   msg.msg_name = dest_addr;
175
+   msg.msg_namelen = addrlen;
176
+   msg.msg_iov = iov;
177
+   msg.msg_iovlen = 2;
178
+   msg.msg_control = NULL;
179
+   msg.msg_controllen = 0;
180
+   msg.msg_flags = 0;
181
+
182
+   res = sendmsg(impl->timing_fd, &msg, MSG_NOSIGNAL);
183
+   if (res < 0) {
184
+       res = -errno;
185
+       pw_log_warn("error sending timing packet: %d", res);
186
+   }
187
+   pw_log_debug("raop timing sync: remote:%"PRIx64" received:%"PRIx64" transmitted:%"PRIx64,
188
            remote, received, transmitted);
189
 
190
-   return sendto(impl->timing_fd, pkt, sizeof(pkt), 0, dest_addr, addrlen);
191
+   return res;
192
 }
193
 
194
 static int write_codec_pcm(void *dst, void *frames, uint32_t n_frames)
195
@@ -382,8 +442,11 @@
196
 
197
 static int flush_to_udp_packet(struct impl *impl)
198
 {
199
-   const size_t max = 12 + 8 + impl->block_size;
200
-   uint32_t pktmax, len, n_frames;
201
+   const size_t max = 8 + impl->block_size;
202
+   uint32_t outmax, len, n_frames;
203
+   struct rtp_header header;
204
+   struct iovec iov2;
205
+   struct msghdr msg;
206
    uint8_t *dst;
207
    int res;
208
 
209
@@ -394,15 +457,21 @@
210
        impl->sync = 0;
211
        send_udp_sync_packet(impl, NULL, 0);
212
    }
213
-   pkt0 = htonl(0x80600000);
214
+
215
+   spa_zero(header);
216
+   header.v = 2;
217
+   header.pt = 96;
218
    if (impl->first)
219
-       pkt0 |= htonl((uint32_t)0x80 << 16);
220
-   pkt0 |= htonl((uint32_t)impl->seq);
221
-   pkt1 = htonl(impl->rtptime);
222
-   pkt2 = htonl(impl->ssrc);
223
+       header.m = 1;
224
+   header.sequence_number = htons(impl->seq);
225
+   header.timestamp = htonl(impl->rtptime);
226
+   header.ssrc = htonl(impl->ssrc);
227
+
228
+   iov0.iov_base = &header;
229
+   iov0.iov_len = 12;
230
 
231
    n_frames = impl->filled / impl->frame_size;
232
-   dst = (uint8_t*)&pkt3;
233
+   dst = (uint8_t*)&out0;
234
 
235
    switch (impl->codec) {
236
    case CODEC_PCM:
237
@@ -417,11 +486,25 @@
238
    if (impl->encryption == CRYPTO_RSA)
239
        aes_encrypt(impl, dst, len);
240
 
241
+   iov1.iov_base = out;
242
+   iov1.iov_len = len;
243
+
244
    impl->rtptime += n_frames;
245
    impl->seq = (impl->seq + 1) & 0xffff;
246
 
247
-   pw_log_debug("send %u", len + 12);
248
-   res = send(impl->server_fd, pkt, len + 12, 0);
249
+   msg.msg_name = NULL;
250
+   msg.msg_namelen = 0;
251
+   msg.msg_iov = iov;
252
+   msg.msg_iovlen = 2;
253
+   msg.msg_control = NULL;
254
+   msg.msg_controllen = 0;
255
+   msg.msg_flags = 0;
256
+
257
+   res = sendmsg(impl->server_fd, &msg, MSG_NOSIGNAL);
258
+   if (res < 0) {
259
+       res = -errno;
260
+       pw_log_warn("error streaming packet: %d", res);
261
+   }
262
 
263
    impl->first = false;
264
 
265
@@ -430,22 +513,34 @@
266
 
267
 static int flush_to_tcp_packet(struct impl *impl)
268
 {
269
-   const size_t max = 16 + 8 + impl->block_size;
270
-   uint32_t pktmax, len, n_frames;
271
+   const size_t max = 8 + impl->block_size;
272
+   uint32_t tcp_pkt1, outmax, len, n_frames;
273
+   struct rtp_header header;
274
+   struct iovec iov3;
275
+   struct msghdr msg;
276
    uint8_t *dst;
277
    int res;
278
 
279
    if (!impl->recording)
280
        return 0;
281
 
282
-   pkt0 = htonl(0x24000000);
283
-   pkt1 = htonl(0x80e00000);
284
-   pkt1 |= htonl((uint32_t)impl->seq);
285
-   pkt2 = htonl(impl->rtptime);
286
-   pkt3 = htonl(impl->ssrc);
287
+   tcp_pkt0 = htonl(0x24000000);
288
+
289
+   iov0.iov_base = &tcp_pkt;
290
+   iov0.iov_len = 4;
291
+
292
+   spa_zero(header);
293
+   header.v = 2;
294
+   header.pt = 96;
295
+   header.sequence_number = htons(impl->seq);
296
+   header.timestamp = htonl(impl->rtptime);
297
+   header.ssrc = htonl(impl->ssrc);
298
+
299
+   iov1.iov_base = &header;
300
+   iov1.iov_len = 12;
301
 
302
    n_frames = impl->filled / impl->frame_size;
303
-   dst = (uint8_t*)&pkt4;
304
+   dst = (uint8_t*)&out0;
305
 
306
    switch (impl->codec) {
307
    case CODEC_PCM:
308
@@ -460,13 +555,27 @@
309
    if (impl->encryption == CRYPTO_RSA)
310
        aes_encrypt(impl, dst, len);
311
 
312
-   pkt0 |= htonl((uint32_t) len + 12);
313
+   out0 |= htonl((uint32_t) len + 12);
314
+
315
+   iov2.iov_base = out;
316
+   iov2.iov_len = len;
317
 
318
    impl->rtptime += n_frames;
319
    impl->seq = (impl->seq + 1) & 0xffff;
320
 
321
-   pw_log_debug("send %u", len + 16);
322
-   res = send(impl->server_fd, pkt, len + 16, 0);
323
+   msg.msg_name = NULL;
324
+   msg.msg_namelen = 0;
325
+   msg.msg_iov = iov;
326
+   msg.msg_iovlen = 2;
327
+   msg.msg_control = NULL;
328
+   msg.msg_controllen = 0;
329
+   msg.msg_flags = 0;
330
+
331
+   res = sendmsg(impl->server_fd, &msg, MSG_NOSIGNAL);
332
+   if (res < 0) {
333
+       res = -errno;
334
+       pw_log_warn("error streaming packet: %d", res);
335
+   }
336
 
337
    impl->first = false;
338
 
339
@@ -783,7 +892,7 @@
340
    return 0;
341
 }
342
 
343
-static int rtsp_add_auth(struct impl *impl, const char *method)
344
+static int rtsp_add_raop_auth_header(struct impl *impl, const char *method)
345
 {
346
    char auth1024;
347
 
348
@@ -793,7 +902,7 @@
349
    if (spa_streq(impl->auth_method, "Basic")) {
350
        char buf256;
351
        char enc512;
352
-       spa_scnprintf(buf, sizeof(buf), "%s:%s", DEFAULT_USER_NAME, impl->password);
353
+       spa_scnprintf(buf, sizeof(buf), "%s:%s", RAOP_AUTH_USER_NAME, impl->password);
354
        base64_encode((uint8_t*)buf, strlen(buf), enc, '=');
355
        spa_scnprintf(auth, sizeof(auth), "Basic %s", enc);
356
    }
357
@@ -805,13 +914,13 @@
358
 
359
        url = pw_rtsp_client_get_url(impl->rtsp);
360
 
361
-       MD5_hash(h1, "%s:%s:%s", DEFAULT_USER_NAME, impl->realm, impl->password);
362
+       MD5_hash(h1, "%s:%s:%s", RAOP_AUTH_USER_NAME, impl->realm, impl->password);
363
        MD5_hash(h2, "%s:%s", method, url);
364
        MD5_hash(resp, "%s:%s:%s", h1, impl->nonce, h2);
365
 
366
        spa_scnprintf(auth, sizeof(auth),
367
                "username=\"%s\", realm=\"%s\", nonce=\"%s\", uri=\"%s\", response=\"%s\"",
368
-               DEFAULT_USER_NAME, impl->realm, impl->nonce, url, resp);
369
+               RAOP_AUTH_USER_NAME, impl->realm, impl->nonce, url, resp);
370
    }
371
    else
372
        goto error;
373
@@ -821,7 +930,7 @@
374
 
375
    return 0;
376
 error:
377
-   pw_log_error("error adding auth");
378
+   pw_log_error("error adding raop RSA auth");
379
    return -EINVAL;
380
 }
381
 
382
@@ -831,7 +940,7 @@
383
 {
384
    int res;
385
 
386
-   rtsp_add_auth(impl, method);
387
+   rtsp_add_raop_auth_header(impl, method);
388
 
389
    res = pw_rtsp_client_send(impl->rtsp, method, &impl->headers->dict,
390
            content_type, content, reply, impl);
391
@@ -872,10 +981,18 @@
392
 
393
    char header128, volstr64;
394
    snprintf(header, sizeof(header), "volume: %s\r\n",
395
-           spa_dtoa(volstr, sizeof(volstr), impl->volume));
396
+           spa_dtoa(volstr, sizeof(volstr), impl->mute ? VOLUME_MUTE : impl->volume));
397
    return rtsp_send(impl, "SET_PARAMETER", "text/parameters", header, rtsp_log_reply_status);
398
 }
399
 
400
+static void rtsp_do_post_feedback(void *data, uint64_t expirations)
401
+{
402
+   struct impl *impl = data;
403
+
404
+   pw_rtsp_client_url_send(impl->rtsp, "/feedback", "POST", &impl->headers->dict,
405
+                   NULL, NULL, 0, rtsp_log_reply_status, impl);
406
+}
407
+
408
 static int rtsp_record_reply(void *data, int status, const struct spa_dict *headers, const struct pw_array *content)
409
 {
410
    struct impl *impl = data;
411
@@ -886,9 +1003,19 @@
412
    struct spa_pod_builder b;
413
    struct spa_latency_info latency;
414
    char progress128;
415
+   struct timespec timeout, interval;
416
 
417
    pw_log_info("record status: %d", status);
418
 
419
+   timeout.tv_sec = 2;
420
+   timeout.tv_nsec = 0;
421
+   interval.tv_sec = 2;
422
+   interval.tv_nsec = 0;
423
+
424
+   if (!impl->feedback_timer)
425
+       impl->feedback_timer = pw_loop_add_timer(impl->loop, rtsp_do_post_feedback, impl);
426
+   pw_loop_update_timer(impl->loop, impl->feedback_timer, &timeout, &interval, false);
427
+
428
    if ((str = spa_dict_lookup(headers, "Audio-Latency")) != NULL) {
429
        uint32_t l;
430
        if (spa_atou32(str, &l, 0))
431
@@ -1241,7 +1368,6 @@
432
        if (!sdp)
433
            return -errno;
434
        break;
435
-
436
    case CRYPTO_AUTH_SETUP:
437
        sdp = spa_aprintf("v=0\r\n"
438
                "o=iTunes %s 0 IN IP%d %s\r\n"
439
@@ -1260,16 +1386,24 @@
440
        break;
441
 
442
    case CRYPTO_RSA:
443
-       if ((res = pw_getrandom(impl->key, sizeof(impl->key), 0)) < 0 ||
444
-           (res = pw_getrandom(impl->iv, sizeof(impl->iv), 0)) < 0)
445
+   {
446
+       uint8_t rac16;
447
+       char sac16*4;
448
+
449
+       if ((res = pw_getrandom(rac, sizeof(rac), 0)) < 0 ||
450
+           (res = pw_getrandom(impl->aes_key, sizeof(impl->aes_key), 0)) < 0 ||
451
+           (res = pw_getrandom(impl->aes_iv, sizeof(impl->aes_iv), 0)) < 0)
452
            return res;
453
 
454
-       rsa_len = rsa_encrypt(impl->key, 16, rsakey);
455
+       base64_encode(rac, sizeof(rac), sac, '\0');
456
+       pw_properties_set(impl->headers, "Apple-Challenge", sac);
457
+
458
+       rsa_len = rsa_encrypt(impl->aes_key, 16, rsakey);
459
        if (rsa_len < 0)
460
            return -rsa_len;
461
 
462
-           base64_encode(rsakey, rsa_len, key, '=');
463
-           base64_encode(impl->iv, 16, iv, '=');
464
+       base64_encode(rsakey, rsa_len, key, '=');
465
+       base64_encode(impl->aes_iv, 16, iv, '=');
466
 
467
        sdp = spa_aprintf("v=0\r\n"
468
                "o=iTunes %s 0 IN IP%d %s\r\n"
469
@@ -1287,6 +1421,7 @@
470
        if (!sdp)
471
            return -errno;
472
        break;
473
+   }
474
    default:
475
        return -ENOTSUP;
476
    }
477
@@ -1294,7 +1429,7 @@
478
    return rtsp_send(impl, "ANNOUNCE", "application/sdp", sdp, rtsp_announce_reply);
479
 }
480
 
481
-static int rtsp_auth_setup_reply(void *data, int status, const struct spa_dict *headers, const struct pw_array *content)
482
+static int rtsp_post_auth_setup_reply(void *data, int status, const struct spa_dict *headers, const struct pw_array *content)
483
 {
484
    struct impl *impl = data;
485
 
486
@@ -1303,7 +1438,7 @@
487
    return rtsp_do_announce(impl);
488
 }
489
 
490
-static int rtsp_do_auth_setup(struct impl *impl)
491
+static int rtsp_do_post_auth_setup(struct impl *impl)
492
 {
493
    static const unsigned char content33 =
494
        "\x01"
495
@@ -1312,10 +1447,10 @@
496
 
497
    return pw_rtsp_client_url_send(impl->rtsp, "/auth-setup", "POST", &impl->headers->dict,
498
                       "application/octet-stream", content, sizeof(content),
499
-                      rtsp_auth_setup_reply, impl);
500
+                      rtsp_post_auth_setup_reply, impl);
501
 }
502
 
503
-static int rtsp_auth_reply(void *data, int status, const struct spa_dict *headers, const struct pw_array *content)
504
+static int rtsp_options_auth_reply(void *data, int status, const struct spa_dict *headers, const struct pw_array *content)
505
 {
506
    struct impl *impl = data;
507
    int res = 0;
508
@@ -1325,7 +1460,7 @@
509
    switch (status) {
510
    case 200:
511
        if (impl->encryption == CRYPTO_AUTH_SETUP)
512
-           res = rtsp_do_auth_setup(impl);
513
+           res = rtsp_do_post_auth_setup(impl);
514
        else
515
            res = rtsp_do_announce(impl);
516
        break;
517
@@ -1351,7 +1486,7 @@
518
    return NULL;
519
 }
520
 
521
-static int rtsp_do_auth(struct impl *impl, const struct spa_dict *headers)
522
+static int rtsp_do_options_auth(struct impl *impl, const struct spa_dict *headers)
523
 {
524
    const char *str, *realm, *nonce;
525
    int n_tokens;
526
@@ -1382,7 +1517,7 @@
527
        impl->nonce = strdup(nonce);
528
    }
529
 
530
-   return rtsp_send(impl, "OPTIONS", NULL, NULL, rtsp_auth_reply);
531
+   return rtsp_send(impl, "OPTIONS", NULL, NULL, rtsp_options_auth_reply);
532
 }
533
 
534
 static int rtsp_options_reply(void *data, int status, const struct spa_dict *headers, const struct pw_array *content)
535
@@ -1394,11 +1529,11 @@
536
 
537
    switch (status) {
538
    case 401:
539
-       res = rtsp_do_auth(impl, headers);
540
+       res = rtsp_do_options_auth(impl, headers);
541
        break;
542
    case 200:
543
        if (impl->encryption == CRYPTO_AUTH_SETUP)
544
-           res = rtsp_do_auth_setup(impl);
545
+           res = rtsp_do_post_auth_setup(impl);
546
        else
547
            res = rtsp_do_announce(impl);
548
        break;
549
@@ -1410,27 +1545,24 @@
550
 {
551
    struct impl *impl = data;
552
    uint32_t sci2;
553
-   uint8_t rac16;
554
-   char sac16*4;
555
    int res;
556
 
557
    pw_log_info("connected");
558
 
559
    impl->connected = true;
560
 
561
-   if ((res = pw_getrandom(sci, sizeof(sci), 0)) < 0 ||
562
-       (res = pw_getrandom(rac, sizeof(rac), 0)) < 0) {
563
+   if ((res = pw_getrandom(sci, sizeof(sci), 0)) < 0) {
564
        pw_log_error("error generating random data: %s", spa_strerror(res));
565
        return;
566
    }
567
 
568
    pw_properties_setf(impl->headers, "Client-Instance",
569
-           "%08x%08x", sci0, sci1);
570
+           "%08X%08X", sci0, sci1);
571
 
572
-   base64_encode(rac, sizeof(rac), sac, '\0');
573
-   pw_properties_set(impl->headers, "Apple-Challenge", sac);
574
+   pw_properties_setf(impl->headers, "DACP-ID",
575
+           "%08X%08X", sci0, sci1);
576
 
577
-   pw_properties_set(impl->headers, "User-Agent", DEFAULT_USER_AGENT);
578
+   pw_properties_set(impl->headers, "User-Agent", DEFAULT_USER_NAME "/" PACKAGE_VERSION);
579
 
580
    pw_rtsp_client_send(impl->rtsp, "OPTIONS", &impl->headers->dict,
581
            NULL, NULL, rtsp_options_reply, impl);
582
@@ -1463,6 +1595,10 @@
583
        close(impl->timing_fd);
584
        impl->timing_fd = -1;
585
    }
586
+   if (impl->feedback_timer != NULL) {
587
+       pw_loop_destroy_source(impl->loop, impl->feedback_timer);
588
+       impl->feedback_timer = NULL;
589
+   }
590
    free(impl->auth_method);
591
    impl->auth_method = NULL;
592
    free(impl->realm);
593
@@ -1587,10 +1723,13 @@
594
        {
595
            bool mute;
596
            if (spa_pod_get_bool(&prop->value, &mute) == 0) {
597
-               impl->mute = mute;
598
+               if (impl->mute != mute) {
599
+                   impl->mute = mute;
600
+                   rtsp_send_volume(impl);
601
+               }
602
                         }
603
            spa_pod_builder_prop(&b, SPA_PROP_softMute, 0);
604
-           spa_pod_builder_bool(&b, impl->mute);
605
+           spa_pod_builder_bool(&b, false);
606
            spa_pod_builder_raw_padded(&b, prop, SPA_POD_PROP_SIZE(prop));
607
            break;
608
        }
609
@@ -1608,12 +1747,11 @@
610
                    soft_volsi = 1.0f;
611
                }
612
                volume /= n_vols;
613
-               volume = SPA_CLAMPF(20.0 * log10(volume), VOLUME_MIN, VOLUME_MAX);
614
+               volume = SPA_CLAMPF(cbrt(volume) * 30 - 30, VOLUME_MIN, VOLUME_MAX);
615
                impl->volume = volume;
616
 
617
                rtsp_send_volume(impl);
618
            }
619
-
620
            spa_pod_builder_prop(&b, SPA_PROP_softVolumes, 0);
621
            spa_pod_builder_array(&b, sizeof(float), SPA_TYPE_Float,
622
                    n_vols, soft_vols);
623
pipewire-0.3.80.tar.gz/src/modules/module-raop/rtsp-client.c -> pipewire-0.3.81.tar.gz/src/modules/module-raop/rtsp-client.c Changed
23
 
1
@@ -21,12 +21,12 @@
2
 
3
 struct message {
4
    struct spa_list link;
5
-   void *data;
6
    size_t len;
7
    size_t offset;
8
    uint32_t cseq;
9
    int (*reply) (void *user_data, int status, const struct spa_dict *headers, const struct pw_array *content);
10
    void *user_data;
11
+   unsigned char data;
12
 };
13
 
14
 enum client_recv_state {
15
@@ -598,7 +598,6 @@
16
 
17
    fclose(f);
18
 
19
-   msg->data = SPA_PTROFF(msg, sizeof(*msg), void);
20
    msg->len = len - sizeof(*msg);
21
    msg->offset = 0;
22
    msg->reply = reply;
23
pipewire-0.3.80.tar.gz/src/modules/module-rt.c -> pipewire-0.3.81.tar.gz/src/modules/module-rt.c Changed
21
 
1
@@ -35,6 +35,9 @@
2
 #if defined(__FreeBSD__) || defined(__MidnightBSD__)
3
 #include <sys/thr.h>
4
 #endif
5
+#if defined(__GNU__)
6
+#include <mach.h>
7
+#endif
8
 #include <fcntl.h>
9
 #include <unistd.h>
10
 #include <pthread.h>
11
@@ -221,6 +224,9 @@
12
    long pid;
13
    thr_self(&pid);
14
    return (pid_t)pid;
15
+#elif defined(__GNU__)
16
+       mach_port_t thread = mach_thread_self();
17
+       return (pid_t)thread;
18
 #else
19
 #error "No gettid impl"
20
 #endif
21
pipewire-0.3.80.tar.gz/src/modules/module-rtp-session.c -> pipewire-0.3.81.tar.gz/src/modules/module-rtp-session.c Changed
11
 
1
@@ -1335,7 +1335,8 @@
2
    if (spa_streq(service_name, "_pipewire-audio._udp")) {
3
        uint32_t mask = 0;
4
        for (l = txt; l && compatible; l = l->next) {
5
-           char *key, *value, *k = NULL;
6
+           const char *k = NULL;
7
+           char *key, *value;
8
 
9
            if (avahi_string_list_get_pair(l, &key, &value, NULL) != 0)
10
                break;
11
pipewire-0.3.80.tar.gz/src/pipewire/conf.c -> pipewire-0.3.81.tar.gz/src/pipewire/conf.c Changed
23
 
1
@@ -18,7 +18,7 @@
2
 #ifdef HAVE_PWD_H
3
 #include <pwd.h>
4
 #endif
5
-#if defined(__FreeBSD__) || defined(__MidnightBSD__)
6
+#if defined(__FreeBSD__) || defined(__MidnightBSD__) || defined(__GNU__)
7
 #ifndef O_PATH
8
 #define O_PATH 0
9
 #endif
10
@@ -599,10 +599,11 @@
11
        char key256, val1024;
12
        const char *str, *value;
13
        int match = 0, fail = 0;
14
-       int len, skip = 0;
15
+       int len;
16
 
17
        while (spa_json_get_string(&it0, key, sizeof(key)) > 0) {
18
            bool success = false;
19
+           int skip = 0;
20
 
21
            if ((len = spa_json_next(&it0, &value)) <= 0)
22
                break;
23
pipewire-0.3.80.tar.gz/src/pipewire/context.c -> pipewire-0.3.81.tar.gz/src/pipewire/context.c Changed
145
 
1
@@ -1306,9 +1306,10 @@
2
        struct spa_fraction latency = SPA_FRACTION(0, 0);
3
        struct spa_fraction max_latency = SPA_FRACTION(0, 0);
4
        struct spa_fraction rate = SPA_FRACTION(0, 0);
5
-       uint32_t quantum, target_rate, current_rate;
6
+       uint32_t target_quantum, target_rate, current_rate, current_quantum;
7
        uint64_t quantum_stamp = 0, rate_stamp = 0;
8
-       bool force_rate, force_quantum, restore_rate = false;
9
+       bool force_rate, force_quantum, restore_rate = false, restore_quantum = false;
10
+       bool do_reconfigure = false, was_target_pending;
11
        const uint32_t *node_rates;
12
        uint32_t node_n_rates, node_def_rate;
13
        uint32_t node_max_quantum, node_min_quantum, node_def_quantum, node_rate_quantum;
14
@@ -1364,10 +1365,10 @@
15
                 fraction_compare(&s->max_latency, &max_latency) < 0))
16
                max_latency = s->max_latency;
17
 
18
-           /* largest rate */
19
+           /* largest rate, which is in fact the smallest fraction */
20
            if (rate.denom == 0 ||
21
                (s->rate.denom > 0 &&
22
-                fraction_compare(&s->rate, &rate) > 0))
23
+                fraction_compare(&s->rate, &rate) < 0))
24
                rate = s->rate;
25
 
26
            if (s->active)
27
@@ -1386,6 +1387,12 @@
28
            pw_log_info("(%s-%u) restore rate", n->name, n->info.id);
29
            restore_rate = true;
30
        }
31
+       if (n->forced_quantum && !force_quantum && n->runnable) {
32
+           /* A node that was forced to a quantum but is no longer being
33
+            * forced can restore its quantum */
34
+           pw_log_info("(%s-%u) restore quantum", n->name, n->info.id);
35
+           restore_quantum = true;
36
+       }
37
 
38
        if (force_quantum)
39
            lock_quantum = false;
40
@@ -1416,8 +1423,9 @@
41
                        rate.denom, target_rate);
42
        }
43
 
44
+       was_target_pending = n->target_pending;
45
+
46
        if (target_rate != current_rate) {
47
-           bool do_reconfigure = false;
48
            /* we doing a rate switch */
49
            pw_log_info("(%s-%u) state:%s new rate:%u/(%u)->%u",
50
                    n->name, n->info.id,
51
@@ -1427,23 +1435,17 @@
52
 
53
            if (force_rate) {
54
                if (settings->clock_rate_update_mode == CLOCK_RATE_UPDATE_MODE_HARD)
55
-                   do_reconfigure = !n->target_pending;
56
+                   do_reconfigure |= !was_target_pending;
57
            } else {
58
                if (n->info.state >= PW_NODE_STATE_SUSPENDED)
59
-                   do_reconfigure = !n->target_pending;
60
+                   do_reconfigure |= !was_target_pending;
61
            }
62
-           if (do_reconfigure)
63
-               reconfigure_driver(context, n);
64
-
65
            /* we're setting the pending rate. This will become the new
66
             * current rate in the next iteration of the graph. */
67
            n->target_rate = SPA_FRACTION(1, target_rate);
68
-           n->target_pending = true;
69
            n->forced_rate = force_rate;
70
+           n->target_pending = true;
71
            current_rate = target_rate;
72
-           /* we might be suspended now and the links need to be prepared again */
73
-           if (do_reconfigure)
74
-               goto again;
75
        }
76
 
77
        if (node_rate_quantum != 0 && current_rate != node_rate_quantum) {
78
@@ -1460,26 +1462,42 @@
79
                node_max_quantum = tmp;
80
        }
81
 
82
-       quantum = node_def_quantum;
83
-       if (latency.denom != 0)
84
-           quantum = (latency.num * current_rate / latency.denom);
85
-       quantum = SPA_CLAMP(quantum, node_min_quantum, node_max_quantum);
86
-       quantum = SPA_MIN(quantum, lim_quantum);
87
-
88
-       if (settings->clock_power_of_two_quantum)
89
-           quantum = flp2(quantum);
90
+       current_quantum = n->target_quantum;
91
+       if (!restore_quantum &&
92
+          (lock_quantum || n->reconfigure || !running ||
93
+           (!force_quantum && (n->info.state > PW_NODE_STATE_IDLE))))
94
+           target_quantum = current_quantum;
95
+       else {
96
+           target_quantum = node_def_quantum;
97
+           if (latency.denom != 0)
98
+               target_quantum = (latency.num * current_rate / latency.denom);
99
+           target_quantum = SPA_CLAMP(target_quantum, node_min_quantum, node_max_quantum);
100
+           target_quantum = SPA_MIN(target_quantum, lim_quantum);
101
+
102
+           if (settings->clock_power_of_two_quantum)
103
+               target_quantum = flp2(target_quantum);
104
+       }
105
 
106
-       if (running && quantum != n->target_quantum && !lock_quantum) {
107
+       if (target_quantum != current_quantum) {
108
            pw_log_info("(%s-%u) new quantum:%"PRIu64"->%u",
109
                    n->name, n->info.id,
110
                    n->target_quantum,
111
-                   quantum);
112
+                   target_quantum);
113
            /* this is the new pending quantum */
114
-           n->target_quantum = quantum;
115
+           n->target_quantum = target_quantum;
116
+           n->forced_quantum = force_quantum;
117
            n->target_pending = true;
118
+
119
+           if (force_quantum)
120
+               do_reconfigure |= !was_target_pending;
121
        }
122
 
123
        if (n->target_pending) {
124
+           if (do_reconfigure) {
125
+               reconfigure_driver(context, n);
126
+               /* we might be suspended now and the links need to be prepared again */
127
+               goto again;
128
+           }
129
            /* we have a pending change. We place the new values in the
130
             * pending fields so that they are picked up by the driver in
131
             * the next cycle */
132
@@ -1501,8 +1519,10 @@
133
            n->target_rate = n->rt.position->clock.target_rate;
134
        }
135
 
136
-       pw_log_debug("%p: driver %p running:%d runnable:%d quantum:%u '%s'",
137
-               context, n, running, n->runnable, quantum, n->name);
138
+       pw_log_debug("%p: driver %p running:%d runnable:%d quantum:%u rate:%u (%"PRIu64"/%u)'%s'",
139
+               context, n, running, n->runnable, target_quantum, target_rate,
140
+               n->rt.position->clock.target_duration,
141
+               n->rt.position->clock.target_rate.denom, n->name);
142
 
143
        /* first change the node states of the followers to the new target */
144
        spa_list_for_each(s, &n->follower_list, follower_link) {
145
pipewire-0.3.80.tar.gz/src/pipewire/impl-link.c -> pipewire-0.3.81.tar.gz/src/pipewire/impl-link.c Changed
18
 
1
@@ -1545,6 +1545,7 @@
2
 void pw_impl_link_destroy(struct pw_impl_link *link)
3
 {
4
    struct impl *impl = SPA_CONTAINER_OF(link, struct impl, this);
5
+   bool was_prepared = link->prepared;
6
 
7
    pw_log_debug("%p: destroy", impl);
8
    pw_log_info("(%s) destroy", link->name);
9
@@ -1570,7 +1571,7 @@
10
        pw_global_destroy(link->global);
11
    }
12
 
13
-   if (link->prepared)
14
+   if (was_prepared)
15
        pw_context_recalc_graph(link->context, "link destroy");
16
 
17
    pw_log_debug("%p: free", impl);
18
pipewire-0.3.80.tar.gz/src/pipewire/impl-node.c -> pipewire-0.3.81.tar.gz/src/pipewire/impl-node.c Changed
42
 
1
@@ -8,6 +8,7 @@
2
 #include <unistd.h>
3
 #include <errno.h>
4
 #include <time.h>
5
+#include <malloc.h>
6
 
7
 #include <spa/support/system.h>
8
 #include <spa/pod/parser.h>
9
@@ -1321,6 +1322,7 @@
10
 
11
    this->target_rate = SPA_FRACTION(1, rate);
12
    this->target_quantum = quantum;
13
+   this->elapsed = 0;
14
 
15
    pos->clock.rate = pos->clock.target_rate = this->target_rate;
16
    pos->clock.duration = pos->clock.target_duration = this->target_quantum;
17
@@ -1744,8 +1746,10 @@
18
        if (all_ready)
19
            a->position.state = SPA_IO_POSITION_STATE_RUNNING;
20
    }
21
-   if (SPA_LIKELY(a->position.state != SPA_IO_POSITION_STATE_RUNNING))
22
-       a->position.offset += a->position.clock.duration;
23
+   if (SPA_LIKELY(a->position.state == SPA_IO_POSITION_STATE_RUNNING))
24
+       node->elapsed += a->position.clock.duration;
25
+
26
+   a->position.offset = a->position.clock.position - node->elapsed;
27
 }
28
 
29
 /* Called from the data-loop and it is the starting point for driver nodes.
30
@@ -2103,6 +2107,11 @@
31
 
32
    spa_system_close(node->data_system, node->source.fd);
33
    free(impl);
34
+
35
+#ifdef HAVE_MALLOC_TRIM
36
+   int res = malloc_trim(0);
37
+   pw_log_debug("malloc_trim(): %d", res);
38
+#endif
39
 }
40
 
41
 SPA_EXPORT
42
pipewire-0.3.80.tar.gz/src/pipewire/mem.c -> pipewire-0.3.81.tar.gz/src/pipewire/mem.c Changed
20
 
1
@@ -25,7 +25,8 @@
2
 PW_LOG_TOPIC_EXTERN(log_mem);
3
 #define PW_LOG_TOPIC_DEFAULT log_mem
4
 
5
-#if !defined(__FreeBSD__) && !defined(__MidnightBSD__) && !defined(HAVE_MEMFD_CREATE)
6
+#if !defined(__FreeBSD__) && !defined(__MidnightBSD__) && !defined(__GNU__) \
7
+       && !defined(HAVE_MEMFD_CREATE)
8
 /*
9
  * No glibc wrappers exist for memfd_create(2), so provide our own.
10
  *
11
@@ -42,7 +43,7 @@
12
 #define HAVE_MEMFD_CREATE 1
13
 #endif
14
 
15
-#if defined(__FreeBSD__) || defined(__MidnightBSD__)
16
+#if defined(__FreeBSD__) || defined(__MidnightBSD__) || defined(__GNU__)
17
 #define MAP_LOCKED 0
18
 #endif
19
 
20
pipewire-0.3.80.tar.gz/src/pipewire/pipewire.c -> pipewire-0.3.81.tar.gz/src/pipewire/pipewire.c Changed
28
 
1
@@ -7,7 +7,7 @@
2
 #include <unistd.h>
3
 #include <limits.h>
4
 #include <stdio.h>
5
-#if !defined(__FreeBSD__) && !defined(__MidnightBSD__)
6
+#if !defined(__FreeBSD__) && !defined(__MidnightBSD__) && !defined(__GNU__)
7
 #include <sys/prctl.h>
8
 #endif
9
 #include <pwd.h>
10
@@ -720,7 +720,7 @@
11
    static char namePATH_MAX;
12
 
13
    spa_memzero(name, sizeof(name));
14
-#if defined(__linux__) || defined(__FreeBSD_kernel__) || defined(__MidnightBSD_kernel__)
15
+#if defined(__linux__) || defined(__FreeBSD_kernel__) || defined(__MidnightBSD_kernel__) || defined(__GNU__)
16
    {
17
        if (readlink("/proc/self/exe", name, sizeof(name)-1) > 0) {
18
            prgname = strrchr(name, '/') + 1;
19
@@ -738,7 +738,7 @@
20
        }
21
    }
22
 #endif
23
-#if !defined(__FreeBSD__) && !defined(__MidnightBSD__)
24
+#if !defined(__FreeBSD__) && !defined(__MidnightBSD__) && !defined(__GNU__)
25
    {
26
        if (prctl(PR_GET_NAME, (unsigned long) name, 0, 0, 0) == 0) {
27
            prgname = name;
28
pipewire-0.3.80.tar.gz/src/pipewire/private.h -> pipewire-0.3.81.tar.gz/src/pipewire/private.h Changed
26
 
1
@@ -22,7 +22,7 @@
2
 #include <spa/utils/result.h>
3
 #include <spa/utils/type-info.h>
4
 
5
-#if defined(__FreeBSD__) || defined(__MidnightBSD__)
6
+#if defined(__FreeBSD__) || defined(__MidnightBSD__) || defined(__GNU__)
7
 struct ucred {
8
 };
9
 #endif
10
@@ -673,6 +673,7 @@
11
    unsigned int suspend_on_idle:1;
12
    unsigned int reconfigure:1;
13
    unsigned int forced_rate:1;
14
+   unsigned int forced_quantum:1;
15
    unsigned int trigger:1;     /**< has the TRIGGER property and needs an extra
16
                      *  trigger to start processing. */
17
    unsigned int can_suspend:1;
18
@@ -731,6 +732,7 @@
19
    uint64_t target_quantum;
20
 
21
    uint64_t driver_start;
22
+   uint64_t elapsed;       /* elapsed time in playing */
23
 
24
    void *user_data;                /**< extra user data */
25
 };
26
pipewire-0.3.80.tar.gz/src/pipewire/stream.c -> pipewire-0.3.81.tar.gz/src/pipewire/stream.c Changed
35
 
1
@@ -157,6 +157,7 @@
2
    unsigned int driving:1;
3
    unsigned int using_trigger:1;
4
    unsigned int trigger:1;
5
+   unsigned int early_process:1;
6
    int in_set_param;
7
    int in_emit_param_changed;
8
 };
9
@@ -1103,7 +1104,7 @@
10
             * rate matching node (audioconvert) has been scheduled to
11
             * update the values. */
12
            ask_more = !impl->process_rt && impl->rate_match == NULL &&
13
-               queue_is_empty(impl, &impl->queued) &&
14
+               (impl->early_process || queue_is_empty(impl, &impl->queued)) &&
15
                !queue_is_empty(impl, &impl->dequeued);
16
            pw_log_trace_fp("%p: pop %d %p ask_more:%u %p", stream, b->id, io,
17
                    ask_more, impl->rate_match);
18
@@ -1121,7 +1122,7 @@
19
        }
20
    } else {
21
        ask_more = !impl->process_rt &&
22
-           queue_is_empty(impl, &impl->queued) &&
23
+           (impl->early_process || queue_is_empty(impl, &impl->queued)) &&
24
            !queue_is_empty(impl, &impl->dequeued);
25
    }
26
 
27
@@ -1912,6 +1913,7 @@
28
        impl->node_methods.process = impl_node_process_output;
29
 
30
    impl->process_rt = SPA_FLAG_IS_SET(flags, PW_STREAM_FLAG_RT_PROCESS);
31
+   impl->early_process = SPA_FLAG_IS_SET(flags, PW_STREAM_FLAG_EARLY_PROCESS);
32
 
33
    impl->impl_node.iface = SPA_INTERFACE_INIT(
34
            SPA_TYPE_INTERFACE_Node,
35
pipewire-0.3.80.tar.gz/src/pipewire/stream.h -> pipewire-0.3.81.tar.gz/src/pipewire/stream.h Changed
13
 
1
@@ -394,6 +394,11 @@
2
                              *  does a trigger_process() that will then
3
                              *  dequeue/queue a buffer from another process()
4
                              *  function. since 0.3.73 */
5
+   PW_STREAM_FLAG_EARLY_PROCESS    = (1 << 11),    /**< Call process as soon as there is a buffer
6
+                             *  to dequeue. This is only relevant for
7
+                             *  playback and when not using RT_PROCESS. It
8
+                             *  can be used to keep the maximum number of
9
+                             *  buffers queued. Since 0.3.81 */
10
 };
11
 
12
 /** Create a new unconneced \ref pw_stream
13
pipewire-0.3.80.tar.gz/src/pipewire/thread.c -> pipewire-0.3.81.tar.gz/src/pipewire/thread.c Changed
11
 
1
@@ -53,6 +53,9 @@
2
 }
3
 #endif
4
 #endif
5
+#if defined(__GNU__)
6
+int pthread_setname_np(pthread_t thread, const char *name) { return 0; }
7
+#endif
8
 
9
 static struct spa_thread *impl_create(void *object,
10
            const struct spa_dict *props,
11
pipewire-0.3.80.tar.gz/src/pipewire/utils.h -> pipewire-0.3.81.tar.gz/src/pipewire/utils.h Changed
9
 
1
@@ -15,6 +15,7 @@
2
 #ifndef _POSIX_C_SOURCE
3
 # include <sys/mount.h>
4
 #endif
5
+#include <errno.h>
6
 
7
 #ifndef ENODATA
8
 #define ENODATA 9919
9
pipewire-0.3.81.tar.gz/src/tools/dfffile.c Added
315
 
1
@@ -0,0 +1,313 @@
2
+/* PipeWire */
3
+/* SPDX-FileCopyrightText: Copyright © 2021 Wim Taymans */
4
+/* SPDX-License-Identifier: MIT */
5
+
6
+#include <errno.h>
7
+#include <sys/mman.h>
8
+#include <sys/stat.h>
9
+#include <unistd.h>
10
+#include <fcntl.h>
11
+#include <math.h>
12
+
13
+#include <spa/utils/string.h>
14
+#include <spa/debug/mem.h>
15
+
16
+#include "dfffile.h"
17
+
18
+struct dff_file {
19
+   uint8_t *data;
20
+   size_t size;
21
+
22
+   int mode;
23
+   int fd;
24
+
25
+   struct dff_file_info info;
26
+
27
+   uint8_t *p;
28
+   size_t offset;
29
+};
30
+
31
+struct dff_chunk {
32
+   uint32_t id;
33
+   uint64_t size;
34
+   void *data;
35
+};
36
+
37
+#define FOURCC(a,b,c,d) (d | (c << 8) | (b << 16) | (a << 24))
38
+
39
+static inline uint16_t parse_be16(const uint8_t *in)
40
+{
41
+   return (in0 << 8) | in1;
42
+}
43
+static inline uint32_t parse_be32(const uint8_t *in)
44
+{
45
+   return FOURCC(in0, in1, in2, in3);
46
+}
47
+
48
+static inline uint64_t parse_be64(const uint8_t *in)
49
+{
50
+   uint64_t res = in7;
51
+   res |= ((uint64_t)in6) << 8;
52
+   res |= ((uint64_t)in5) << 16;
53
+   res |= ((uint64_t)in4) << 24;
54
+   res |= ((uint64_t)in3) << 32;
55
+   res |= ((uint64_t)in2) << 40;
56
+   res |= ((uint64_t)in1) << 48;
57
+   res |= ((uint64_t)in0) << 56;
58
+   return res;
59
+}
60
+
61
+static inline int f_avail(struct dff_file *f)
62
+{
63
+   if (f->p < f->data + f->size)
64
+       return f->size + f->data - f->p;
65
+   return 0;
66
+}
67
+
68
+static int read_chunk(struct dff_file *f, struct dff_chunk *c)
69
+{
70
+   if (f_avail(f) < 12)
71
+       return -ENOSPC;
72
+
73
+   c->id = parse_be32(f->p);   /* id of this chunk */
74
+   c->size = parse_be64(f->p + 4); /* size of this chunk */
75
+   f->p += 12;
76
+   c->data = f->p;
77
+   return 0;
78
+}
79
+
80
+static int skip_chunk(struct dff_file *f, const struct dff_chunk *c)
81
+{
82
+   f->p = SPA_PTROFF(c->data, c->size, uint8_t);
83
+   return 0;
84
+}
85
+
86
+static int read_PROP(struct dff_file *f, struct dff_chunk *prop)
87
+{
88
+   struct dff_chunk c1;
89
+   int res;
90
+
91
+   if (f_avail(f) < 4 ||
92
+       memcmp(prop->data, "SND ", 4) != 0)
93
+       return -EINVAL;
94
+   f->p += 4;
95
+
96
+   while (f->p < SPA_PTROFF(prop->data, prop->size, uint8_t)) {
97
+       if ((res = read_chunk(f, &c0)) < 0)
98
+           return res;
99
+
100
+       switch (c0.id) {
101
+       case FOURCC('F', 'S', ' ', ' '):
102
+           f->info.rate = parse_be32(f->p);
103
+           break;
104
+       case FOURCC('C', 'H', 'N', 'L'):
105
+           f->info.channels = parse_be16(f->p);
106
+           switch (f->info.channels) {
107
+           case 2:
108
+               f->info.channel_type = 2;
109
+               break;
110
+           case 5:
111
+               f->info.channel_type = 6;
112
+               break;
113
+           case 6:
114
+               f->info.channel_type = 7;
115
+               break;
116
+           }
117
+           break;
118
+       case FOURCC('C', 'M', 'P', 'R'):
119
+       {
120
+           uint32_t cmpr = parse_be32(f->p);
121
+           if (cmpr != FOURCC('D', 'S', 'D', ' '))
122
+               return -ENOTSUP;
123
+           break;
124
+       }
125
+       case FOURCC('A', 'B', 'S', 'S'):
126
+           break;
127
+       case FOURCC('L', 'S', 'C', 'O'):
128
+           break;
129
+       default:
130
+           break;
131
+       }
132
+       skip_chunk(f, &c0);
133
+   }
134
+   return 0;
135
+}
136
+
137
+static int read_FRM8(struct dff_file *f)
138
+{
139
+   struct dff_chunk c2;
140
+   int res;
141
+   bool found_dsd = false;
142
+
143
+   if ((res = read_chunk(f, &c0)) < 0)
144
+       return res;
145
+   if (c0.id != FOURCC('F','R','M','8'))
146
+       return -EINVAL;
147
+   if (f_avail(f) < 4 ||
148
+       memcmp(c0.data, "DSD ", 4) != 0)
149
+       return -EINVAL;
150
+   f->p += 4;
151
+
152
+   while (true) {
153
+       if ((res = read_chunk(f, &c1)) < 0)
154
+           return res;
155
+
156
+       switch (c1.id) {
157
+       case FOURCC('F', 'V', 'E', 'R'):
158
+           break;
159
+       case FOURCC('P', 'R', 'O', 'P'):
160
+           read_PROP(f, &c1);
161
+           break;
162
+       case FOURCC('D', 'S', 'D', ' '):
163
+       {
164
+           f->info.length = c1.size;
165
+           f->info.samples = c1.size / f->info.channels;
166
+           f->info.lsb = 0;
167
+           f->info.blocksize = 1;
168
+           found_dsd = true;
169
+           break;
170
+       }
171
+       default:
172
+           break;
173
+       }
174
+       if (found_dsd)
175
+           break;
176
+
177
+       skip_chunk(f, &c1);
178
+   }
179
+   return 0;
180
+}
181
+
182
+static int open_read(struct dff_file *f, const char *filename, struct dff_file_info *info)
183
+{
184
+   int res;
185
+   struct stat st;
186
+
187
+   if ((f->fd = open(filename, O_RDONLY)) < 0) {
188
+       res = -errno;
189
+       goto exit;
190
+   }
191
+   if (fstat(f->fd, &st) < 0) {
192
+       res = -errno;
193
+       goto exit_close;
194
+   }
195
+   f->size = st.st_size;
196
+
197
+   f->data = mmap(NULL, f->size, PROT_READ, MAP_SHARED, f->fd, 0);
198
+   if (f->data == MAP_FAILED) {
199
+       res = -errno;
200
+       goto exit_close;
201
+   }
202
+
203
+   f->p = f->data;
204
+
205
+   if ((res = read_FRM8(f)) < 0)
206
+       goto exit_unmap;
207
+
208
+   f->mode = 1;
209
+   *info = f->info;
210
+   return 0;
211
+
212
+exit_unmap:
213
+   munmap(f->data, f->size);
214
+exit_close:
215
+   close(f->fd);
216
+exit:
217
+   return res;
218
+}
219
+
220
+struct dff_file *
221
+dff_file_open(const char *filename, const char *mode, struct dff_file_info *info)
222
+{
223
+        int res;
224
+        struct dff_file *f;
225
+
226
+        f = calloc(1, sizeof(struct dff_file));
227
+        if (f == NULL)
228
+                return NULL;
229
+
230
+        if (spa_streq(mode, "r")) {
231
+                if ((res = open_read(f, filename, info)) < 0)
232
+                        goto exit_free;
233
+        } else {
234
+                res = -EINVAL;
235
+                goto exit_free;
236
+        }
237
+        return f;
238
+
239
+exit_free:
240
+        free(f);
241
+        errno = -res;
242
+        return NULL;
243
+}
244
+
245
+static const uint8_t bitrev256 = {
246
+   0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0, 0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0,
247
+   0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8, 0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8,
248
+   0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4, 0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4,
249
+   0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec, 0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc,
250
+   0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2, 0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2,
251
+   0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea, 0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa,
252
+   0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6, 0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6,
253
+   0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee, 0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe,
254
+   0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1, 0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1,
255
+   0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9, 0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9,
256
+   0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5, 0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5,
257
+   0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed, 0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd,
258
+   0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3, 0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3,
259
+   0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb, 0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb,
260
+   0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7, 0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7,
261
+   0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef, 0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff,
262
+};
263
+
264
+ssize_t
265
+dff_file_read(struct dff_file *f, void *data, size_t samples, const struct dff_layout *layout)
266
+{
267
+   uint8_t *d = data;
268
+   int32_t step = SPA_ABS(layout->interleave);
269
+   uint32_t channels = f->info.channels;
270
+   bool rev = layout->lsb != f->info.lsb;
271
+   size_t total, offset, scale;
272
+
273
+   offset = f->offset;
274
+   scale = SPA_CLAMP(f->info.rate / (44100u * 64u), 1u, 4u);
275
+
276
+   samples *= step;
277
+   samples *= scale;
278
+
279
+   for (total = 0; total < samples && offset < f->info.length; total++) {
280
+       uint32_t i;
281
+       int32_t j;
282
+       const uint8_t *s = f->p + offset;
283
+
284
+       for (i = 0; i < layout->channels; i++) {
285
+           if (layout->interleave > 0) {
286
+               for (j = 0; j < step; j++)
287
+                   *d++ = rev ?
288
+                       bitrevsj * channels + i :
289
+                       sj * channels + i;
290
+           } else {
291
+               for (j = step-1; j >= 0; j--)
292
+                   *d++ = rev ?
293
+                       bitrevsj * channels + i :
294
+                       sj * channels + i;
295
+           }
296
+       }
297
+       offset += step * channels;
298
+   }
299
+   f->offset = offset;
300
+
301
+   return total;
302
+}
303
+
304
+int dff_file_close(struct dff_file *f)
305
+{
306
+   if (f->mode == 1) {
307
+       munmap(f->data, f->size);
308
+   } else
309
+       return -EINVAL;
310
+
311
+   close(f->fd);
312
+   free(f);
313
+   return 0;
314
+}
315
pipewire-0.3.81.tar.gz/src/tools/dfffile.h Added
33
 
1
@@ -0,0 +1,31 @@
2
+/* PipeWire */
3
+/* SPDX-FileCopyrightText: Copyright © 2023 Wim Taymans */
4
+/* SPDX-License-Identifier: MIT */
5
+
6
+#include <stdio.h>
7
+
8
+#include <spa/utils/defs.h>
9
+
10
+struct dff_file;
11
+
12
+struct dff_file_info {
13
+   uint32_t channel_type;
14
+   uint32_t channels;
15
+   uint32_t rate;
16
+   bool lsb;
17
+   uint64_t samples;
18
+   uint64_t length;
19
+   uint32_t blocksize;
20
+};
21
+
22
+struct dff_layout {
23
+   int32_t interleave;
24
+   uint32_t channels;
25
+   bool lsb;
26
+};
27
+
28
+struct dff_file * dff_file_open(const char *filename, const char *mode, struct dff_file_info *info);
29
+
30
+ssize_t dff_file_read(struct dff_file *f, void *data, size_t samples, const struct dff_layout *layout);
31
+
32
+int dff_file_close(struct dff_file *f);
33
pipewire-0.3.80.tar.gz/src/tools/meson.build -> pipewire-0.3.81.tar.gz/src/tools/meson.build Changed
9
 
1
@@ -47,6 +47,7 @@
2
   pwcat_sources = 
3
     'pw-cat.c',
4
     'midifile.c',
5
+    'dfffile.c',
6
     'dsffile.c',
7
   
8
 
9
pipewire-0.3.80.tar.gz/src/tools/pw-cat.c -> pipewire-0.3.81.tar.gz/src/tools/pw-cat.c Changed
130
 
1
@@ -41,6 +41,7 @@
2
 #endif
3
 
4
 #include "midifile.h"
5
+#include "dfffile.h"
6
 #include "dsffile.h"
7
 
8
 #define DEFAULT_MEDIA_TYPE "Audio"
9
@@ -143,6 +144,11 @@
10
        struct dsf_file_info info;
11
        struct dsf_layout layout;
12
    } dsf;
13
+   struct {
14
+       struct dff_file *file;
15
+       struct dff_file_info info;
16
+       struct dff_layout layout;
17
+   } dff;
18
 
19
 #ifdef HAVE_PW_CAT_FFMPEG_INTEGRATION
20
    struct {
21
@@ -798,6 +804,10 @@
22
    data->dsf.layout.channels = info.info.dsd.channels;
23
    data->dsf.layout.lsb = info.info.dsd.bitorder == SPA_PARAM_BITORDER_lsb;
24
 
25
+   data->dff.layout.interleave = info.info.dsd.interleave,
26
+   data->dff.layout.channels = info.info.dsd.channels;
27
+   data->dff.layout.lsb = info.info.dsd.bitorder == SPA_PARAM_BITORDER_lsb;
28
+
29
    data->stride = data->dsf.layout.channels * SPA_ABS(data->dsf.layout.interleave);
30
 
31
    if (data->verbose) {
32
@@ -1164,26 +1174,46 @@
33
    return dsf_file_read(d->dsf.file, src, n_frames, &d->dsf.layout);
34
 }
35
 
36
-static int setup_dsffile(struct data *data)
37
+static int dff_play(struct data *d, void *src, unsigned int n_frames, bool *null_frame)
38
+{
39
+   return dff_file_read(d->dff.file, src, n_frames, &d->dff.layout);
40
+}
41
+
42
+static int setup_dsdfile(struct data *data)
43
 {
44
    if (data->mode == mode_record)
45
        return -ENOTSUP;
46
 
47
    data->dsf.file = dsf_file_open(data->filename, "r", &data->dsf.info);
48
    if (data->dsf.file == NULL) {
49
-       fprintf(stderr, "dsffile: can't read dsf file '%s': %m\n", data->filename);
50
-       return -errno;
51
+       data->dff.file = dff_file_open(data->filename, "r", &data->dff.info);
52
+       if (data->dff.file == NULL) {
53
+           fprintf(stderr, "dsdfile: can't read dsd file '%s': %m\n", data->filename);
54
+           return -errno;
55
+       }
56
    }
57
 
58
-   if (data->verbose)
59
-       printf("dsffile: opened file \"%s\" channels:%d rate:%d samples:%"PRIu64" bitorder:%s\n",
60
+   if (data->dsf.file != NULL) {
61
+       if (data->verbose)
62
+           printf("dsffile: opened file \"%s\" channels:%d rate:%d "
63
+                   "samples:%"PRIu64" bitorder:%s\n",
64
                data->filename,
65
                data->dsf.info.channels, data->dsf.info.rate,
66
                data->dsf.info.samples,
67
                data->dsf.info.lsb ? "lsb" : "msb");
68
 
69
-   data->fill = dsf_play;
70
+       data->fill = dsf_play;
71
+   } else {
72
+       if (data->verbose)
73
+           printf("dfffile: opened file \"%s\" channels:%d rate:%d "
74
+                   "samples:%"PRIu64" bitorder:%s\n",
75
+               data->filename,
76
+               data->dff.info.channels, data->dff.info.rate,
77
+               data->dff.info.samples,
78
+               data->dff.info.lsb ? "lsb" : "msb");
79
 
80
+       data->fill = dff_play;
81
+   }
82
    return 0;
83
 }
84
 
85
@@ -1839,7 +1869,7 @@
86
            ret = setup_midifile(&data);
87
            break;
88
        case TYPE_DSD:
89
-           ret = setup_dsffile(&data);
90
+           ret = setup_dsdfile(&data);
91
            break;
92
 #ifdef HAVE_PW_CAT_FFMPEG_INTEGRATION
93
        case TYPE_ENCODED:
94
@@ -1918,13 +1948,21 @@
95
    case TYPE_DSD:
96
    {
97
        struct spa_audio_info_dsd info;
98
+       uint32_t channel_type;
99
 
100
        spa_zero(info);
101
-       info.channels = data.dsf.info.channels;
102
-       info.rate = data.dsf.info.rate / 8;
103
+       if (data.dsf.file != NULL) {
104
+           info.channels = data.dsf.info.channels;
105
+           info.rate = data.dsf.info.rate / 8;
106
+           channel_type = data.dsf.info.channel_type;
107
+       } else {
108
+           info.channels = data.dff.info.channels;
109
+           info.rate = data.dff.info.rate / 8;
110
+           channel_type = data.dff.info.channel_type;
111
+       }
112
 
113
        SPA_FOR_EACH_ELEMENT_VAR(dsd_layouts, i) {
114
-           if (i->type != data.dsf.info.channel_type)
115
+           if (i->type != channel_type)
116
                continue;
117
            info.channels = i->info.n_channels;
118
            memcpy(info.position, i->info.position,
119
@@ -2020,6 +2058,10 @@
120
        sf_close(data.file);
121
    if (data.midi.file)
122
        midi_file_close(data.midi.file);
123
+   if (data.dsf.file)
124
+       dsf_file_close(data.dsf.file);
125
+   if (data.dff.file)
126
+       dff_file_close(data.dff.file);
127
 #ifdef HAVE_PW_CAT_FFMPEG_INTEGRATION
128
    if (data.encoded.packet)
129
        av_packet_free(&data.encoded.packet);
130
pipewire-0.3.80.tar.gz/src/tools/pw-cli.c -> pipewire-0.3.81.tar.gz/src/tools/pw-cli.c Changed
250
 
1
@@ -348,6 +348,10 @@
2
    bool ret;
3
 
4
    global = calloc(1, sizeof(struct global));
5
+   if (global == NULL) {
6
+       fprintf(stderr, "Allocation failed: %m");
7
+       return;
8
+   }
9
    global->rd = rd;
10
    global->id = id;
11
    global->permissions = permissions;
12
@@ -615,6 +619,8 @@
13
    struct pw_core_info *info = pd->info;
14
 
15
    info_global(pd);
16
+   if (info == NULL)
17
+       return;
18
    printf("\tcookie: %u\n", info->cookie);
19
    printf("\tuser-name: \"%s\"\n", info->user_name);
20
    printf("\thost-name: \"%s\"\n", info->host_name);
21
@@ -629,6 +635,8 @@
22
    struct pw_module_info *info = pd->info;
23
 
24
    info_global(pd);
25
+   if (info == NULL)
26
+       return;
27
    printf("\tname: \"%s\"\n", info->name);
28
    printf("\tfilename: \"%s\"\n", info->filename);
29
    printf("\targs: \"%s\"\n", info->args);
30
@@ -641,6 +649,8 @@
31
    struct pw_node_info *info = pd->info;
32
 
33
    info_global(pd);
34
+   if (info == NULL)
35
+       return;
36
    printf("%c\tinput ports: %u/%u\n", MARK_CHANGE(PW_NODE_CHANGE_MASK_INPUT_PORTS),
37
            info->n_input_ports, info->max_input_ports);
38
    printf("%c\toutput ports: %u/%u\n", MARK_CHANGE(PW_NODE_CHANGE_MASK_OUTPUT_PORTS),
39
@@ -661,6 +671,8 @@
40
    struct pw_port_info *info = pd->info;
41
 
42
    info_global(pd);
43
+   if (info == NULL)
44
+       return;
45
    printf("\tdirection: \"%s\"\n", pw_direction_as_string(info->direction));
46
    print_properties(info->props, MARK_CHANGE(PW_PORT_CHANGE_MASK_PROPS), true);
47
    print_params(info->params, info->n_params, MARK_CHANGE(PW_PORT_CHANGE_MASK_PARAMS), true);
48
@@ -672,6 +684,8 @@
49
    struct pw_factory_info *info = pd->info;
50
 
51
    info_global(pd);
52
+   if (info == NULL)
53
+       return;
54
    printf("\tname: \"%s\"\n", info->name);
55
    printf("\tobject-type: %s/%d\n", info->type, info->version);
56
    print_properties(info->props, MARK_CHANGE(PW_FACTORY_CHANGE_MASK_PROPS), true);
57
@@ -683,6 +697,8 @@
58
    struct pw_client_info *info = pd->info;
59
 
60
    info_global(pd);
61
+   if (info == NULL)
62
+       return;
63
    print_properties(info->props, MARK_CHANGE(PW_CLIENT_CHANGE_MASK_PROPS), true);
64
    info->change_mask = 0;
65
 }
66
@@ -692,6 +708,8 @@
67
    struct pw_link_info *info = pd->info;
68
 
69
    info_global(pd);
70
+   if (info == NULL)
71
+       return;
72
    printf("\toutput-node-id: %u\n", info->output_node_id);
73
    printf("\toutput-port-id: %u\n", info->output_port_id);
74
    printf("\tinput-node-id: %u\n", info->input_node_id);
75
@@ -717,6 +735,8 @@
76
    struct pw_device_info *info = pd->info;
77
 
78
    info_global(pd);
79
+   if (info == NULL)
80
+       return;
81
    print_properties(info->props, MARK_CHANGE(PW_DEVICE_CHANGE_MASK_PROPS), true);
82
    print_params(info->params, info->n_params, MARK_CHANGE(PW_DEVICE_CHANGE_MASK_PARAMS), true);
83
    info->change_mask = 0;
84
@@ -727,6 +747,8 @@
85
    struct pw_session_info *info = pd->info;
86
 
87
    info_global(pd);
88
+   if (info == NULL)
89
+       return;
90
    print_properties(info->props, MARK_CHANGE(0), true);
91
    print_params(info->params, info->n_params, MARK_CHANGE(1), true);
92
    info->change_mask = 0;
93
@@ -738,6 +760,8 @@
94
    const char *direction;
95
 
96
    info_global(pd);
97
+   if (info == NULL)
98
+       return;
99
    printf("\tname: %s\n", info->name);
100
    printf("\tmedia-class: %s\n",  info->media_class);
101
    switch(info->direction) {
102
@@ -765,6 +789,8 @@
103
    struct pw_endpoint_stream_info *info = pd->info;
104
 
105
    info_global(pd);
106
+   if (info == NULL)
107
+       return;
108
    printf("\tid: %u\n", info->id);
109
    printf("\tendpoint-id: %u\n", info->endpoint_id);
110
    printf("\tname: %s\n", info->name);
111
@@ -987,25 +1013,25 @@
112
    struct remote_data *rd = pd->rd;
113
    struct pw_session_info *info = pd->info;
114
 
115
-   if (!info) {
116
+   if (info == NULL)
117
        info = pd->info = calloc(1, sizeof(*info));
118
+   if (info != NULL) {
119
        info->id = update->id;
120
+       if (update->change_mask & PW_ENDPOINT_CHANGE_MASK_PARAMS) {
121
+           info->n_params = update->n_params;
122
+           free(info->params);
123
+           info->params = malloc(info->n_params * sizeof(struct spa_param_info));
124
+           memcpy(info->params, update->params,
125
+               info->n_params * sizeof(struct spa_param_info));
126
+       }
127
+       if (update->change_mask & PW_ENDPOINT_CHANGE_MASK_PROPS) {
128
+           pw_properties_free ((struct pw_properties *)info->props);
129
+           info->props =
130
+               (struct spa_dict *) pw_properties_new_dict (update->props);
131
+       }
132
    }
133
-   if (update->change_mask & PW_ENDPOINT_CHANGE_MASK_PARAMS) {
134
-       info->n_params = update->n_params;
135
-       free(info->params);
136
-       info->params = malloc(info->n_params * sizeof(struct spa_param_info));
137
-       memcpy(info->params, update->params,
138
-           info->n_params * sizeof(struct spa_param_info));
139
-   }
140
-   if (update->change_mask & PW_ENDPOINT_CHANGE_MASK_PROPS) {
141
-       pw_properties_free ((struct pw_properties *)info->props);
142
-       info->props =
143
-           (struct spa_dict *) pw_properties_new_dict (update->props);
144
-   }
145
-
146
    if (pd->global == NULL)
147
-       pd->global = pw_map_lookup(&rd->globals, info->id);
148
+       pd->global = pw_map_lookup(&rd->globals, update->id);
149
    if (pd->global && pd->global->info_pending) {
150
        info_session(pd);
151
        pd->global->info_pending = false;
152
@@ -1034,33 +1060,34 @@
153
    struct remote_data *rd = pd->rd;
154
    struct pw_endpoint_info *info = pd->info;
155
 
156
-   if (!info) {
157
+   if (info == NULL)
158
        info = pd->info = calloc(1, sizeof(*info));
159
+   if (info != NULL) {
160
        info->id = update->id;
161
        info->name = update->name ? strdup(update->name) : NULL;
162
        info->media_class = update->media_class ? strdup(update->media_class) : NULL;
163
        info->direction = update->direction;
164
        info->flags = update->flags;
165
-   }
166
-   if (update->change_mask & PW_ENDPOINT_CHANGE_MASK_STREAMS)
167
-       info->n_streams = update->n_streams;
168
-   if (update->change_mask & PW_ENDPOINT_CHANGE_MASK_SESSION)
169
-       info->session_id = update->session_id;
170
-   if (update->change_mask & PW_ENDPOINT_CHANGE_MASK_PARAMS) {
171
-       info->n_params = update->n_params;
172
-       free(info->params);
173
-       info->params = malloc(info->n_params * sizeof(struct spa_param_info));
174
-       memcpy(info->params, update->params,
175
-           info->n_params * sizeof(struct spa_param_info));
176
-   }
177
-   if (update->change_mask & PW_ENDPOINT_CHANGE_MASK_PROPS) {
178
-       pw_properties_free ((struct pw_properties *)info->props);
179
-       info->props =
180
-           (struct spa_dict *) pw_properties_new_dict (update->props);
181
-   }
182
 
183
+       if (update->change_mask & PW_ENDPOINT_CHANGE_MASK_STREAMS)
184
+           info->n_streams = update->n_streams;
185
+       if (update->change_mask & PW_ENDPOINT_CHANGE_MASK_SESSION)
186
+           info->session_id = update->session_id;
187
+       if (update->change_mask & PW_ENDPOINT_CHANGE_MASK_PARAMS) {
188
+           info->n_params = update->n_params;
189
+           free(info->params);
190
+           info->params = malloc(info->n_params * sizeof(struct spa_param_info));
191
+           memcpy(info->params, update->params,
192
+               info->n_params * sizeof(struct spa_param_info));
193
+       }
194
+       if (update->change_mask & PW_ENDPOINT_CHANGE_MASK_PROPS) {
195
+           pw_properties_free ((struct pw_properties *)info->props);
196
+           info->props =
197
+               (struct spa_dict *) pw_properties_new_dict (update->props);
198
+       }
199
+   }
200
    if (pd->global == NULL)
201
-       pd->global = pw_map_lookup(&rd->globals, info->id);
202
+       pd->global = pw_map_lookup(&rd->globals, update->id);
203
    if (pd->global && pd->global->info_pending) {
204
        info_endpoint(pd);
205
        pd->global->info_pending = false;
206
@@ -1088,27 +1115,28 @@
207
    struct remote_data *rd = pd->rd;
208
    struct pw_endpoint_stream_info *info = pd->info;
209
 
210
-   if (!info) {
211
+   if (info == NULL)
212
        info = pd->info = calloc(1, sizeof(*info));
213
+   if (info != NULL) {
214
        info->id = update->id;
215
        info->endpoint_id = update->endpoint_id;
216
        info->name = update->name ? strdup(update->name) : NULL;
217
-   }
218
-   if (update->change_mask & PW_ENDPOINT_STREAM_CHANGE_MASK_PARAMS) {
219
-       info->n_params = update->n_params;
220
-       free(info->params);
221
-       info->params = malloc(info->n_params * sizeof(struct spa_param_info));
222
-       memcpy(info->params, update->params,
223
-           info->n_params * sizeof(struct spa_param_info));
224
-   }
225
-   if (update->change_mask & PW_ENDPOINT_STREAM_CHANGE_MASK_PROPS) {
226
-       pw_properties_free ((struct pw_properties *)info->props);
227
-       info->props =
228
-           (struct spa_dict *) pw_properties_new_dict (update->props);
229
-   }
230
 
231
+       if (update->change_mask & PW_ENDPOINT_STREAM_CHANGE_MASK_PARAMS) {
232
+           info->n_params = update->n_params;
233
+           free(info->params);
234
+           info->params = malloc(info->n_params * sizeof(struct spa_param_info));
235
+           memcpy(info->params, update->params,
236
+               info->n_params * sizeof(struct spa_param_info));
237
+       }
238
+       if (update->change_mask & PW_ENDPOINT_STREAM_CHANGE_MASK_PROPS) {
239
+           pw_properties_free ((struct pw_properties *)info->props);
240
+           info->props =
241
+               (struct spa_dict *) pw_properties_new_dict (update->props);
242
+       }
243
+   }
244
    if (pd->global == NULL)
245
-       pd->global = pw_map_lookup(&rd->globals, info->id);
246
+       pd->global = pw_map_lookup(&rd->globals, update->id);
247
    if (pd->global && pd->global->info_pending) {
248
        info_endpoint_stream(pd);
249
        pd->global->info_pending = false;
250
pipewire-0.3.80.tar.gz/src/tools/pw-link.c -> pipewire-0.3.81.tar.gz/src/tools/pw-link.c Changed
12
 
1
@@ -197,7 +197,9 @@
2
 static void print_port(struct data *data, const char *prefix, struct object *n,
3
        struct object *p, bool verbose)
4
 {
5
-   char buffer1024, id64 = "", *prefix2 = "";
6
+   char buffer1024, id64 = "";
7
+   const char *prefix2 = "";
8
+
9
    if (data->opt_id) {
10
        snprintf(id, sizeof(id), "%4d ", p->id);
11
        prefix2 = "     ";
12
pipewire-0.3.80.tar.gz/test/pwtest.c -> pipewire-0.3.81.tar.gz/test/pwtest.c Changed
10
 
1
@@ -1207,7 +1207,7 @@
2
    time_t t = time(NULL);
3
    struct tm *tm = localtime(&t);
4
    char *dir;
5
-   char *tmpdir = getenv("TMPDIR");
6
+   const char *tmpdir = getenv("TMPDIR");
7
    char pathPATH_MAX;
8
    FILE *fp;
9
 
10
pipewire-0.3.80.tar.gz/test/test-logger.c -> pipewire-0.3.81.tar.gz/test/test-logger.c Changed
19
 
1
@@ -232,7 +232,7 @@
2
    enum spa_log_level level = pwtest_get_iteration(current_test);
3
    enum spa_log_level default_level = pw_log_level;
4
    struct spa_log *default_logger = pw_log_get();
5
-   char *lvl = NULL;
6
+   const char *lvl = NULL;
7
    char *oldenv = getenv("PIPEWIRE_DEBUG");
8
 
9
    if (oldenv)
10
@@ -276,7 +276,7 @@
11
    struct spa_log *default_logger = pw_log_get();
12
    char *oldenv = getenv("PIPEWIRE_DEBUG");
13
    char lvlstr32;
14
-   char *lvl = "X";
15
+   const char *lvl = "X";
16
 
17
    if (oldenv)
18
        oldenv = strdup(oldenv);
19
pipewire-0.3.80.tar.gz/test/test-spa-json.c -> pipewire-0.3.81.tar.gz/test/test-spa-json.c Changed
29
 
1
@@ -166,7 +166,7 @@
2
    return PWTEST_PASS;
3
 }
4
 
5
-static void test_array(char *str, char **vals)
6
+static void test_array(const char *str, const char * const vals)
7
 {
8
    struct spa_json it2;
9
    char val256;
10
@@ -183,12 +183,12 @@
11
 
12
 PWTEST(json_array)
13
 {
14
-   test_array("FL,FR", (char *){ "FL", "FR", NULL });
15
-   test_array(" FL , FR ", (char *){ "FL", "FR", NULL });
16
-   test_array(" FL , FR ", (char *){ "FL", "FR", NULL });
17
-   test_array("FL FR", (char *){ "FL", "FR", NULL });
18
-   test_array("FL FR", (char *){ "FL", "FR", NULL });
19
-   test_array(" FL FR ", (char *){ "FL", "FR", NULL });
20
+   test_array("FL,FR", (const char *){ "FL", "FR", NULL });
21
+   test_array(" FL , FR ", (const char *){ "FL", "FR", NULL });
22
+   test_array(" FL , FR ", (const char *){ "FL", "FR", NULL });
23
+   test_array("FL FR", (const char *){ "FL", "FR", NULL });
24
+   test_array("FL FR", (const char *){ "FL", "FR", NULL });
25
+   test_array(" FL FR ", (const char *){ "FL", "FR", NULL });
26
 
27
    return PWTEST_PASS;
28
 }
29
Refresh

No build results available

Refresh

No rpmlint results available

Request History
Bjørn Lie's avatar

zaitor created request over 1 year ago

New upstream rel


Bjørn Lie's avatar

zaitor accepted request over 1 year ago

Xin