Overview

Request 5782 (accepted)

New upstream release

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

pipewire-aptx.changes Changed
x
 
1
@@ -1,4 +1,9 @@
2
 -------------------------------------------------------------------
3
+Sun May 14 10:53:41 UTC 2023 - Bjørn Lie <zaitor@opensuse.org>
4
+
5
+- Update to version 0.3.70
6
+
7
+-------------------------------------------------------------------
8
 Fri Apr 14 13:35:34 UTC 2023 - Bjørn Lie <zaitor@opensuse.org>
9
 
10
 - Update to version 0.3.69
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.69
6
+Version:        0.3.70
7
 Release:        0
8
 Summary:        PipeWire Bluetooth aptX codec plugin
9
 License:        MIT
10
pipewire-0.3.69.tar.gz/.gitlab-ci.yml -> pipewire-0.3.70.tar.gz/.gitlab-ci.yml Changed
111
 
1
@@ -25,8 +25,8 @@
2
 .fedora:
3
   variables:
4
     # Update this tag when you want to trigger a rebuild
5
-    FDO_DISTRIBUTION_TAG: '2023-03-09.0'
6
-    FDO_DISTRIBUTION_VERSION: '35'
7
+    FDO_DISTRIBUTION_TAG: '2023-04-18.0'
8
+    FDO_DISTRIBUTION_VERSION: '37'
9
     FDO_DISTRIBUTION_PACKAGES: >-
10
       alsa-lib-devel
11
       avahi-devel
12
@@ -50,15 +50,17 @@
13
       libmysofa-devel
14
       libsndfile-devel
15
       libubsan
16
-      libusb-devel
17
+      libusb1-devel
18
       lilv-devel
19
       libv4l-devel
20
       libva-devel
21
       libX11-devel
22
       ModemManager-devel
23
+      meson
24
       openssl-devel
25
       pulseaudio-libs-devel
26
       python3-docutils
27
+      python3-pip
28
       sbc-devel
29
       ShellCheck
30
       SDL2-devel
31
@@ -69,18 +71,25 @@
32
       valgrind
33
       ninja-build
34
       pkgconf
35
-      python3-pip
36
       pulseaudio-utils
37
       openal-soft
38
       readline-devel
39
-    FDO_DISTRIBUTION_EXEC: >-
40
-      pip3 install meson
41
+# Uncommenting the following two lines and disabling the meson entry above
42
+# will re-enable use of Meson via pip but please consider using a newer distro
43
+# image first or making the build system compatible instead! This is because
44
+# using pip or another 3rd party repo defeats the point testing the particular
45
+# distro for regressions.
46
+# NOTE: If you do end up using pip3 for meson, be sure to also update the
47
+# build_meson_prerelease and build_meson_exact_release build instructions
48
+# to uninstall the pip3 version again and probably to not call dnf remove
49
+#    FDO_DISTRIBUTION_EXEC: >-
50
+#      pip3 install meson
51
 
52
 .ubuntu:
53
   variables:
54
     # Update this tag when you want to trigger a rebuild
55
-    FDO_DISTRIBUTION_TAG: '2023-02-16.0'
56
-    FDO_DISTRIBUTION_VERSION: '20.04'
57
+    FDO_DISTRIBUTION_TAG: '2023-04-18.0'
58
+    FDO_DISTRIBUTION_VERSION: '22.04'
59
     FDO_DISTRIBUTION_PACKAGES: >-
60
       debhelper-compat
61
       findutils
62
@@ -100,19 +109,25 @@
63
       libva-dev
64
       libv4l-dev
65
       libx11-dev
66
+      meson
67
       ninja-build
68
       pkg-config
69
       python3-docutils
70
       systemd
71
-      python3-pip
72
-    FDO_DISTRIBUTION_EXEC: >-
73
-      pip3 install meson
74
+# Uncommenting the following three lines and disabling the meson entry above
75
+# will re-enable use of Meson via pip but please consider using a newer distro
76
+# image first or making the build system compatible instead! This is because
77
+# using pip or another 3rd party repo defeats the point testing the particular
78
+# distro for regressions.
79
+#      python3-pip
80
+#    FDO_DISTRIBUTION_EXEC: >-
81
+#      pip3 install meson
82
 
83
 .alpine:
84
   variables:
85
     # Update this tag when you want to trigger a rebuild
86
-    FDO_DISTRIBUTION_TAG: '2022-09-07.0'
87
-    FDO_DISTRIBUTION_VERSION: '3.15'
88
+    FDO_DISTRIBUTION_TAG: '2023-04-17.0'
89
+    FDO_DISTRIBUTION_VERSION: '3.17'
90
     FDO_DISTRIBUTION_PACKAGES: >-
91
       alsa-lib-dev
92
       avahi-dev
93
@@ -358,6 +373,7 @@
94
   extends:
95
     - .build_on_fedora
96
   script:
97
+    - dnf remove --assumeyes meson
98
     - pip3 install --upgrade --pre meson
99
     - echo "Building with meson options $MESON_OPTIONS"
100
     - meson setup "$BUILD_DIR" --prefix="$PREFIX" $MESON_OPTIONS
101
@@ -374,7 +390,8 @@
102
     - meson_version=$(head -n 5 meson.build | grep 'meson_version' | sed -e 's/.*\(0-9\+\.0-9\+\.0-9\+\).*/\1/')
103
     - echo "Requiring meson version $meson_version"
104
     - test -n "$meson_version" || (echo "Meson version parser failed" && exit 1)
105
-    - pip3 uninstall --yes meson
106
+    - dnf remove --assumeyes meson
107
+#    - pip3 uninstall --yes meson
108
     - pip3 install "meson==$meson_version"
109
     - echo "Building with meson options $MESON_OPTIONS"
110
     - meson setup "$BUILD_DIR" --prefix="$PREFIX" $MESON_OPTIONS
111
pipewire-0.3.69.tar.gz/NEWS -> pipewire-0.3.70.tar.gz/NEWS Changed
82
 
1
@@ -1,3 +1,70 @@
2
+# PipeWire 0.3.70 (2023-04-20)
3
+
4
+This is a quick bugfix release that is API and ABI compatible with previous
5
+0.3.x releases.
6
+
7
+## Highlights
8
+  - Fix a regression in the scheduler that could keep some nodes IDLE.
9
+  - Fix a regression in the biquad filters in filter-chain.
10
+  - Fix a regression and potential crash in the ALSA mixer probing.
11
+  - Fix a regression in pipewiresrc with timestamps that could cause cheese
12
+    to record video with wrong timestamps.
13
+  - Beamforming support was enabled in the echo-canceler.
14
+  - pulse-tunnel and raop-sink will now proxy local volume changes to the
15
+    remote end.
16
+  - More bugfixes and improvements.
17
+
18
+
19
+## PipeWire
20
+  - Fix a bug in the graph scheduler where some nodes might stay IDLE in
21
+    some cases (like when connecting the source of the echo-canceler to the
22
+    sink).
23
+  - pw-metadata can now be created from the factory with initial values for
24
+    the metadata. (#3076)
25
+  - Conditions were added to the pipewire config file to make it possible to
26
+    configure the access module and the exec sections.
27
+  - Support was added in pw-stream to intercept and override properties for
28
+    the adapter. This can be used to implement custom volume control, for
29
+    example.
30
+
31
+## Tools
32
+  - pw-metadata can now list all available metadata objects with the -l
33
+    option.
34
+  - A new pw-config tool was added to debug configuration file loading and
35
+    parsing.
36
+
37
+## Modules
38
+  - The webrtc echo canceler now supports beamforming. You can provide the
39
+    coordinates of the microphones and let webrtc perform beamforming on
40
+    the captured samples to improve quality and remove noise.
41
+  - Fix a regression in the filter-chain with biquad filters. (#3161) and
42
+    improve error reporting.
43
+  - The pulse-tunnel will now proxy the volume changes to the remote end.
44
+  - The RAOP sink will now send volume parameters to control the volume
45
+    remotely. (#2061)
46
+
47
+## SPA
48
+  - One ALSA commit was not correctly reverted and might cause crashes.
49
+  - The ALSA sink and source now calculate the ALSA ringbuffer memory
50
+    location more correctly wich might improve compatibility with some
51
+    hardware.
52
+  - v4l2 now sets the values of the controls in the Props param.
53
+
54
+## Pulse-server
55
+  - The echo-canceler aec_args are now parsed like they would be under
56
+    pulseaudio.
57
+
58
+## Bluetooth
59
+  - More work on synchronizing BAP devices.
60
+
61
+## GStreamer
62
+  - The GStreamer source can now renegotiate the format when it changes.
63
+  - The GStreamer source now uses the BaseSrc clocking code to implement
64
+    the clock and timing code.
65
+
66
+
67
+Older versions:
68
+
69
 # PipeWire 0.3.69 (2023-04-13)
70
 
71
 This is a quick bugfix release that is API and ABI compatible with previous
72
@@ -51,9 +118,6 @@
73
 ## GStreamer
74
   - Fix the GStreamer clock to make cheese video recording work again. (#3149)
75
 
76
-
77
-Older versions:
78
-
79
 # PipeWire 0.3.68 (2023-04-06)
80
 
81
 This is a bugfix release that is API and ABI compatible with previous
82
pipewire-0.3.69.tar.gz/doc/pipewire-daemon.dox -> pipewire-0.3.70.tar.gz/doc/pipewire-daemon.dox Changed
12
 
1
@@ -64,8 +64,8 @@
2
 
3
 The first configuration file found is loaded as the base configuration.
4
 
5
-Next, configuration sections are collected in the directories in this
6
-order:
7
+Next, configuration sections (from files ending with a .conf extension) are collected
8
+in the directories in this order:
9
 
10
 - `$datadir/pipewire/pipewire.conf.d/` (usually `/usr/share/pipewire/pipewire.conf.d/`)
11
 - `$sysconfdir/pipewire/pipewire.conf.d/` (usually `/etc/pipewire/pipewire.conf.d/`)
12
pipewire-0.3.69.tar.gz/man/meson.build -> pipewire-0.3.70.tar.gz/man/meson.build Changed
9
 
1
@@ -12,6 +12,7 @@
2
   'pipewire.conf.5.rst.in',
3
   'pw-cat.1.rst.in',
4
   'pw-cli.1.rst.in',
5
+  'pw-config.1.rst.in',
6
   'pw-dot.1.rst.in',
7
   'pw-link.1.rst.in',
8
   'pw-metadata.1.rst.in',
9
pipewire-0.3.70.tar.gz/man/pw-config.1.rst.in Added
112
 
1
@@ -0,0 +1,110 @@
2
+pw-config
3
+#########
4
+
5
+-----------------------------
6
+Debug PipeWire Config parsing
7
+-----------------------------
8
+
9
+:Manual section: 1
10
+:Manual group: General Commands Manual
11
+
12
+SYNOPSIS
13
+========
14
+
15
+| **pw-config** *options* paths
16
+
17
+| **pw-config** *options* list *SECTION*
18
+
19
+| **pw-config** *options* merge *SECTION*
20
+
21
+DESCRIPTION
22
+===========
23
+
24
+List config paths and config sections and display the parsed
25
+output.
26
+
27
+This tool can be used to get an overview of the config file that will be
28
+parsed by the PipeWire server and clients.
29
+
30
+COMMON OPTIONS
31
+==============
32
+
33
+-h | --help
34
+  Show help.
35
+
36
+--version
37
+  Show version information.
38
+
39
+-n | --name=NAME
40
+  Config Name (default 'pipewire.conf')
41
+
42
+-p | --prefix=PREFIX
43
+  Config Prefix (default '')
44
+
45
+-L | --no-newline
46
+  Omit newlines after values
47
+
48
+-r | --recurse
49
+  Reformat config sections recursively
50
+
51
+-N | --no-colors
52
+  Disable color output
53
+
54
+-C | --color=WHEN
55
+  whether to enable color support. WHEN is `never`, `always`, or `auto`
56
+
57
+LISTING PATHS
58
+=============
59
+
60
+Specify the paths command. It will display all the config files that will
61
+be parsed and in what order.
62
+
63
+LISTING CONFIG SECTIONS
64
+=======================
65
+
66
+Specify the list command with an optional *SECTION* to list the configuration
67
+fragments used for *SECTION*. Without a *SECTION*, all sections will be
68
+listed.
69
+
70
+Use the -r options to reformat the sections.
71
+
72
+MERGING A CONFIG SECTION
73
+========================
74
+
75
+With the merge option and a *SECTION*, pw-config will merge all config files into
76
+a merged config section and dump the results. This will be the section used by
77
+the client or server.
78
+
79
+Use the -r options to reformat the sections.
80
+
81
+EXAMPLES
82
+========
83
+
84
+**pw-config**
85
+  List all config files that will be used
86
+
87
+**pw-config** -n pipewire-pulse.conf
88
+  List all config files that will be used by the PipeWire pulseaudio server.
89
+
90
+**pw-config** -n pipewire-pulse.conf list
91
+  List all config sections used by the PipeWire pulseaudio server
92
+
93
+**pw-config** -n jack.conf list context.properties
94
+  List the context.properties fragments used by the JACK clients
95
+
96
+**pw-config** -n jack.conf merge context.properties
97
+  List the merged context.properties used by the JACK clients
98
+
99
+**pw-config** -n pipewire.conf -r merge context.modules
100
+  List the merged context.modules used by the PipeWire server and reformat
101
+
102
+AUTHORS
103
+=======
104
+
105
+The PipeWire Developers <@PACKAGE_BUGREPORT@>; PipeWire is available from @PACKAGE_URL@
106
+
107
+SEE ALSO
108
+========
109
+
110
+``pipewire(1)``,
111
+``pw-dump(1)``,
112
pipewire-0.3.69.tar.gz/man/pw-metadata.1.rst.in -> pipewire-0.3.70.tar.gz/man/pw-metadata.1.rst.in Changed
11
 
1
@@ -44,6 +44,9 @@
2
 --version
3
   Show version information.
4
 
5
+-l | --list
6
+  List available metadata objects
7
+
8
 -m | --monitor
9
   Keeps running and log the changes to the metadata.
10
 
11
pipewire-0.3.69.tar.gz/meson.build -> pipewire-0.3.70.tar.gz/meson.build Changed
8
 
1
@@ -1,5 +1,5 @@
2
 project('pipewire', 'c' ,
3
-  version : '0.3.69',
4
+  version : '0.3.70',
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
pipewire-0.3.69.tar.gz/spa/examples/adapter-control.c -> pipewire-0.3.70.tar.gz/spa/examples/adapter-control.c Changed
11
 
1
@@ -964,8 +964,7 @@
2
 int main(int argc, char *argv)
3
 {
4
    struct data data = { 0 };
5
-   int res = 0;
6
-   char c;
7
+   int res = 0, c;
8
 
9
    /* default values*/
10
    data.volume_ramp_samples = DEFAULT_RAMP_SAMPLES;
11
pipewire-0.3.69.tar.gz/spa/plugins/aec/aec-webrtc.cpp -> pipewire-0.3.70.tar.gz/spa/plugins/aec/aec-webrtc.cpp Changed
117
 
1
@@ -10,6 +10,7 @@
2
 #include <spa/support/log.h>
3
 #include <spa/utils/string.h>
4
 #include <spa/utils/names.h>
5
+#include <spa/utils/json.h>
6
 #include <spa/support/plugin.h>
7
 
8
 #include <webrtc/modules/audio_processing/include/audio_processing.h>
9
@@ -40,6 +41,53 @@
10
    return default_value;
11
 }
12
 
13
+
14
+/*  f0 f1 f2  */
15
+static int parse_point(struct spa_json *it, float (&f)3)
16
+{
17
+   struct spa_json arr;
18
+   int i, res;
19
+
20
+   if (spa_json_enter_array(it, &arr) <= 0)
21
+       return -EINVAL;
22
+
23
+   for (i = 0; i < 3; i++) {
24
+       if ((res = spa_json_get_float(&arr, &fi)) <= 0)
25
+           return -EINVAL;
26
+   }
27
+   return 0;
28
+}
29
+
30
+/*  point1 point2 ...  */
31
+static int parse_mic_geometry(struct impl_data *impl, const char *mic_geometry,
32
+       std::vector<webrtc::Point>& geometry)
33
+{
34
+   int res;
35
+   size_t i;
36
+   struct spa_json it2;
37
+
38
+   spa_json_init(&it0, mic_geometry, strlen(mic_geometry));
39
+   if (spa_json_enter_array(&it0, &it1) <= 0) {
40
+       spa_log_error(impl->log, "Error: webrtc.mic-geometry expects an array");
41
+       return -EINVAL;
42
+   }
43
+
44
+   for (i = 0; i < geometry.size(); i++) {
45
+       float f3;
46
+
47
+       if ((res = parse_point(&it1, f)) < 0) {
48
+           spa_log_error(impl->log, "Error: can't parse webrtc.mic-geometry points: %d", res);
49
+           return res;
50
+       }
51
+
52
+       spa_log_info(impl->log, "mic %zd position: (%g %g %g)", i, f0, f1, f2);
53
+       geometryi.c0 = f0;
54
+       geometryi.c1 = f1;
55
+       geometryi.c2 = f2;
56
+   }
57
+   return 0;
58
+}
59
+
60
 static int webrtc_init2(void *object, const struct spa_dict *args,
61
        struct spa_audio_info_raw *rec_info, struct spa_audio_info_raw *out_info,
62
        struct spa_audio_info_raw *play_info)
63
@@ -61,6 +109,8 @@
64
    bool experimental_agc = webrtc_get_spa_bool(args, "webrtc.experimental_agc", false);
65
    bool experimental_ns = webrtc_get_spa_bool(args, "webrtc.experimental_ns", false);
66
 
67
+   bool beamforming = webrtc_get_spa_bool(args, "webrtc.beamforming", false);
68
+
69
    // FIXME: Intelligibility enhancer is not currently supported
70
    // This filter will modify playback buffer (when calling ProcessReverseStream), but now
71
    // playback buffer modifications are discarded.
72
@@ -71,6 +121,44 @@
73
    config.Set<webrtc::ExperimentalAgc>(new webrtc::ExperimentalAgc(experimental_agc));
74
    config.Set<webrtc::ExperimentalNs>(new webrtc::ExperimentalNs(experimental_ns));
75
 
76
+   if (beamforming) {
77
+       std::vector<webrtc::Point> geometry(rec_info->channels);
78
+       const char *mic_geometry, *target_direction;
79
+
80
+       /* The beamformer gives a single mono channel */
81
+       out_info->channels = 1;
82
+       out_info->position0 = SPA_AUDIO_CHANNEL_MONO;
83
+
84
+       if ((mic_geometry = spa_dict_lookup(args, "webrtc.mic-geometry")) == NULL) {
85
+           spa_log_error(impl->log, "Error: webrtc.beamforming requires webrtc.mic-geometry");
86
+           return -EINVAL;
87
+       }
88
+
89
+       if ((res = parse_mic_geometry(impl, mic_geometry, geometry)) < 0)
90
+           return res;
91
+
92
+       if ((target_direction = spa_dict_lookup(args, "webrtc.target-direction")) != NULL) {
93
+           webrtc::SphericalPointf direction(0.0f, 0.0f, 0.0f);
94
+           struct spa_json it;
95
+           float f3;
96
+
97
+           spa_json_init(&it, target_direction, strlen(target_direction));
98
+           if (parse_point(&it, f) < 0) {
99
+               spa_log_error(impl->log, "Error: can't parse target-direction %s",
100
+                       target_direction);
101
+               return -EINVAL;
102
+           }
103
+
104
+           direction.s0 = f0;
105
+           direction.s1 = f1;
106
+           direction.s2 = f2;
107
+
108
+           config.Set<webrtc::Beamforming>(new webrtc::Beamforming(true, geometry, direction));
109
+       } else {
110
+           config.Set<webrtc::Beamforming>(new webrtc::Beamforming(true, geometry));
111
+       }
112
+   }
113
+
114
    webrtc::ProcessingConfig pconfig = {{
115
        webrtc::StreamConfig(rec_info->rate, rec_info->channels, false), /* input stream */
116
        webrtc::StreamConfig(out_info->rate, out_info->channels, false), /* output stream */
117
pipewire-0.3.69.tar.gz/spa/plugins/alsa/acp/alsa-util.c -> pipewire-0.3.70.tar.gz/spa/plugins/alsa/acp/alsa-util.c Changed
26
 
1
@@ -1648,20 +1648,12 @@
2
     } else if (mask & SND_CTL_EVENT_MASK_ADD) {
3
         snd_ctl_elem_iface_t iface = snd_hctl_elem_get_interface(helem);
4
         if (iface == SND_CTL_ELEM_IFACE_CARD || iface == SND_CTL_ELEM_IFACE_PCM) {
5
-            snd_mixer_t *mixer = snd_mixer_class_get_mixer(class);
6
-            snd_ctl_elem_iface_t iface = snd_hctl_elem_get_interface(helem);
7
-            const char *name = snd_hctl_elem_get_name(helem);
8
-            const int index = snd_hctl_elem_get_index(helem);
9
-            const int device = snd_hctl_elem_get_device(helem);
10
             snd_mixer_elem_t *new_melem;
11
 
12
-            new_melem = pa_alsa_mixer_find(mixer, iface, name, index, device);
13
-            if (!new_melem) {
14
-                /* Put the hctl pointer as our private data - it will be useful for callbacks */
15
-                if ((err = snd_mixer_elem_new(&new_melem, SND_MIXER_ELEM_PULSEAUDIO, 0, helem, NULL)) < 0) {
16
-                    pa_log_warn("snd_mixer_elem_new failed: %s", pa_alsa_strerror(err));
17
-                    return 0;
18
-                }
19
+            /* Put the hctl pointer as our private data - it will be useful for callbacks */
20
+            if ((err = snd_mixer_elem_new(&new_melem, SND_MIXER_ELEM_PULSEAUDIO, 0, helem, NULL)) < 0) {
21
+                pa_log_warn("snd_mixer_elem_new failed: %s", pa_alsa_strerror(err));
22
+                return 0;
23
             }
24
 
25
             if ((err = snd_mixer_elem_attach(new_melem, helem)) < 0) {
26
pipewire-0.3.69.tar.gz/spa/plugins/alsa/alsa-pcm.c -> pipewire-0.3.70.tar.gz/spa/plugins/alsa/alsa-pcm.c Changed
24
 
1
@@ -2179,7 +2179,7 @@
2
 
3
        if (SPA_LIKELY(state->use_mmap)) {
4
            for (i = 0; i < b->buf->n_datas; i++) {
5
-               spa_memcpy(SPA_PTROFF(my_areasi.addr, off * frame_size, void),
6
+               spa_memcpy(channel_area_addr(&my_areasi, off),
7
                        SPA_PTROFF(di.data, offs, void), n_bytes);
8
            }
9
        } else {
10
@@ -2290,11 +2290,11 @@
11
 
12
            for (i = 0; i < b->buf->n_datas; i++) {
13
                spa_memcpy(di.data,
14
-                       SPA_PTROFF(my_areasi.addr, offset * frame_size, void),
15
+                       channel_area_addr(&my_areasi, offset),
16
                        l0);
17
                if (SPA_UNLIKELY(l1 > 0))
18
                    spa_memcpy(SPA_PTROFF(di.data, l0, void),
19
-                           my_areasi.addr,
20
+                           channel_area_addr(&my_areasi, 0),
21
                            l1);
22
                di.chunk->offset = 0;
23
                di.chunk->size = n_bytes;
24
pipewire-0.3.69.tar.gz/spa/plugins/alsa/alsa-pcm.h -> pipewire-0.3.70.tar.gz/spa/plugins/alsa/alsa-pcm.h Changed
22
 
1
@@ -13,6 +13,7 @@
2
 #include <math.h>
3
 
4
 #include <alsa/asoundlib.h>
5
+#include <alsa/version.h>
6
 #include <alsa/use-case.h>
7
 
8
 #include <spa/support/plugin.h>
9
@@ -353,6 +354,12 @@
10
    return missed;
11
 }
12
 
13
+/* This function is also as snd_pcm_channel_area_addr() since 1.2.6 which is not yet
14
+ * in ubuntu and I can't figure out how to do the ALSA version check. */
15
+static inline void *channel_area_addr(const snd_pcm_channel_area_t *area, snd_pcm_uframes_t offset)
16
+{
17
+        return (char *)area->addr + (area->first + area->step * offset) / 8;
18
+}
19
 
20
 #ifdef __cplusplus
21
 } /* extern "C" */
22
pipewire-0.3.69.tar.gz/spa/plugins/audioconvert/audioadapter.c -> pipewire-0.3.70.tar.gz/spa/plugins/audioconvert/audioadapter.c Changed
29
 
1
@@ -51,6 +51,7 @@
2
    uint64_t follower_flags;
3
    struct spa_audio_info follower_current_format;
4
    struct spa_audio_info default_format;
5
+   int in_set_param;
6
 
7
    struct spa_handle *hnd_convert;
8
    struct spa_node *convert;
9
@@ -675,13 +676,16 @@
10
    }
11
 
12
    case SPA_PARAM_Props:
13
-       if (this->target != this->follower)
14
-           res = spa_node_set_param(this->target, id, flags, param);
15
-       res2 = spa_node_set_param(this->follower, id, flags, param);
16
+   {
17
+       int in_set_param = ++this->in_set_param;
18
+       res = spa_node_set_param(this->follower, id, flags, param);
19
+       if (this->target != this->follower && this->in_set_param == in_set_param)
20
+           res2 = spa_node_set_param(this->target, id, flags, param);
21
        if (res < 0 && res2 < 0)
22
            return res;
23
        res = 0;
24
        break;
25
+   }
26
    case SPA_PARAM_ProcessLatency:
27
        res = spa_node_set_param(this->follower, id, flags, param);
28
        break;
29
pipewire-0.3.69.tar.gz/spa/plugins/bluez5/bluez5-dbus.c -> pipewire-0.3.70.tar.gz/spa/plugins/bluez5/bluez5-dbus.c Changed
120
 
1
@@ -2986,13 +2986,8 @@
2
            else if (spa_streq(key, "State")) {
3
                enum spa_bt_transport_state state  = spa_bt_transport_state_from_string(value);
4
 
5
-               /* Emit transition to active only for transports with
6
-                * acquired fd. If the acquire completes after prop
7
-                * update, we set the state in acquire completion.  BlueZ
8
-                * currently sends events in the order where this never
9
-                * happens, but let's not rely on that.
10
-                */
11
-               if (state != SPA_BT_TRANSPORT_STATE_ACTIVE || transport->fd >= 0)
12
+               /* Transition to active emitted only from acquire callback. */
13
+               if (state != SPA_BT_TRANSPORT_STATE_ACTIVE)
14
                    spa_bt_transport_set_state(transport, state);
15
            }
16
            else if (spa_streq(key, "Device")) {
17
@@ -3287,7 +3282,6 @@
18
 {
19
    struct spa_bt_monitor *monitor = transport->monitor;
20
    struct spa_bt_transport *t;
21
-   bool sink = (transport->profile & SPA_BT_PROFILE_BAP_SINK) != 0;
22
 
23
    if (!(transport->profile & (SPA_BT_PROFILE_BAP_SINK | SPA_BT_PROFILE_BAP_SOURCE)))
24
        return 0;
25
@@ -3311,21 +3305,28 @@
26
        if (t->iso_io) {
27
            spa_log_debug(monitor->log, "transport %p: attach ISO IO to %p",
28
                    transport, t);
29
-           transport->iso_io = spa_bt_iso_io_attach(t->iso_io, transport->fd, sink);
30
+           transport->iso_io = spa_bt_iso_io_attach(t->iso_io, transport);
31
+           if (transport->iso_io == NULL)
32
+               return -errno;
33
            return 0;
34
        }
35
    }
36
 
37
    spa_log_debug(monitor->log, "transport %p: new ISO IO", transport);
38
-   transport->iso_io = spa_bt_iso_io_create(transport->fd, sink,
39
-           transport->bap_cig, transport->bap_interval,
40
-           monitor->log, monitor->data_loop, monitor->data_system);
41
+   transport->iso_io = spa_bt_iso_io_create(transport, monitor->log, monitor->data_loop, monitor->data_system);
42
    if (transport->iso_io == NULL)
43
        return -errno;
44
 
45
    return 0;
46
 }
47
 
48
+static bool transport_in_same_cig(struct spa_bt_transport *transport, struct spa_bt_transport *other)
49
+{
50
+   return (other->profile & (SPA_BT_PROFILE_BAP_SINK | SPA_BT_PROFILE_BAP_SOURCE)) &&
51
+       other->bap_cig == transport->bap_cig &&
52
+       other->bap_initiator;
53
+}
54
+
55
 static void transport_acquire_reply(DBusPendingCall *pending, void *user_data)
56
 {
57
    struct spa_bt_transport *transport = user_data;
58
@@ -3334,7 +3335,7 @@
59
    int ret = 0;
60
    DBusError err;
61
    DBusMessage *r;
62
-   struct spa_bt_transport *t_linked;
63
+   struct spa_bt_transport *t, *t_linked;
64
 
65
    r = dbus_pending_call_steal_reply(pending);
66
 
67
@@ -3389,7 +3390,8 @@
68
            spa_log_error(monitor->log, "transport %p: transport_create_iso_io failed",
69
                    transport);
70
 
71
-       spa_bt_transport_set_state(transport, SPA_BT_TRANSPORT_STATE_ACTIVE);
72
+       if (!transport->bap_initiator)
73
+           spa_bt_transport_set_state(transport, SPA_BT_TRANSPORT_STATE_ACTIVE);
74
    }
75
 
76
    /* For LE Audio, multiple transport from the same device may share the same
77
@@ -3413,7 +3415,27 @@
78
            spa_log_error(monitor->log, "transport %p: transport_create_iso_io failed",
79
                    t_linked);
80
 
81
-       spa_bt_transport_set_state(t_linked, SPA_BT_TRANSPORT_STATE_ACTIVE);
82
+       if (!transport->bap_initiator)
83
+           spa_bt_transport_set_state(t_linked, SPA_BT_TRANSPORT_STATE_ACTIVE);
84
+   }
85
+
86
+   /*
87
+    * Transports in same CIG emit state change events at the same time,
88
+    * after all pending acquires complete.
89
+    */
90
+   if (transport->bap_initiator) {
91
+       spa_list_for_each(t, &monitor->transport_list, link) {
92
+           if (!transport_in_same_cig(transport, t))
93
+               continue;
94
+           if (t->acquire_call)
95
+               return;
96
+       }
97
+       spa_list_for_each(t, &monitor->transport_list, link) {
98
+           if (!transport_in_same_cig(transport, t))
99
+               continue;
100
+           if (t->fd >= 0)
101
+               spa_bt_transport_set_state(t, SPA_BT_TRANSPORT_STATE_ACTIVE);
102
+       }
103
    }
104
 }
105
 
106
@@ -3462,13 +3484,6 @@
107
    return 0;
108
 }
109
 
110
-static bool transport_in_same_cig(struct spa_bt_transport *transport, struct spa_bt_transport *other)
111
-{
112
-   return (other->profile & (SPA_BT_PROFILE_BAP_SINK | SPA_BT_PROFILE_BAP_SOURCE)) &&
113
-       other->bap_cig == transport->bap_cig &&
114
-       other->bap_initiator;
115
-}
116
-
117
 static bool another_cig_transport_active(struct spa_bt_transport *transport)
118
 {
119
    struct spa_bt_monitor *monitor = transport->monitor;
120
pipewire-0.3.69.tar.gz/spa/plugins/bluez5/dbus-monitor.c -> pipewire-0.3.70.tar.gz/spa/plugins/bluez5/dbus-monitor.c Changed
10
 
1
@@ -75,7 +75,7 @@
2
            monitor, g_dbus_object_get_object_path(object), name ? name : "<null>");
3
 
4
    if (g_object_get_data(G_OBJECT(iface), "dbus-monitor-signals-connected")) {
5
-       g_object_disconnect(G_OBJECT(iface), "g-properties-changed",
6
+       g_object_disconnect(G_OBJECT(iface), "any_signal",
7
                G_CALLBACK(on_g_properties_changed),
8
                monitor, NULL);
9
        g_object_set_data(G_OBJECT(iface), "dbus-monitor-signals-connected", NULL);
10
pipewire-0.3.69.tar.gz/spa/plugins/bluez5/iso-io.c -> pipewire-0.3.70.tar.gz/spa/plugins/bluez5/iso-io.c Changed
331
 
1
@@ -19,11 +19,15 @@
2
 #include "config.h"
3
 #include "iso-io.h"
4
 
5
+#include "media-codecs.h"
6
+#include "defs.h"
7
+
8
 static struct spa_log_topic log_topic = SPA_LOG_TOPIC(0, "spa.bluez5.iso");
9
 #undef SPA_LOG_TOPIC_DEFAULT
10
 #define SPA_LOG_TOPIC_DEFAULT &log_topic
11
 
12
-#define IDLE_TIME  (200 * SPA_NSEC_PER_MSEC)
13
+#define IDLE_TIME  (500 * SPA_NSEC_PER_MSEC)
14
+#define EMPTY_BUF_SIZE 65536
15
 
16
 struct group {
17
    struct spa_log *log;
18
@@ -37,6 +41,7 @@
19
    uint64_t next;
20
    uint64_t duration;
21
    uint32_t paused;
22
+   bool started;
23
 };
24
 
25
 struct stream {
26
@@ -48,6 +53,9 @@
27
    bool idle;
28
 
29
    spa_bt_iso_io_pull_t pull;
30
+
31
+   const struct media_codec *codec;
32
+   uint32_t block_size;
33
 };
34
 
35
 struct modify_info
36
@@ -86,6 +94,33 @@
37
    spa_assert_se(res == 0);
38
 }
39
 
40
+static int stream_silence(struct stream *stream)
41
+{
42
+   static uint8_t emptyEMPTY_BUF_SIZE = {0};
43
+   const size_t max_size = sizeof(stream->this.buf);
44
+   int res, used, need_flush;
45
+   size_t encoded;
46
+
47
+   stream->idle = true;
48
+
49
+   res = used = stream->codec->start_encode(stream->this.codec_data, stream->this.buf, max_size, 0, 0);
50
+   if (res < 0)
51
+       return res;
52
+
53
+   res = stream->codec->encode(stream->this.codec_data, empty, stream->block_size,
54
+           SPA_PTROFF(stream->this.buf, used, void), max_size - used, &encoded, &need_flush);
55
+   if (res < 0)
56
+       return res;
57
+
58
+   used += encoded;
59
+
60
+   if (!need_flush)
61
+       return -EINVAL;
62
+
63
+   stream->this.size = used;
64
+   return 0;
65
+}
66
+
67
 static int set_timeout(struct group *group, uint64_t time)
68
 {
69
    struct itimerspec ts;
70
@@ -112,9 +147,10 @@
71
 {
72
    struct group *group = source->data;
73
    struct stream *stream;
74
+   bool resync = false;
75
+   bool fail = false;
76
    uint64_t exp;
77
    int res;
78
-   bool active = false;
79
 
80
    if ((res = spa_system_timerfd_read(group->data_system, group->timerfd, &exp)) < 0) {
81
        if (res != -EAGAIN)
82
@@ -124,26 +160,20 @@
83
    }
84
 
85
    /*
86
-    * If an idle stream activates when another stream is already active,
87
-    * pause output of all streams for a while to avoid desynchronization.
88
+    * If a stream failed, pause output of all streams for a while to avoid
89
+    * desynchronization.
90
     */
91
    spa_list_for_each(stream, &group->streams, link) {
92
        if (!stream->sink)
93
            continue;
94
-       if (!stream->idle) {
95
-           active = true;
96
-           break;
97
-       }
98
-   }
99
 
100
-   spa_list_for_each(stream, &group->streams, link) {
101
-       if (!stream->sink)
102
-           continue;
103
-
104
-       if (stream->idle && stream->this.size > 0 && active && !group->paused)
105
-           group->paused = 1u + IDLE_TIME / group->duration;
106
+       if (stream->this.need_resync) {
107
+           resync = true;
108
+           stream->this.need_resync = false;
109
+       }
110
 
111
-       stream->idle = (stream->this.size == 0);
112
+       if (!group->started && !stream->idle && stream->this.size > 0)
113
+           group->started = true;
114
    }
115
 
116
    if (group->paused) {
117
@@ -157,23 +187,36 @@
118
 
119
        if (!stream->sink)
120
            continue;
121
-       if (stream->idle || group->paused) {
122
+       if (group->paused || !group->started) {
123
            stream->this.resync = true;
124
            stream->this.size = 0;
125
            continue;
126
        }
127
+       if (stream->this.size == 0) {
128
+           spa_log_debug(group->log, "%p: ISO group:%u miss fd:%d",
129
+                   group, group->cig, stream->fd);
130
+           if (stream_silence(stream) < 0) {
131
+               fail = true;
132
+               continue;
133
+           }
134
+       }
135
 
136
        res = send(stream->fd, stream->this.buf, stream->this.size, MSG_DONTWAIT | MSG_NOSIGNAL);
137
-       if (res < 0)
138
+       if (res < 0) {
139
            res = -errno;
140
+           fail = true;
141
+       }
142
 
143
-       spa_log_trace(group->log, "%p: ISO group:%u sent fd:%d size:%u ts:%u res:%d",
144
+       spa_log_trace(group->log, "%p: ISO group:%u sent fd:%d size:%u ts:%u idle:%d res:%d",
145
                group, group->cig, stream->fd, (unsigned)stream->this.size,
146
-               (unsigned)stream->this.timestamp, res);
147
+               (unsigned)stream->this.timestamp, stream->idle, res);
148
 
149
        stream->this.size = 0;
150
    }
151
 
152
+   if (fail)
153
+       group->paused = 1u + IDLE_TIME / group->duration;
154
+
155
    /* Pull data for the next interval */
156
    group->next += exp * group->duration;
157
 
158
@@ -181,23 +224,27 @@
159
        if (!stream->sink)
160
            continue;
161
 
162
+       if (resync)
163
+           stream->this.resync = true;
164
+
165
        if (stream->pull) {
166
+           stream->idle = false;
167
            stream->this.now = group->next;
168
            stream->pull(&stream->this);
169
        } else {
170
-           stream->this.size = 0;
171
+           stream_silence(stream);
172
        }
173
    }
174
 
175
    set_timeout(group, group->next);
176
 }
177
 
178
-static struct group *group_create(uint8_t cig, uint32_t interval,
179
+static struct group *group_create(struct spa_bt_transport *t,
180
        struct spa_log *log, struct spa_loop *data_loop, struct spa_system *data_system)
181
 {
182
    struct group *group;
183
 
184
-   if (interval <= 5000) {
185
+   if (t->bap_interval <= 5000) {
186
        errno = EINVAL;
187
        return NULL;
188
    }
189
@@ -208,11 +255,11 @@
190
 
191
    spa_log_topic_init(log, &log_topic);
192
 
193
-   group->cig = cig;
194
+   group->cig = t->bap_cig;
195
    group->log = log;
196
    group->data_loop = data_loop;
197
    group->data_system = data_system;
198
-   group->duration = interval * SPA_NSEC_PER_USEC;
199
+   group->duration = t->bap_interval * SPA_NSEC_PER_USEC;
200
 
201
    spa_list_init(&group->streams);
202
 
203
@@ -261,36 +308,80 @@
204
    free(group);
205
 }
206
 
207
-struct stream *stream_create(int fd, bool sink, struct group *group)
208
+struct stream *stream_create(struct spa_bt_transport *t, struct group *group)
209
 {
210
    struct stream *stream;
211
+   void *codec_data = NULL;
212
+   int block_size = 0;
213
+   struct spa_audio_info format = { 0 };
214
+   int res;
215
+   bool sink = (t->profile & SPA_BT_PROFILE_BAP_SINK) != 0;
216
+
217
+   if (!t->media_codec->bap) {
218
+       res = -EINVAL;
219
+       goto fail;
220
+   }
221
+
222
+   if (sink) {
223
+       res = t->media_codec->validate_config(t->media_codec, 0, t->configuration, t->configuration_len, &format);
224
+       if (res < 0)
225
+           goto fail;
226
+
227
+       codec_data = t->media_codec->init(t->media_codec, 0, t->configuration, t->configuration_len,
228
+               &format, NULL, t->write_mtu);
229
+       if (!codec_data) {
230
+           res = -EINVAL;
231
+           goto fail;
232
+       }
233
+
234
+       block_size = t->media_codec->get_block_size(codec_data);
235
+       if (block_size < 0 || block_size > EMPTY_BUF_SIZE) {
236
+           res = -EINVAL;
237
+           goto fail;
238
+       }
239
+   }
240
 
241
    stream = calloc(1, sizeof(struct stream));
242
    if (stream == NULL)
243
-       return NULL;
244
+       goto fail_errno;
245
 
246
-   stream->fd = fd;
247
+   stream->fd = t->fd;
248
    stream->sink = sink;
249
    stream->group = group;
250
-   stream->idle = true;
251
    stream->this.duration = group->duration;
252
 
253
+   stream->codec = t->media_codec;
254
+   stream->this.codec_data = codec_data;
255
+   stream->this.format = format;
256
+   stream->block_size = block_size;
257
+
258
+   if (sink)
259
+       stream_silence(stream);
260
+
261
    stream_link(group, stream);
262
 
263
    return stream;
264
+
265
+fail_errno:
266
+   res = -errno;
267
+fail:
268
+   if (codec_data)
269
+       t->media_codec->deinit(codec_data);
270
+   errno = -res;
271
+   return NULL;
272
 }
273
 
274
-struct spa_bt_iso_io *spa_bt_iso_io_create(int fd, bool sink, uint8_t cig, uint32_t interval,
275
+struct spa_bt_iso_io *spa_bt_iso_io_create(struct spa_bt_transport *t,
276
        struct spa_log *log, struct spa_loop *data_loop, struct spa_system *data_system)
277
 {
278
    struct stream *stream;
279
    struct group *group;
280
 
281
-   group = group_create(cig, interval, log, data_loop, data_system);
282
+   group = group_create(t, log, data_loop, data_system);
283
    if (group == NULL)
284
        return NULL;
285
 
286
-   stream = stream_create(fd, sink, group);
287
+   stream = stream_create(t, group);
288
    if (stream == NULL) {
289
        int err = errno;
290
        group_destroy(group);
291
@@ -301,11 +392,11 @@
292
    return &stream->this;
293
 }
294
 
295
-struct spa_bt_iso_io *spa_bt_iso_io_attach(struct spa_bt_iso_io *this, int fd, bool sink)
296
+struct spa_bt_iso_io *spa_bt_iso_io_attach(struct spa_bt_iso_io *this, struct spa_bt_transport *t)
297
 {
298
    struct stream *stream = SPA_CONTAINER_OF(this, struct stream, this);
299
 
300
-   stream = stream_create(fd, sink, stream->group);
301
+   stream = stream_create(t, stream->group);
302
    if (stream == NULL)
303
        return NULL;
304
 
305
@@ -321,6 +412,10 @@
306
    if (spa_list_is_empty(&stream->group->streams))
307
        group_destroy(stream->group);
308
 
309
+   if (stream->this.codec_data)
310
+       stream->codec->deinit(stream->this.codec_data);
311
+   stream->this.codec_data = NULL;
312
+
313
    free(stream);
314
 }
315
 
316
@@ -348,10 +443,12 @@
317
 
318
    enabled = group_is_enabled(stream->group);
319
 
320
-   if (!enabled && was_enabled)
321
+   if (!enabled && was_enabled) {
322
+       stream->group->started = false;
323
        set_timeout(stream->group, 0);
324
-   else if (enabled && !was_enabled)
325
+   } else if (enabled && !was_enabled) {
326
        set_timers(stream->group);
327
+   }
328
 
329
    stream->idle = true;
330
    stream->this.resync = true;
331
pipewire-0.3.69.tar.gz/spa/plugins/bluez5/iso-io.h -> pipewire-0.3.70.tar.gz/spa/plugins/bluez5/iso-io.h Changed
33
 
1
@@ -9,6 +9,9 @@
2
 #include <spa/support/loop.h>
3
 #include <spa/support/log.h>
4
 #include <spa/node/io.h>
5
+#include <spa/param/audio/format.h>
6
+
7
+struct spa_bt_transport;
8
 
9
 /**
10
  * ISO I/O.
11
@@ -26,15 +29,19 @@
12
    uint32_t timestamp; /**< Packet timestamp (set by pull callback) */
13
    uint8_t buf4096;    /**< Packet data (set by pull callback) */
14
    size_t size;        /**< Packet size (set by pull callback) */
15
+   bool need_resync;   /**< Resync requested (set by pull callback) */
16
+
17
+   struct spa_audio_info format;   /**< Audio format */
18
+   void *codec_data;       /**< Codec data */
19
 
20
    void *user_data;
21
 };
22
 
23
 typedef void (*spa_bt_iso_io_pull_t)(struct spa_bt_iso_io *io);
24
 
25
-struct spa_bt_iso_io *spa_bt_iso_io_create(int fd, bool sink, uint8_t cig, uint32_t interval,
26
+struct spa_bt_iso_io *spa_bt_iso_io_create(struct spa_bt_transport *t,
27
        struct spa_log *log, struct spa_loop *data_loop, struct spa_system *data_system);
28
-struct spa_bt_iso_io *spa_bt_iso_io_attach(struct spa_bt_iso_io *io, int fd, bool sink);
29
+struct spa_bt_iso_io *spa_bt_iso_io_attach(struct spa_bt_iso_io *io, struct spa_bt_transport *t);
30
 void spa_bt_iso_io_destroy(struct spa_bt_iso_io *io);
31
 void spa_bt_iso_io_set_cb(struct spa_bt_iso_io *io, spa_bt_iso_io_pull_t pull, void *user_data);
32
 
33
pipewire-0.3.69.tar.gz/spa/plugins/bluez5/media-sink.c -> pipewire-0.3.70.tar.gz/spa/plugins/bluez5/media-sink.c Changed
110
 
1
@@ -134,6 +134,7 @@
2
    unsigned int is_output:1;
3
    unsigned int flush_pending:1;
4
    unsigned int iso_pending:1;
5
+   unsigned int own_codec_data:1;
6
 
7
    unsigned int is_duplex:1;
8
    unsigned int is_internal:1;
9
@@ -990,16 +991,15 @@
10
    err = value - target;
11
    max_err = iso_io->duration;
12
 
13
-   if (err > max_err || (iso_io->resync && err > 0)) {
14
+   if (iso_io->resync && err >= 0) {
15
        unsigned int req = err * port->current_format.info.raw.rate / SPA_NSEC_PER_SEC;
16
 
17
        if (req > 0) {
18
            spa_bt_rate_control_init(&port->ratectl, 0);
19
            drop_frames(this, req);
20
-           spa_log_debug(this->log, "%p: ISO sync skip frames:%u resync:%d",
21
-                   this, req, iso_io->resync);
22
        }
23
-   } else if (-err > max_err || (iso_io->resync && -err > 0)) {
24
+       spa_log_debug(this->log, "%p: ISO sync skip frames:%u", this, req);
25
+   } else if (iso_io->resync && -err >= 0) {
26
        unsigned int req = -err * port->current_format.info.raw.rate / SPA_NSEC_PER_SEC;
27
        static const uint8_t empty8192 = {0};
28
 
29
@@ -1007,9 +1007,12 @@
30
            spa_bt_rate_control_init(&port->ratectl, 0);
31
            req = SPA_MIN(req, sizeof(empty) / port->frame_size);
32
            add_data(this, empty, req * port->frame_size);
33
-           spa_log_debug(this->log, "%p: ISO sync pad frames:%u resync:%d",
34
-                   this, req, iso_io->resync);
35
        }
36
+       spa_log_debug(this->log, "%p: ISO sync pad frames:%u", this, req);
37
+   } else if (err > max_err || -err > max_err) {
38
+       iso_io->need_resync = true;
39
+       spa_log_debug(this->log, "%p: ISO sync need resync err:%+.3f",
40
+               this, err / SPA_NSEC_PER_MSEC);
41
    } else {
42
        spa_bt_rate_control_update(&port->ratectl, err, 0,
43
                iso_io->duration, period, RATE_CTL_DIFF_MAX);
44
@@ -1172,15 +1175,22 @@
45
 
46
    flags = this->is_duplex ? MEDIA_CODEC_FLAG_SINK : 0;
47
 
48
-   this->codec_data = this->codec->init(this->codec,
49
-           flags,
50
-           this->transport->configuration,
51
-           this->transport->configuration_len,
52
-           &port->current_format,
53
-           this->codec_props,
54
-           this->transport->write_mtu);
55
-   if (this->codec_data == NULL)
56
-       return -EIO;
57
+   if (!this->transport->iso_io) {
58
+       this->own_codec_data = true;
59
+       this->codec_data = this->codec->init(this->codec,
60
+               flags,
61
+               this->transport->configuration,
62
+               this->transport->configuration_len,
63
+               &port->current_format,
64
+               this->codec_props,
65
+               this->transport->write_mtu);
66
+       if (this->codec_data == NULL)
67
+           return -EIO;
68
+   } else {
69
+       this->own_codec_data = false;
70
+       this->codec_data = this->transport->iso_io->codec_data;
71
+       this->codec_props_changed = true;
72
+   }
73
 
74
    spa_log_info(this->log, "%p: using %s codec %s, delay:%"PRIi64" ms", this,
75
            this->codec->bap ? "BAP" : "A2DP", this->codec->description,
76
@@ -1333,7 +1343,7 @@
77
 
78
    spa_loop_invoke(this->data_loop, do_remove_transport_source, 0, NULL, 0, true, this);
79
 
80
-   if (this->codec_data)
81
+   if (this->codec_data && this->own_codec_data)
82
        this->codec->deinit(this->codec_data);
83
    this->codec_data = NULL;
84
 }
85
@@ -1652,6 +1662,15 @@
86
            info.info.raw.channels > SPA_AUDIO_MAX_CHANNELS)
87
            return -EINVAL;
88
 
89
+       if (this->transport && this->transport->iso_io) {
90
+           if (memcmp(&info.info.raw, &this->transport->iso_io->format.info.raw,
91
+                           sizeof(info.info.raw))) {
92
+               spa_log_error(this->log, "unexpected incompatible "
93
+                       "BAP audio format");
94
+               return -EINVAL;
95
+           }
96
+       }
97
+
98
        port->frame_size = info.info.raw.channels;
99
        switch (info.info.raw.format) {
100
        case SPA_AUDIO_FORMAT_S16:
101
@@ -1930,7 +1949,7 @@
102
    else
103
        transport_stop(this);
104
 
105
-   if (state < SPA_BT_TRANSPORT_STATE_ACTIVE && was_started) {
106
+   if (state < SPA_BT_TRANSPORT_STATE_ACTIVE && was_started && !this->is_duplex && this->is_output) {
107
        /*
108
         * If establishing connection fails due to remote end not activating
109
         * the transport, we won't get a write error, but instead see a transport
110
pipewire-0.3.69.tar.gz/spa/plugins/bluez5/media-source.c -> pipewire-0.3.70.tar.gz/spa/plugins/bluez5/media-source.c Changed
10
 
1
@@ -1468,7 +1468,7 @@
2
        io->buffer_id = SPA_ID_INVALID;
3
    }
4
 
5
-   if (!this->source.loop) {
6
+   if (this->transport_started && !this->source.loop) {
7
        io->status = -EIO;
8
        return SPA_STATUS_STOPPED;
9
    }
10
pipewire-0.3.69.tar.gz/spa/plugins/support/logger.c -> pipewire-0.3.70.tar.gz/spa/plugins/support/logger.c Changed
58
 
1
@@ -293,7 +293,8 @@
2
 {
3
    struct impl *this;
4
    struct spa_loop *loop = NULL;
5
-   const char *str;
6
+   const char *str, *dest = "";
7
+   bool linebuf = false;
8
 
9
    spa_return_val_if_fail(factory != NULL, -EINVAL);
10
    spa_return_val_if_fail(handle != NULL, -EINVAL);
11
@@ -337,25 +338,37 @@
12
        if ((str = spa_dict_lookup(info, SPA_KEY_LOG_LEVEL)) != NULL)
13
            this->log.level = atoi(str);
14
        if ((str = spa_dict_lookup(info, SPA_KEY_LOG_FILE)) != NULL) {
15
-           this->file = fopen(str, "we");
16
-           if (this->file == NULL)
17
-               fprintf(stderr, "Warning: failed to open file %s: (%m)", str);
18
-           else
19
-               this->close_file = true;
20
+           dest = str;
21
+           if (spa_streq(str, "stderr"))
22
+               this->file = stderr;
23
+           else if (spa_streq(str, "stdout"))
24
+               this->file = stdout;
25
+           else {
26
+               this->file = fopen(str, "we");
27
+               if (this->file == NULL)
28
+                   fprintf(stderr, "Warning: failed to open file %s: (%m)", str);
29
+               else
30
+                   this->close_file = true;
31
+           }
32
        }
33
        if ((str = spa_dict_lookup(info, SPA_KEY_LOG_PATTERNS)) != NULL)
34
            support_log_parse_patterns(&this->patterns, str);
35
    }
36
-   if (this->file == NULL)
37
+   if (this->file == NULL) {
38
        this->file = stderr;
39
+       dest = "stderr";
40
+   } else {
41
+       linebuf = true;
42
+   }
43
+   if (linebuf)
44
+       setlinebuf(this->file);
45
+
46
    if (!isatty(fileno(this->file)))
47
        this->colors = false;
48
 
49
    spa_ringbuffer_init(&this->trace_rb);
50
 
51
-   spa_log_debug(&this->log, NAME " %p: initialized", this);
52
-
53
-   setlinebuf(this->file);
54
+   spa_log_debug(&this->log, NAME " %p: initialized to %s linebuf:%u", this, dest, linebuf);
55
 
56
    return 0;
57
 }
58
pipewire-0.3.69.tar.gz/spa/plugins/v4l2/v4l2-source.c -> pipewire-0.3.70.tar.gz/spa/plugins/v4l2/v4l2-source.c Changed
142
 
1
@@ -63,7 +63,8 @@
2
 struct control {
3
    uint32_t id;
4
    uint32_t ctrl_id;
5
-   double value;
6
+   uint32_t type;
7
+   int32_t value;
8
 };
9
 
10
 struct port {
11
@@ -148,6 +149,37 @@
12
 
13
 #include "v4l2-utils.c"
14
 
15
+static const struct spa_dict_item info_items = {
16
+   { SPA_KEY_DEVICE_API, "v4l2" },
17
+   { SPA_KEY_MEDIA_CLASS, "Video/Source" },
18
+   { SPA_KEY_MEDIA_ROLE, "Camera" },
19
+   { SPA_KEY_NODE_DRIVER, "true" },
20
+};
21
+
22
+static void emit_node_info(struct impl *this, bool full)
23
+{
24
+   uint64_t old = full ? this->info.change_mask : 0;
25
+   if (full)
26
+       this->info.change_mask = this->info_all;
27
+   if (this->info.change_mask) {
28
+       this->info.props = &SPA_DICT_INIT_ARRAY(info_items);
29
+       spa_node_emit_info(&this->hooks, &this->info);
30
+       this->info.change_mask = old;
31
+   }
32
+}
33
+
34
+static void emit_port_info(struct impl *this, struct port *port, bool full)
35
+{
36
+   uint64_t old = full ? port->info.change_mask : 0;
37
+   if (full)
38
+       port->info.change_mask = port->info_all;
39
+   if (port->info.change_mask) {
40
+       spa_node_emit_port_info(&this->hooks,
41
+               SPA_DIRECTION_OUTPUT, 0, &port->info);
42
+       port->info.change_mask = old;
43
+   }
44
+}
45
+
46
 static int port_get_format(struct port *port,
47
               uint32_t index,
48
               const struct spa_pod *filter,
49
@@ -255,14 +287,40 @@
50
    case SPA_PARAM_Props:
51
    {
52
        struct props *p = &this->props;
53
+       struct spa_pod_frame f;
54
+       struct port *port = &this->out_ports0;
55
+       uint32_t i;
56
+
57
+       if ((res = spa_v4l2_update_controls(this)) < 0) {
58
+           spa_log_error(this->log, "error: %s", spa_strerror(res));
59
+           return res;
60
+       }
61
 
62
        switch (result.index) {
63
        case 0:
64
-           param = spa_pod_builder_add_object(&b,
65
-               SPA_TYPE_OBJECT_Props, id,
66
+           spa_pod_builder_push_object(&b, &f, SPA_TYPE_OBJECT_Props, id);
67
+           spa_pod_builder_add(&b,
68
                SPA_PROP_device,     SPA_POD_String(p->device),
69
                SPA_PROP_deviceName, SPA_POD_String(p->device_name),
70
-               SPA_PROP_deviceFd,   SPA_POD_Int(p->device_fd));
71
+               SPA_PROP_deviceFd,   SPA_POD_Int(p->device_fd),
72
+               0);
73
+           for (i = 0; i < port->n_controls; i++) {
74
+               struct control *c = &port->controlsi;
75
+
76
+               spa_pod_builder_prop(&b, c->id, 0);
77
+               switch (c->type) {
78
+               case SPA_TYPE_Int:
79
+                   spa_pod_builder_int(&b, c->value);
80
+                   break;
81
+               case SPA_TYPE_Bool:
82
+                   spa_pod_builder_bool(&b, c->value);
83
+                   break;
84
+               default:
85
+                   spa_pod_builder_int(&b, c->value);
86
+                   break;
87
+               }
88
+           }
89
+           param = spa_pod_builder_pop(&b, &f);
90
            break;
91
        default:
92
            return 0;
93
@@ -322,7 +380,9 @@
94
                break;
95
            }
96
        }
97
-
98
+       this->info.change_mask |= SPA_NODE_CHANGE_MASK_PARAMS;
99
+       this->paramsNODE_Props.flags ^= SPA_PARAM_INFO_SERIAL;
100
+       emit_node_info(this, true);
101
        break;
102
    }
103
    default:
104
@@ -399,37 +459,6 @@
105
    return 0;
106
 }
107
 
108
-static const struct spa_dict_item info_items = {
109
-   { SPA_KEY_DEVICE_API, "v4l2" },
110
-   { SPA_KEY_MEDIA_CLASS, "Video/Source" },
111
-   { SPA_KEY_MEDIA_ROLE, "Camera" },
112
-   { SPA_KEY_NODE_DRIVER, "true" },
113
-};
114
-
115
-static void emit_node_info(struct impl *this, bool full)
116
-{
117
-   uint64_t old = full ? this->info.change_mask : 0;
118
-   if (full)
119
-       this->info.change_mask = this->info_all;
120
-   if (this->info.change_mask) {
121
-       this->info.props = &SPA_DICT_INIT_ARRAY(info_items);
122
-       spa_node_emit_info(&this->hooks, &this->info);
123
-       this->info.change_mask = old;
124
-   }
125
-}
126
-
127
-static void emit_port_info(struct impl *this, struct port *port, bool full)
128
-{
129
-   uint64_t old = full ? port->info.change_mask : 0;
130
-   if (full)
131
-       port->info.change_mask = port->info_all;
132
-   if (port->info.change_mask) {
133
-       spa_node_emit_port_info(&this->hooks,
134
-               SPA_DIRECTION_OUTPUT, 0, &port->info);
135
-       port->info.change_mask = old;
136
-   }
137
-}
138
-
139
 static int
140
 impl_node_add_listener(void *object,
141
        struct spa_hook *listener,
142
pipewire-0.3.69.tar.gz/spa/plugins/v4l2/v4l2-utils.c -> pipewire-0.3.70.tar.gz/spa/plugins/v4l2/v4l2-utils.c Changed
75
 
1
@@ -1139,10 +1139,9 @@
2
 
3
    spa_log_debug(this->log, "Control '%s' %d %d", queryctrl.name, prop_id, ctrl_id);
4
 
5
-   port->n_controls++;
6
-
7
    switch (queryctrl.type) {
8
    case V4L2_CTRL_TYPE_INTEGER:
9
+       port->controlsport->n_controls.type = SPA_TYPE_Int;
10
        param = spa_pod_builder_add_object(&b,
11
            SPA_TYPE_OBJECT_PropInfo, SPA_PARAM_PropInfo,
12
            SPA_PROP_INFO_id,   SPA_POD_Id(prop_id),
13
@@ -1154,6 +1153,7 @@
14
            SPA_PROP_INFO_description, SPA_POD_String(queryctrl.name));
15
        break;
16
    case V4L2_CTRL_TYPE_BOOLEAN:
17
+       port->controlsport->n_controls.type = SPA_TYPE_Bool;
18
        param = spa_pod_builder_add_object(&b,
19
            SPA_TYPE_OBJECT_PropInfo, SPA_PARAM_PropInfo,
20
            SPA_PROP_INFO_id,   SPA_POD_Id(prop_id),
21
@@ -1165,6 +1165,7 @@
22
        struct v4l2_querymenu querymenu;
23
        struct spa_pod_builder_state state;
24
 
25
+       port->controlsport->n_controls.type = SPA_TYPE_Int;
26
        spa_pod_builder_push_object(&b, &f0, SPA_TYPE_OBJECT_PropInfo, SPA_PARAM_PropInfo);
27
        spa_pod_builder_add(&b,
28
            SPA_PROP_INFO_id,    SPA_POD_Id(prop_id),
29
@@ -1205,6 +1206,9 @@
30
        goto next;
31
 
32
    }
33
+
34
+   port->n_controls++;
35
+
36
    if (spa_pod_filter(&b, &result.param, param, filter) < 0)
37
        goto next;
38
 
39
@@ -1220,6 +1224,35 @@
40
 }
41
 
42
 static int
43
+spa_v4l2_update_controls(struct impl *this)
44
+{
45
+   struct port *port = &this->out_ports0;
46
+   struct spa_v4l2_device *dev = &port->dev;
47
+   int res;
48
+   uint32_t i;
49
+
50
+   if ((res = spa_v4l2_open(dev, this->props.device)) < 0)
51
+       return res;
52
+
53
+   for (i = 0; i < port->n_controls; i++) {
54
+       struct control *c = &port->controlsi;
55
+       struct v4l2_control control;
56
+
57
+       spa_zero(control);
58
+       control.id = c->ctrl_id;
59
+       if (xioctl(dev->fd, VIDIOC_G_CTRL, &control) < 0) {
60
+           res = -errno;
61
+           goto done;
62
+       }
63
+       c->value = control.value;
64
+   }
65
+   res = 0;
66
+done:
67
+   spa_v4l2_close(dev);
68
+   return res;
69
+}
70
+
71
+static int
72
 spa_v4l2_set_control(struct impl *this, uint32_t id,
73
               const struct spa_pod_prop *prop)
74
 {
75
pipewire-0.3.69.tar.gz/src/daemon/minimal.conf.in -> pipewire-0.3.70.tar.gz/src/daemon/minimal.conf.in Changed
18
 
1
@@ -152,7 +152,15 @@
2
     #{ factory = spa-node-factory   args = { factory.name = api.vulkan.compute.source node.name = my-compute-source } }
3
 
4
     # Make a default metadata store
5
-    { factory = metadata            args = { metadata.name = default } }
6
+    { factory = metadata
7
+        args = {
8
+            metadata.name = default
9
+    #       metadata.values = 
10
+    #            { key = default.audio.sink   value = { name = somesink } }
11
+    #            { key = default.audio.source value = { name = somesource } }
12
+    #        
13
+        }
14
+    }
15
 
16
     # A default dummy driver. This handles nodes marked with the "node.always-driver"
17
     # property when no other driver is currently active. JACK clients need this.
18
pipewire-0.3.69.tar.gz/src/daemon/pipewire.conf.in -> pipewire-0.3.70.tar.gz/src/daemon/pipewire.conf.in Changed
54
 
1
@@ -47,6 +47,9 @@
2
 
3
     # keys checked below to disable module loading
4
     module.x11.bell = true
5
+    # enables autoloading of access module, when disabled an alternative
6
+    # access module needs to be loaded.
7
+    module.access = true
8
 }
9
 
10
 context.spa-libs = {
11
@@ -152,6 +155,7 @@
12
             # access.force permission.
13
             #access.force = flatpak
14
         }
15
+        condition =  { module.access = true } 
16
     }
17
 
18
     # Makes a factory for wrapping nodes in an adapter with a
19
@@ -263,6 +267,17 @@
20
     #        audio.position         = "FL,FR"
21
     #    }
22
     #}
23
+
24
+    # Use the metadata factory to create metadata and some default values.
25
+    #{ factory = metadata
26
+    #    args = {
27
+    #        metadata.name = my-metadata
28
+    #        metadata.values = 
29
+    #            { key = default.audio.sink   value = { name = somesink } }
30
+    #            { key = default.audio.source value = { name = somesource } }
31
+    #        
32
+    #    }
33
+    #}
34
 
35
 
36
 context.exec = 
37
@@ -279,12 +294,14 @@
38
     # but it is better to start it as a systemd service.
39
     # Run the session manager with -h for options.
40
     #
41
-    @sm_comment@{ path = "@session_manager_path@" args = "@session_manager_args@" }
42
+    @sm_comment@{ path = "@session_manager_path@" args = "@session_manager_args@"
43
+    @sm_comment@  condition =  { exec.session-manager = null } { exec.session-manager = true }  }
44
     #
45
     # You can optionally start the pulseaudio-server here as well
46
     # but it is better to start it as a systemd service.
47
     # It can be interesting to start another daemon here that listens
48
     # on another address with the -a option (eg. -a tcp:4713).
49
     #
50
-    @pulse_comment@{ path = "@pipewire_path@" args = "-c pipewire-pulse.conf" }
51
+    @pulse_comment@{ path = "@pipewire_path@" args = "-c pipewire-pulse.conf"
52
+    @pulse_comment@  condition =  { exec.pipewire-pulse = null } { exec.pipewire-pulse = true }  }
53
 
54
pipewire-0.3.69.tar.gz/src/gst/gstpipewiresrc.c -> pipewire-0.3.70.tar.gz/src/gst/gstpipewiresrc.c Changed
138
 
1
@@ -91,6 +91,8 @@
2
 static gboolean gst_pipewire_src_stop (GstBaseSrc * basesrc);
3
 static gboolean gst_pipewire_src_event (GstBaseSrc * src, GstEvent * event);
4
 static gboolean gst_pipewire_src_query (GstBaseSrc * src, GstQuery * query);
5
+static void gst_pipewire_src_get_times (GstBaseSrc * basesrc, GstBuffer * buffer,
6
+    GstClockTime * start, GstClockTime * end);
7
 
8
 static void
9
 gst_pipewire_src_set_property (GObject * object, guint prop_id,
10
@@ -413,6 +415,7 @@
11
   gstbasesrc_class->stop = gst_pipewire_src_stop;
12
   gstbasesrc_class->event = gst_pipewire_src_event;
13
   gstbasesrc_class->query = gst_pipewire_src_query;
14
+  gstbasesrc_class->get_times = gst_pipewire_src_get_times;
15
   gstpushsrc_class->create = gst_pipewire_src_create;
16
 
17
   GST_DEBUG_CATEGORY_INIT (pipewire_src_debug, "pipewiresrc", 0,
18
@@ -578,7 +581,7 @@
19
     GST_LOG_OBJECT (pwsrc, "pts %" G_GUINT64_FORMAT ", dts_offset %" G_GUINT64_FORMAT, h->pts, h->dts_offset);
20
 
21
     if (GST_CLOCK_TIME_IS_VALID (h->pts)) {
22
-      GST_BUFFER_PTS (buf) = h->pts + GST_PIPEWIRE_CLOCK (pwsrc->clock)->time_offset;
23
+      GST_BUFFER_PTS (buf) = h->pts;
24
       if (GST_BUFFER_PTS (buf) + h->dts_offset > 0)
25
         GST_BUFFER_DTS (buf) = GST_BUFFER_PTS (buf) + h->dts_offset;
26
     }
27
@@ -1126,14 +1129,43 @@
28
   return res;
29
 }
30
 
31
+static void
32
+gst_pipewire_src_get_times (GstBaseSrc * basesrc, GstBuffer * buffer,
33
+    GstClockTime * start, GstClockTime * end)
34
+{
35
+  GstPipeWireSrc *pwsrc = GST_PIPEWIRE_SRC (basesrc);
36
+
37
+  /* for live sources, sync on the timestamp of the buffer */
38
+  if (gst_base_src_is_live (basesrc)) {
39
+    GstClockTime timestamp = GST_BUFFER_PTS (buffer);
40
+
41
+    if (GST_CLOCK_TIME_IS_VALID (timestamp)) {
42
+      /* get duration to calculate end time */
43
+      GstClockTime duration = GST_BUFFER_DURATION (buffer);
44
+
45
+      if (GST_CLOCK_TIME_IS_VALID (duration)) {
46
+        *end = timestamp + duration;
47
+      }
48
+      *start = timestamp;
49
+    }
50
+  } else {
51
+    *start = GST_CLOCK_TIME_NONE;
52
+    *end = GST_CLOCK_TIME_NONE;
53
+  }
54
+
55
+  GST_LOG_OBJECT (pwsrc, "start %" GST_TIME_FORMAT " (%" G_GUINT64_FORMAT
56
+      "), end %" GST_TIME_FORMAT " (%" G_GUINT64_FORMAT ")",
57
+      GST_TIME_ARGS (*start), *start, GST_TIME_ARGS (*end), *end);
58
+}
59
+
60
 static GstFlowReturn
61
 gst_pipewire_src_create (GstPushSrc * psrc, GstBuffer ** buffer)
62
 {
63
   GstPipeWireSrc *pwsrc;
64
-  GstClockTime pts, dts, base_time;
65
   const char *error = NULL;
66
   GstBuffer *buf;
67
   gboolean update_time = FALSE, timeout = FALSE;
68
+  GstCaps *caps = NULL;
69
 
70
   pwsrc = GST_PIPEWIRE_SRC (psrc);
71
 
72
@@ -1157,6 +1189,18 @@
73
     if (state != PW_STREAM_STATE_STREAMING)
74
       goto streaming_stopped;
75
 
76
+    if ((caps = pwsrc->caps) != NULL) {
77
+      pwsrc->caps = NULL;
78
+      pw_thread_loop_unlock (pwsrc->core->loop);
79
+
80
+      GST_DEBUG_OBJECT (pwsrc, "set format %" GST_PTR_FORMAT, caps);
81
+      gst_base_src_set_caps (GST_BASE_SRC (pwsrc), caps);
82
+      gst_caps_unref (caps);
83
+
84
+      pw_thread_loop_lock (pwsrc->core->loop);
85
+      continue;
86
+    }
87
+
88
     if (pwsrc->eos) {
89
       if (pwsrc->last_buffer == NULL)
90
         goto streaming_eos;
91
@@ -1196,37 +1240,24 @@
92
 
93
   *buffer = buf;
94
 
95
-  if (pwsrc->is_live)
96
-    base_time = GST_ELEMENT_CAST (psrc)->base_time;
97
-  else
98
-    base_time = 0;
99
-
100
   if (update_time) {
101
-    GstClock *clock = gst_element_get_clock (GST_ELEMENT_CAST (pwsrc));
102
+    GstClock *clock;
103
+    GstClockTime pts, dts;
104
+
105
+    clock = gst_element_get_clock (GST_ELEMENT_CAST (pwsrc));
106
     if (clock != NULL) {
107
       pts = dts = gst_clock_get_time (clock);
108
       gst_object_unref (clock);
109
     } else {
110
       pts = dts = GST_CLOCK_TIME_NONE;
111
     }
112
-  } else {
113
-    pts = GST_BUFFER_PTS (*buffer);
114
-    dts = GST_BUFFER_DTS (*buffer);
115
-  }
116
-
117
-  if (GST_CLOCK_TIME_IS_VALID (pts))
118
-    pts = (pts >= base_time ? pts - base_time : 0);
119
-  if (GST_CLOCK_TIME_IS_VALID (dts))
120
-    dts = (dts >= base_time ? dts - base_time : 0);
121
 
122
-  GST_LOG_OBJECT (pwsrc,
123
-      "pts %" G_GUINT64_FORMAT ", dts %" G_GUINT64_FORMAT
124
-      ", base-time %" GST_TIME_FORMAT " -> %" GST_TIME_FORMAT ", %" GST_TIME_FORMAT,
125
-      GST_BUFFER_PTS (*buffer), GST_BUFFER_DTS (*buffer), GST_TIME_ARGS (base_time),
126
-      GST_TIME_ARGS (pts), GST_TIME_ARGS (dts));
127
+    GST_BUFFER_PTS (*buffer) = pts;
128
+    GST_BUFFER_DTS (*buffer) = dts;
129
 
130
-  GST_BUFFER_PTS (*buffer) = pts;
131
-  GST_BUFFER_DTS (*buffer) = dts;
132
+    GST_LOG_OBJECT (pwsrc, "Sending keepalive buffer pts/dts: %" GST_TIME_FORMAT
133
+      " (%" G_GUINT64_FORMAT ")", GST_TIME_ARGS (pts), pts);
134
+  }
135
 
136
   return GST_FLOW_OK;
137
 
138
pipewire-0.3.69.tar.gz/src/modules/module-filter-chain/builtin_plugin.c -> pipewire-0.3.70.tar.gz/src/modules/module-filter-chain/builtin_plugin.c Changed
116
 
1
@@ -287,13 +287,19 @@
2
    impl->rate = SampleRate;
3
    impl->b0 = impl->a0 = 1.0f;
4
    impl->type = bq_type_from_name(Descriptor->name);
5
+   if (impl->type != BQ_NONE)
6
+       return impl;
7
 
8
-   if (config == NULL)
9
+   if (config == NULL) {
10
+       pw_log_error("biquads:bq_raw requires a config section");
11
        goto error;
12
+   }
13
 
14
    spa_json_init(&it0, config, strlen(config));
15
-   if (spa_json_enter_object(&it0, &it1) <= 0)
16
+   if (spa_json_enter_object(&it0, &it1) <= 0) {
17
+       pw_log_error("biquads:config section must be an object");
18
        goto error;
19
+   }
20
 
21
    while (spa_json_get_string(&it1, key, sizeof(key)) > 0) {
22
        if (spa_streq(key, "coefficients")) {
23
@@ -349,8 +355,11 @@
24
                            goto error;
25
                        }
26
                    }
27
-                   else if (spa_json_next(&it1, &val) < 0)
28
-                       break;
29
+                   else {
30
+                       pw_log_warn("biquads: ignoring coefficients key: '%s'", key);
31
+                       if (spa_json_next(&it3, &val) < 0)
32
+                           break;
33
+                   }
34
                }
35
                if (labs((long)rate - (long)SampleRate) <
36
                    labs((long)best_rate - (long)SampleRate)) {
37
@@ -359,8 +368,11 @@
38
                }
39
            }
40
        }
41
-       else if (spa_json_next(&it1, &val) < 0)
42
-           break;
43
+       else {
44
+           pw_log_warn("biquads: ignoring config key: '%s'", key);
45
+           if (spa_json_next(&it1, &val) < 0)
46
+               break;
47
+       }
48
    }
49
 
50
    return impl;
51
@@ -842,12 +854,16 @@
52
    unsigned long rate;
53
 
54
    errno = EINVAL;
55
-   if (config == NULL)
56
+   if (config == NULL) {
57
+       pw_log_error("convolver: requires a config section");
58
        return NULL;
59
+   }
60
 
61
    spa_json_init(&it0, config, strlen(config));
62
-   if (spa_json_enter_object(&it0, &it1) <= 0)
63
+   if (spa_json_enter_object(&it0, &it1) <= 0) {
64
+       pw_log_error("convolver:config must be an object");
65
        return NULL;
66
+   }
67
 
68
    while (spa_json_get_string(&it1, key, sizeof(key)) > 0) {
69
        if (spa_streq(key, "blocksize")) {
70
@@ -918,8 +934,11 @@
71
                return NULL;
72
            }
73
        }
74
-       else if (spa_json_next(&it1, &val) < 0)
75
-           break;
76
+       else {
77
+           pw_log_warn("convolver: ignoring config key: '%s'", key);
78
+           if (spa_json_next(&it1, &val) < 0)
79
+               break;
80
+       }
81
    }
82
    if (filenames0 == NULL) {
83
        pw_log_error("convolver:filename was not given");
84
@@ -1061,13 +1080,16 @@
85
    float max_delay = 1.0f;
86
 
87
    if (config == NULL) {
88
+       pw_log_error("delay: requires a config section");
89
        errno = EINVAL;
90
        return NULL;
91
    }
92
 
93
    spa_json_init(&it0, config, strlen(config));
94
-   if (spa_json_enter_object(&it0, &it1) <= 0)
95
+   if (spa_json_enter_object(&it0, &it1) <= 0) {
96
+       pw_log_error("delay:config must be an object");
97
        return NULL;
98
+   }
99
 
100
    while (spa_json_get_string(&it1, key, sizeof(key)) > 0) {
101
        if (spa_streq(key, "max-delay")) {
102
@@ -1075,9 +1097,11 @@
103
                pw_log_error("delay:max-delay requires a number");
104
                return NULL;
105
            }
106
+       } else {
107
+           pw_log_warn("delay: ignoring config key: '%s'", key);
108
+           if (spa_json_next(&it1, &val) < 0)
109
+               break;
110
        }
111
-       else if (spa_json_next(&it1, &val) < 0)
112
-           break;
113
    }
114
    if (max_delay <= 0.0f)
115
        max_delay = 1.0f;
116
pipewire-0.3.69.tar.gz/src/modules/module-metadata.c -> pipewire-0.3.70.tar.gz/src/modules/module-metadata.c Changed
135
 
1
@@ -10,6 +10,7 @@
2
 #include "config.h"
3
 
4
 #include <spa/utils/result.h>
5
+#include <spa/utils/json.h>
6
 
7
 #include <pipewire/impl.h>
8
 #include <pipewire/extensions/metadata.h>
9
@@ -19,6 +20,12 @@
10
 
11
 #define NAME "metadata"
12
 
13
+#define FACTORY_USAGE   "("PW_KEY_METADATA_NAME" = <name> ) "                      \
14
+                        "("PW_KEY_METADATA_VALUES" =  "                        \
15
+                        "   { ( id = <int> ) key = <string> ( type = <string> ) value = <json> } " \
16
+                        "   ..."                                   \
17
+                        "   )"
18
+
19
 PW_LOG_TOPIC_STATIC(mod_topic, "mod." NAME);
20
 #define PW_LOG_TOPIC_DEFAULT mod_topic
21
 
22
@@ -29,7 +36,7 @@
23
 };
24
 
25
 
26
-void * pw_metadata_new(struct pw_context *context, struct pw_resource *resource,
27
+struct pw_metadata *pw_metadata_new(struct pw_context *context, struct pw_resource *resource,
28
           struct pw_properties *properties);
29
 
30
 struct pw_proxy *pw_core_metadata_export(struct pw_core *core,
31
@@ -47,6 +54,56 @@
32
    struct pw_export_type export_metadata;
33
 };
34
 
35
+/*
36
+ * 
37
+ *     { ( "id" = <int>, ) "key" = <string> ("type" = <string>) "value" = <json> }
38
+ *     ....
39
+ * 
40
+ */
41
+static int fill_metadata(struct pw_metadata *metadata, const char *str)
42
+{
43
+   struct spa_json it3;
44
+
45
+   spa_json_init(&it0, str, strlen(str));
46
+   if (spa_json_enter_array(&it0, &it1) <= 0)
47
+       return -EINVAL;
48
+
49
+   while (spa_json_enter_object(&it1, &it2) > 0) {
50
+       char key256, *k = NULL, *v = NULL, *t = NULL;
51
+       int id = 0;
52
+
53
+       while (spa_json_get_string(&it2, key, sizeof(key)) > 0) {
54
+           int len;
55
+           const char *val;
56
+
57
+           if ((len = spa_json_next(&it2, &val)) <= 0)
58
+               return -EINVAL;
59
+
60
+           if (spa_streq(key, "id")) {
61
+               if (spa_json_parse_int(val, len, &id) <= 0)
62
+                   return -EINVAL;
63
+           } else if (spa_streq(key, "key")) {
64
+               if ((k = malloc(len+1)) != NULL)
65
+                   spa_json_parse_stringn(val, len, k, len+1);
66
+           } else if (spa_streq(key, "type")) {
67
+               if ((t = malloc(len+1)) != NULL)
68
+                   spa_json_parse_stringn(val, len, t, len+1);
69
+           } else if (spa_streq(key, "value")) {
70
+               if (spa_json_is_container(val, len))
71
+                   len = spa_json_container_len(&it2, val, len);
72
+               if ((v = malloc(len+1)) != NULL)
73
+                   spa_json_parse_stringn(val, len, v, len+1);
74
+           }
75
+       }
76
+       if (k != NULL && v != NULL)
77
+           pw_metadata_set_property(metadata, id, k, t, v);
78
+       free(k);
79
+       free(v);
80
+       free(t);
81
+   }
82
+   return 0;
83
+}
84
+
85
 static void *create_object(void *_data,
86
               struct pw_resource *resource,
87
               const char *type,
88
@@ -56,9 +113,10 @@
89
 {
90
    struct factory_data *data = _data;
91
    struct pw_context *context = pw_impl_module_get_context(data->module);
92
-   void *result;
93
+   struct pw_metadata *result;
94
    struct pw_resource *metadata_resource = NULL;
95
    struct pw_impl_client *client = resource ? pw_resource_get_client(resource) : NULL;
96
+   const char *str;
97
    int res;
98
 
99
    if (properties == NULL)
100
@@ -91,14 +149,20 @@
101
            goto error_node;
102
        }
103
    } else {
104
-       result = pw_context_create_metadata(context, NULL, properties, 0);
105
-       if (result == NULL) {
106
+       struct pw_impl_metadata *impl;
107
+
108
+       impl = pw_context_create_metadata(context, NULL, properties, 0);
109
+       if (impl == NULL) {
110
            properties = NULL;
111
            res = -errno;
112
            goto error_node;
113
        }
114
-       pw_impl_metadata_register(result, NULL);
115
+       pw_impl_metadata_register(impl, NULL);
116
+       result = pw_impl_metadata_get_implementation(impl);
117
    }
118
+   if ((str = pw_properties_get(properties, PW_KEY_METADATA_VALUES)) != NULL)
119
+       fill_metadata(result, str);
120
+
121
    return result;
122
 
123
 error_resource:
124
@@ -189,7 +253,9 @@
125
                 "metadata",
126
                 PW_TYPE_INTERFACE_Metadata,
127
                 PW_VERSION_METADATA,
128
-                NULL,
129
+                pw_properties_new(
130
+                                         PW_KEY_FACTORY_USAGE, FACTORY_USAGE,
131
+                                         NULL),
132
                 sizeof(*data));
133
    if (factory == NULL)
134
        return -errno;
135
pipewire-0.3.69.tar.gz/src/modules/module-metadata/metadata.c -> pipewire-0.3.70.tar.gz/src/modules/module-metadata/metadata.c Changed
17
 
1
@@ -234,7 +234,7 @@
2
    .destroy = global_resource_destroy,
3
 };
4
 
5
-void *
6
+struct pw_metadata *
7
 pw_metadata_new(struct pw_context *context, struct pw_resource *resource,
8
           struct pw_properties *properties)
9
 {
10
@@ -292,5 +292,5 @@
11
            &impl->resource_listener,
12
            &global_resource_events, impl);
13
 
14
-   return impl;
15
+   return impl->metadata;
16
 }
17
pipewire-0.3.69.tar.gz/src/modules/module-protocol-pulse/modules/module-echo-cancel.c -> pipewire-0.3.70.tar.gz/src/modules/module-protocol-pulse/modules/module-echo-cancel.c Changed
151
 
1
@@ -46,9 +46,10 @@
2
 
3
 static int module_echo_cancel_load(struct module *module)
4
 {
5
+   struct pw_properties * const props = module->props;
6
    struct module_echo_cancel_data *data = module->user_data;
7
+   const char *method;
8
    FILE *f;
9
-   const char *str;
10
    char *args;
11
    size_t size;
12
    uint32_t i;
13
@@ -57,12 +58,14 @@
14
        return -errno;
15
 
16
    fprintf(f, "{");
17
-   /* Can't just serialise this dict because the "null" method gets
18
-    * interpreted as a JSON null */
19
-   if ((str = pw_properties_get(data->props, "aec.method")))
20
-       fprintf(f, " aec.method = \"%s\"", str);
21
-   if ((str = pw_properties_get(data->props, "aec.args")))
22
-       fprintf(f, " aec.args = \"%s\"", str);
23
+   if ((method = pw_properties_get(props, "aec_method")) == NULL)
24
+       method = "webrtc";
25
+
26
+   fprintf(f, " library.name = \"aec/libspa-aec-%s\"", method);
27
+
28
+   fprintf(f, " aec.args = {");
29
+   pw_properties_serialize_dict(f, &data->props->dict, 0);
30
+   fprintf(f, " }");
31
    if (data->info.rate != 0)
32
        fprintf(f, " audio.rate = %u", data->info.rate);
33
    if (data->info.channels != 0) {
34
@@ -149,13 +152,86 @@
35
    { PW_KEY_MODULE_VERSION, PACKAGE_VERSION },
36
 };
37
 
38
+static void rename_bool_prop(struct pw_properties *props, const char *pa_key, const char *pw_key)
39
+{
40
+   const char *str;
41
+   if ((str = pw_properties_get(props, pa_key)) != NULL) {
42
+       pw_properties_set(props, pw_key, module_args_parse_bool(str) ? "true" : "false");
43
+       pw_properties_set(props, pa_key, NULL);
44
+   }
45
+}
46
+static int parse_point(const char **point, float f3)
47
+{
48
+   int length;
49
+   if (sscanf(*point, "%g,%g,%g%n", &f0, &f1, &f2, &length) != 3)
50
+       return -EINVAL;
51
+   return length;
52
+}
53
+
54
+static int rename_geometry(struct pw_properties *props, const char *pa_key, const char *pw_key)
55
+{
56
+   const char *str;
57
+   int len;
58
+   char *args;
59
+   size_t size;
60
+   FILE *f;
61
+
62
+   if ((str = pw_properties_get(props, pa_key)) == NULL)
63
+       return 0;
64
+
65
+   pw_log_info("geometry: %s", str);
66
+
67
+   if ((f = open_memstream(&args, &size)) == NULL)
68
+       return -errno;
69
+
70
+   fprintf(f, " ");
71
+   while (true) {
72
+       float p3;
73
+       if ((len = parse_point(&str, p)) < 0)
74
+           break;
75
+
76
+       fprintf(f, " %f %f %f  ", p0, p1, p2);
77
+       str += len;
78
+       if (*str != ',')
79
+           break;
80
+       str++;
81
+   }
82
+   fprintf(f, "");
83
+   fclose(f);
84
+
85
+   pw_properties_set(props, pw_key, args);
86
+   free(args);
87
+
88
+   pw_properties_set(props, pa_key, NULL);
89
+   return 0;
90
+}
91
+
92
+static int rename_direction(struct pw_properties *props, const char *pa_key, const char *pw_key)
93
+{
94
+   const char *str;
95
+   int res;
96
+   float f3;
97
+
98
+   if ((str = pw_properties_get(props, pa_key)) == NULL)
99
+       return 0;
100
+
101
+   pw_log_info("direction: %s", str);
102
+
103
+   if ((res = parse_point(&str, f)) < 0)
104
+       return res;
105
+
106
+   pw_properties_setf(props, pw_key, " %f %f %f ", f0, f1, f2);
107
+   pw_properties_set(props, pa_key, NULL);
108
+   return 0;
109
+}
110
+
111
 static int module_echo_cancel_prepare(struct module * const module)
112
 {
113
    struct module_echo_cancel_data * const d = module->user_data;
114
    struct pw_properties * const props = module->props;
115
    struct pw_properties *aec_props = NULL, *sink_props = NULL, *source_props = NULL;
116
    struct pw_properties *playback_props = NULL, *capture_props = NULL;
117
-   const char *str;
118
+   const char *str, *method;
119
    struct spa_audio_info_raw info = { 0 };
120
    int res;
121
 
122
@@ -217,13 +293,23 @@
123
        pw_properties_set(props, "sink_properties", NULL);
124
    }
125
 
126
-   if ((str = pw_properties_get(props, "aec_method")) != NULL) {
127
-       pw_properties_set(aec_props, "aec.method", str);
128
-       pw_properties_set(props, "aec_method", NULL);
129
-   }
130
+   if ((method = pw_properties_get(props, "aec_method")) == NULL)
131
+       method = "webrtc";
132
 
133
    if ((str = pw_properties_get(props, "aec_args")) != NULL) {
134
-       pw_properties_set(aec_props, "aec.args", str);
135
+       module_args_add_props(aec_props, str);
136
+       if (spa_streq(method, "webrtc")) {
137
+           rename_bool_prop(aec_props, "high_pass_filter", "webrtc.high_pass_filter");
138
+           rename_bool_prop(aec_props, "noise_suppression", "webrtc.noise_suppression");
139
+           rename_bool_prop(aec_props, "analog_gain_control", "webrtc.gain_control");
140
+           rename_bool_prop(aec_props, "digital_gain_control", "webrtc.gain_control");
141
+           rename_bool_prop(aec_props, "voice_detection", "webrtc.voice_detection");
142
+           rename_bool_prop(aec_props, "extended_filter", "webrtc.extended_filter");
143
+           rename_bool_prop(aec_props, "experimental_agc", "webrtc.experimental_agc");
144
+           rename_bool_prop(aec_props, "beamforming", "webrtc.beamforming");
145
+           rename_geometry(aec_props, "mic_geometry", "webrtc.mic-geometry");
146
+           rename_direction(aec_props, "target_direction", "webrtc.target-direction");
147
+       }
148
        pw_properties_set(props, "aec_args", NULL);
149
    }
150
 
151
pipewire-0.3.69.tar.gz/src/modules/module-pulse-tunnel.c -> pipewire-0.3.70.tar.gz/src/modules/module-pulse-tunnel.c Changed
227
 
1
@@ -162,9 +162,13 @@
2
    void *buffer;
3
    uint8_t empty8192;
4
 
5
+   bool mute;
6
+   pa_cvolume volume;
7
+
8
    pa_threaded_mainloop *pa_mainloop;
9
    pa_context *pa_context;
10
    pa_stream *pa_stream;
11
+   uint32_t pa_index;
12
 
13
    struct ratelimit rate_limit;
14
 
15
@@ -233,6 +237,79 @@
16
    }
17
 }
18
 
19
+static void stream_param_changed(void *d, uint32_t id, const struct spa_pod *param)
20
+{
21
+   struct impl *impl = d;
22
+   char buf1024;
23
+   struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buf, sizeof(buf));
24
+   struct spa_pod_frame f1;
25
+   struct spa_pod_object *obj = (struct spa_pod_object *) param;
26
+   struct spa_pod_prop *prop;
27
+
28
+   if (param == NULL || id != SPA_PARAM_Props)
29
+       return;
30
+
31
+   spa_pod_builder_push_object(&b, &f0, SPA_TYPE_OBJECT_Props, SPA_PARAM_Props);
32
+
33
+   SPA_POD_OBJECT_FOREACH(obj, prop) {
34
+       switch (prop->key) {
35
+       case SPA_PROP_mute:
36
+       {
37
+           bool mute;
38
+           if (spa_pod_get_bool(&prop->value, &mute) == 0) {
39
+               pa_threaded_mainloop_lock(impl->pa_mainloop);
40
+               if (impl->mode == MODE_SOURCE) {
41
+                   pa_context_set_source_output_mute(impl->pa_context,
42
+                           impl->pa_index, mute,
43
+                           NULL, impl);
44
+               } else {
45
+                   pa_context_set_sink_input_mute(impl->pa_context,
46
+                           impl->pa_index, mute,
47
+                           NULL, impl);
48
+               }
49
+               pa_threaded_mainloop_unlock(impl->pa_mainloop);
50
+                        }
51
+           break;
52
+       }
53
+       case SPA_PROP_channelVolumes:
54
+       {
55
+           struct pa_cvolume volume;
56
+           uint32_t n;
57
+           float volsSPA_AUDIO_MAX_CHANNELS;
58
+
59
+           if ((n = spa_pod_copy_array(&prop->value, SPA_TYPE_Float,
60
+                   vols, SPA_AUDIO_MAX_CHANNELS)) > 0) {
61
+               volume.channels = n;
62
+               for (n = 0; n < volume.channels; n++)
63
+                   volume.valuesn = pa_sw_volume_from_linear(volsn);
64
+
65
+               pa_threaded_mainloop_lock(impl->pa_mainloop);
66
+               if (impl->mode == MODE_SOURCE) {
67
+                   pa_context_set_source_output_volume(impl->pa_context,
68
+                           impl->pa_index, &volume,
69
+                           NULL, impl);
70
+               } else {
71
+                   pa_context_set_sink_input_volume(impl->pa_context,
72
+                           impl->pa_index, &volume,
73
+                           NULL, impl);
74
+               }
75
+               pa_threaded_mainloop_unlock(impl->pa_mainloop);
76
+           }
77
+           break;
78
+       }
79
+       case SPA_PROP_softVolumes:
80
+       case SPA_PROP_softMute:
81
+           break;
82
+       default:
83
+           spa_pod_builder_raw_padded(&b, prop, SPA_POD_PROP_SIZE(prop));
84
+           break;
85
+       }
86
+   }
87
+   param = spa_pod_builder_pop(&b, &f0);
88
+
89
+   pw_stream_set_param(impl->stream, id, param);
90
+}
91
+
92
 static void update_rate(struct impl *impl, uint32_t filled)
93
 {
94
    float error, corr;
95
@@ -356,6 +433,7 @@
96
    .destroy = stream_destroy,
97
    .state_changed = stream_state_changed,
98
    .io_changed = stream_io_changed,
99
+   .param_changed = stream_param_changed,
100
    .process = playback_stream_process
101
 };
102
 
103
@@ -364,6 +442,7 @@
104
    .destroy = stream_destroy,
105
    .state_changed = stream_state_changed,
106
    .io_changed = stream_io_changed,
107
+   .param_changed = stream_param_changed,
108
    .process = capture_stream_process
109
 };
110
 
111
@@ -464,6 +543,7 @@
112
        do_destroy = true;
113
        SPA_FALLTHROUGH;
114
    case PA_STREAM_READY:
115
+       impl->pa_index = pa_stream_get_index(impl->pa_stream);
116
        pa_threaded_mainloop_signal(impl->pa_mainloop, 0);
117
        break;
118
    case PA_STREAM_UNCONNECTED:
119
@@ -615,6 +695,78 @@
120
    pa_threaded_mainloop_signal(impl->pa_mainloop, 0);
121
 }
122
 
123
+static int
124
+do_stream_sync_volumes(struct spa_loop *loop,
125
+   bool async, uint32_t seq, const void *data, size_t size, void *user_data)
126
+{
127
+   struct impl *impl = user_data;
128
+   char buf1024;
129
+   struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buf, sizeof(buf));
130
+   struct spa_pod_frame f1;
131
+   struct spa_pod *param;
132
+   uint32_t i;
133
+   float volsSPA_AUDIO_MAX_CHANNELS;
134
+   float soft_volsSPA_AUDIO_MAX_CHANNELS;
135
+
136
+   for (i = 0; i < impl->volume.channels; i++) {
137
+       volsi = pa_sw_volume_to_linear(impl->volume.valuesi);
138
+       soft_volsi = 1.0f;
139
+   }
140
+
141
+   spa_pod_builder_push_object(&b, &f0, SPA_TYPE_OBJECT_Props, SPA_PARAM_Props);
142
+   spa_pod_builder_prop(&b, SPA_PROP_softMute, 0);
143
+   spa_pod_builder_bool(&b, impl->mute);
144
+   spa_pod_builder_prop(&b, SPA_PROP_mute, 0);
145
+   spa_pod_builder_bool(&b, impl->mute);
146
+
147
+   spa_pod_builder_prop(&b, SPA_PROP_channelVolumes, 0);
148
+   spa_pod_builder_array(&b, sizeof(float), SPA_TYPE_Float,
149
+           impl->volume.channels, vols);
150
+   spa_pod_builder_prop(&b, SPA_PROP_softVolumes, 0);
151
+   spa_pod_builder_array(&b, sizeof(float), SPA_TYPE_Float,
152
+           impl->volume.channels, soft_vols);
153
+   param = spa_pod_builder_pop(&b, &f0);
154
+
155
+   pw_stream_set_param(impl->stream, SPA_PARAM_Props, param);
156
+   return 0;
157
+}
158
+
159
+static void stream_sync_volumes(struct impl *impl, const struct pa_cvolume *volume, bool mute)
160
+{
161
+   impl->mute = mute;
162
+   impl->volume = *volume;
163
+   pw_loop_invoke(impl->main_loop, do_stream_sync_volumes, 1, NULL, 0, false, impl);
164
+}
165
+
166
+static void source_output_info_cb(pa_context *c, const pa_source_output_info *i, int eol, void *userdata)
167
+{
168
+   struct impl *impl = userdata;
169
+   if (i != NULL)
170
+       stream_sync_volumes(impl, &i->volume, i->mute);
171
+}
172
+
173
+static void sink_input_info_cb(pa_context *c, const pa_sink_input_info *i, int eol, void *userdata)
174
+{
175
+   struct impl *impl = userdata;
176
+   if (i != NULL)
177
+       stream_sync_volumes(impl, &i->volume, i->mute);
178
+}
179
+
180
+static void context_subscribe_cb(pa_context *c, pa_subscription_event_type_t t,
181
+       uint32_t idx, void *userdata)
182
+{
183
+   struct impl *impl = userdata;
184
+   if (idx != impl->pa_index)
185
+       return;
186
+
187
+   if (impl->mode == MODE_SOURCE)
188
+       pa_context_get_source_output_info(impl->pa_context,
189
+               idx, source_output_info_cb, impl);
190
+   else
191
+       pa_context_get_sink_input_info(impl->pa_context,
192
+               idx, sink_input_info_cb, impl);
193
+}
194
+
195
 static pa_proplist* tunnel_new_proplist(struct impl *impl)
196
 {
197
    pa_proplist *proplist = pa_proplist_new();
198
@@ -659,6 +811,8 @@
199
 
200
    pa_threaded_mainloop_lock(impl->pa_mainloop);
201
 
202
+   pa_context_set_subscribe_callback(impl->pa_context, context_subscribe_cb, impl);
203
+
204
    if (pa_threaded_mainloop_start(impl->pa_mainloop) < 0)
205
        goto error_unlock;
206
 
207
@@ -717,6 +871,9 @@
208
    if (impl->mode == MODE_SOURCE) {
209
        bufferattr.fragsize = latency_bytes / 2;
210
 
211
+       pa_context_subscribe(impl->pa_context,
212
+               PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT, NULL, impl);
213
+
214
        res = pa_stream_connect_record(impl->pa_stream,
215
                remote_node_target, &bufferattr,
216
                PA_STREAM_DONT_MOVE |
217
@@ -728,6 +885,9 @@
218
        bufferattr.minreq = bufferattr.tlength / 4;
219
        bufferattr.prebuf = bufferattr.tlength;
220
 
221
+       pa_context_subscribe(impl->pa_context,
222
+               PA_SUBSCRIPTION_MASK_SINK_INPUT, NULL, impl);
223
+
224
        res = pa_stream_connect_playback(impl->pa_stream,
225
                remote_node_target, &bufferattr,
226
                PA_STREAM_DONT_MOVE |
227
pipewire-0.3.69.tar.gz/src/modules/module-raop-sink.c -> pipewire-0.3.70.tar.gz/src/modules/module-raop-sink.c Changed
103
 
1
@@ -145,6 +145,10 @@
2
 
3
 #define DEFAULT_LATENCY 22050
4
 
5
+#define VOLUME_MAX  0.0
6
+#define VOLUME_DEF -30.0
7
+#define VOLUME_MIN -144.0
8
+
9
 #define MODULE_USAGE   "( raop.ip=<ip address of host> ) "                 \
10
            "( raop.port=<remote port> ) "                      \
11
            "( raop.name=<name of host> ) "                     \
12
@@ -252,6 +256,9 @@
13
    unsigned int ready:1;
14
    unsigned int recording:1;
15
 
16
+   bool mute;
17
+   float volume;
18
+
19
    uint8_t bufferFRAMES_PER_TCP_PACKET * 4;
20
    uint32_t filled;
21
 };
22
@@ -1547,6 +1554,70 @@
23
    return rtsp_send(impl, "TEARDOWN", NULL, NULL, rtsp_teardown_reply);
24
 }
25
 
26
+static void stream_props_changed(struct impl *impl, uint32_t id, const struct spa_pod *param)
27
+{
28
+   char buf1024;
29
+   struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buf, sizeof(buf));
30
+   struct spa_pod_frame f1;
31
+   struct spa_pod_object *obj = (struct spa_pod_object *) param;
32
+   struct spa_pod_prop *prop;
33
+
34
+   spa_pod_builder_push_object(&b, &f0, SPA_TYPE_OBJECT_Props, SPA_PARAM_Props);
35
+
36
+   SPA_POD_OBJECT_FOREACH(obj, prop) {
37
+       switch (prop->key) {
38
+       case SPA_PROP_mute:
39
+       {
40
+           bool mute;
41
+           if (spa_pod_get_bool(&prop->value, &mute) == 0) {
42
+               impl->mute = mute;
43
+                        }
44
+           spa_pod_builder_prop(&b, SPA_PROP_softMute, 0);
45
+           spa_pod_builder_bool(&b, impl->mute);
46
+           spa_pod_builder_raw_padded(&b, prop, SPA_POD_PROP_SIZE(prop));
47
+           break;
48
+       }
49
+       case SPA_PROP_channelVolumes:
50
+       {
51
+           uint32_t i, n_vols;
52
+           float volsSPA_AUDIO_MAX_CHANNELS, volume;
53
+           float soft_volsSPA_AUDIO_MAX_CHANNELS;
54
+           char header128, volstr64;
55
+
56
+           if ((n_vols = spa_pod_copy_array(&prop->value, SPA_TYPE_Float,
57
+                   vols, SPA_AUDIO_MAX_CHANNELS)) > 0) {
58
+               volume = 0.0f;
59
+               for (i = 0; i < n_vols; i++) {
60
+                   volume += volsi;
61
+                   soft_volsi = 1.0f;
62
+               }
63
+               volume /= n_vols;
64
+               volume = SPA_CLAMPF(20.0 * log10(volume), VOLUME_MIN, VOLUME_MAX);
65
+               impl->volume = volume;
66
+
67
+               snprintf(header, sizeof(header), "volume: %s\r\n",
68
+                       spa_dtoa(volstr, sizeof(volstr), volume));
69
+               rtsp_send(impl, "SET_PARAMETER", "text/parameters", header, NULL);
70
+           }
71
+           spa_pod_builder_prop(&b, SPA_PROP_softVolumes, 0);
72
+           spa_pod_builder_array(&b, sizeof(float), SPA_TYPE_Float,
73
+                   n_vols, soft_vols);
74
+           spa_pod_builder_raw_padded(&b, prop, SPA_POD_PROP_SIZE(prop));
75
+           break;
76
+       }
77
+       case SPA_PROP_softVolumes:
78
+       case SPA_PROP_softMute:
79
+           break;
80
+       default:
81
+           spa_pod_builder_raw_padded(&b, prop, SPA_POD_PROP_SIZE(prop));
82
+           break;
83
+       }
84
+   }
85
+   param = spa_pod_builder_pop(&b, &f0);
86
+
87
+   pw_stream_set_param(impl->stream, id, param);
88
+}
89
+
90
 static void stream_param_changed(void *data, uint32_t id, const struct spa_pod *param)
91
 {
92
    struct impl *impl = data;
93
@@ -1558,6 +1629,9 @@
94
        else
95
            rtsp_do_connect(impl);
96
        break;
97
+   case SPA_PARAM_Props:
98
+       if (param != NULL)
99
+           stream_props_changed(impl, id, param);
100
    default:
101
        break;
102
    }
103
pipewire-0.3.69.tar.gz/src/modules/module-rtp-session.c -> pipewire-0.3.70.tar.gz/src/modules/module-rtp-session.c Changed
39
 
1
@@ -264,17 +264,8 @@
2
 {
3
    ssize_t n;
4
    n = sendmsg(fd, msg, MSG_NOSIGNAL);
5
-   if (n < 0) {
6
-       switch (errno) {
7
-       case ECONNREFUSED:
8
-       case ECONNRESET:
9
-           pw_log_debug("remote end not listening");
10
-           break;
11
-       default:
12
-           pw_log_debug("sendmsg() failed: %m");
13
-           break;
14
-       }
15
-   }
16
+   if (n < 0)
17
+       pw_log_debug("sendmsg() failed: %m");
18
    return n;
19
 }
20
 
21
@@ -889,7 +880,7 @@
22
        latency = t3 - t1;
23
        offset = ((t3 + t1) / 2) - t2;
24
 
25
-       pw_log_info("latency:%f offset:%f", latency / 1e5, offset / 1e5);
26
+       pw_log_debug("latency:%f offset:%f", latency / 1e5, offset / 1e5);
27
        if (hdr->count >= 2)
28
            return;
29
    }
30
@@ -1654,7 +1645,7 @@
31
    struct session *sess;
32
    uint64_t current_time = impl->next_time;
33
 
34
-   pw_log_info("timeout");
35
+   pw_log_debug("timeout");
36
    spa_list_for_each(sess, &impl->sessions, link) {
37
        if (sess->state != SESSION_STATE_ESTABLISHED)
38
            continue;
39
pipewire-0.3.69.tar.gz/src/modules/module-rtp-sink.c -> pipewire-0.3.70.tar.gz/src/modules/module-rtp-sink.c Changed
21
 
1
@@ -197,17 +197,8 @@
2
    msg.msg_flags = 0;
3
 
4
    n = sendmsg(impl->rtp_fd, &msg, MSG_NOSIGNAL);
5
-   if (n < 0) {
6
-       switch (errno) {
7
-       case ECONNREFUSED:
8
-       case ECONNRESET:
9
-           pw_log_debug("remote end not listening");
10
-           break;
11
-       default:
12
-           pw_log_warn("sendmsg() failed: %m");
13
-           break;
14
-       }
15
-   }
16
+   if (n < 0)
17
+       pw_log_debug("sendmsg() failed: %m");
18
 }
19
 
20
 static void stream_state_changed(void *data, bool started, const char *error)
21
pipewire-0.3.69.tar.gz/src/modules/module-x11-bell.c -> pipewire-0.3.70.tar.gz/src/modules/module-x11-bell.c Changed
12
 
1
@@ -159,8 +159,8 @@
2
    unsigned int auto_ctrls, auto_values;
3
 
4
    if (!(impl->display = XOpenDisplay(name))) {
5
-       pw_log_error("XOpenDisplay() failed");
6
-       return -EIO;
7
+       pw_log_info("XOpenDisplay() failed. Uninstall or disable the module-x11-bell module");
8
+       return -EHOSTDOWN;
9
    }
10
 
11
    impl->source = pw_loop_add_io(impl->loop,
12
pipewire-0.3.69.tar.gz/src/pipewire/conf.c -> pipewire-0.3.70.tar.gz/src/pipewire/conf.c Changed
223
 
1
@@ -634,8 +634,10 @@
2
                match++;
3
                pw_log_debug("'%s' match '%s' < > '%.*s'", key, str, len, value);
4
            }
5
-           else
6
+           else {
7
                fail++;
8
+               break;
9
+           }
10
        }
11
        if (match > 0 && fail == 0)
12
            return true;
13
@@ -931,17 +933,16 @@
14
 
15
 
16
 SPA_EXPORT
17
-int pw_context_conf_section_for_each(struct pw_context *context, const char *section,
18
+int pw_conf_section_for_each(const struct spa_dict *conf, const char *section,
19
        int (*callback) (void *data, const char *location, const char *section,
20
            const char *str, size_t len),
21
        void *data)
22
 {
23
-   struct pw_properties *conf = context->conf;
24
    const char *path = NULL;
25
    const struct spa_dict_item *it;
26
    int res = 0;
27
 
28
-   spa_dict_for_each(it, &conf->dict) {
29
+   spa_dict_for_each(it, conf) {
30
        if (spa_strendswith(it->key, "config.path")) {
31
            path = it->value;
32
            continue;
33
@@ -961,31 +962,6 @@
34
    return res;
35
 }
36
 
37
-SPA_EXPORT
38
-int pw_context_parse_conf_section(struct pw_context *context,
39
-       struct pw_properties *conf, const char *section)
40
-{
41
-   struct data data = { .context = context };
42
-   int res;
43
-
44
-   if (spa_streq(section, "context.spa-libs"))
45
-       res = pw_context_conf_section_for_each(context, section,
46
-               parse_spa_libs, &data);
47
-   else if (spa_streq(section, "context.modules"))
48
-       res = pw_context_conf_section_for_each(context, section,
49
-               parse_modules, &data);
50
-   else if (spa_streq(section, "context.objects"))
51
-       res = pw_context_conf_section_for_each(context, section,
52
-               parse_objects, &data);
53
-   else if (spa_streq(section, "context.exec"))
54
-       res = pw_context_conf_section_for_each(context, section,
55
-               parse_exec, &data);
56
-   else
57
-       res = -EINVAL;
58
-
59
-   return res == 0 ? data.count : res;
60
-}
61
-
62
 static int update_props(void *user_data, const char *location, const char *key,
63
            const char *val, size_t len)
64
 {
65
@@ -994,6 +970,27 @@
66
    return 0;
67
 }
68
 
69
+SPA_EXPORT
70
+int pw_conf_section_update_props(const struct spa_dict *conf,
71
+       const char *section, struct pw_properties *props)
72
+{
73
+   struct data data = { .props = props };
74
+   int res;
75
+   const char *str;
76
+
77
+   res = pw_conf_section_for_each(conf, section,
78
+           update_props, &data);
79
+
80
+   str = pw_properties_get(props, "config.ext");
81
+   if (res == 0 && str != NULL) {
82
+       char key128;
83
+       snprintf(key, sizeof(key), "%s.%s", section, str);
84
+       res = pw_conf_section_for_each(conf, key,
85
+               update_props, &data);
86
+   }
87
+   return res == 0 ? data.count : res;
88
+}
89
+
90
 static int try_load_conf(const char *conf_prefix, const char *conf_name,
91
             struct pw_properties *conf)
92
 {
93
@@ -1026,13 +1023,12 @@
94
    conf_name = getenv("PIPEWIRE_CONFIG_NAME");
95
    if ((res = try_load_conf(conf_prefix, conf_name, conf)) < 0) {
96
        conf_name = pw_properties_get(props, PW_KEY_CONFIG_NAME);
97
-       if ((res = try_load_conf(conf_prefix, conf_name, conf)) < 0) {
98
+       if (conf_name == NULL)
99
            conf_name = "client.conf";
100
-           if ((res = try_load_conf(conf_prefix, conf_name, conf)) < 0) {
101
-               pw_log_error("can't load default config %s: %s",
102
-                   conf_name, spa_strerror(res));
103
-               return res;
104
-           }
105
+       if ((res = try_load_conf(conf_prefix, conf_name, conf)) < 0) {
106
+           pw_log_error("can't load config %s: %s",
107
+               conf_name, spa_strerror(res));
108
+           return res;
109
        }
110
    }
111
 
112
@@ -1063,26 +1059,6 @@
113
    return res;
114
 }
115
 
116
-SPA_EXPORT
117
-int pw_context_conf_update_props(struct pw_context *context,
118
-       const char *section, struct pw_properties *props)
119
-{
120
-   struct data data = { .context = context, .props = props };
121
-   int res;
122
-   const char *str = pw_properties_get(props, "config.ext");
123
-
124
-   res = pw_context_conf_section_for_each(context, section,
125
-           update_props, &data);
126
-   if (res == 0 && str != NULL) {
127
-       char key128;
128
-       snprintf(key, sizeof(key), "%s.%s", section, str);
129
-       res = pw_context_conf_section_for_each(context, key,
130
-               update_props, &data);
131
-   }
132
-   return res == 0 ? data.count : res;
133
-}
134
-
135
-
136
 /**
137
  * 
138
  *     {
139
@@ -1171,7 +1147,7 @@
140
 }
141
 
142
 SPA_EXPORT
143
-int pw_context_conf_section_match_rules(struct pw_context *context, const char *section,
144
+int pw_conf_section_match_rules(const struct spa_dict *conf, const char *section,
145
        const struct spa_dict *props,
146
        int (*callback) (void *data, const char *location, const char *action,
147
            const char *str, size_t len),
148
@@ -1182,15 +1158,71 @@
149
        .matched = callback,
150
        .data = data };
151
    int res;
152
-   const char *str = spa_dict_lookup(props, "config.ext");
153
+   const char *str;
154
 
155
-   res = pw_context_conf_section_for_each(context, section,
156
+   res = pw_conf_section_for_each(conf, section,
157
            match_rules, &match);
158
+
159
+   str = spa_dict_lookup(props, "config.ext");
160
    if (res == 0 && str != NULL) {
161
        char key128;
162
        snprintf(key, sizeof(key), "%s.%s", section, str);
163
-       res = pw_context_conf_section_for_each(context, key,
164
+       res = pw_conf_section_for_each(conf, key,
165
                match_rules, &match);
166
    }
167
    return res;
168
 }
169
+
170
+SPA_EXPORT
171
+int pw_context_conf_update_props(struct pw_context *context,
172
+       const char *section, struct pw_properties *props)
173
+{
174
+   return pw_conf_section_update_props(&context->conf->dict,
175
+           section, props);
176
+}
177
+
178
+SPA_EXPORT
179
+int pw_context_conf_section_for_each(struct pw_context *context, const char *section,
180
+       int (*callback) (void *data, const char *location, const char *section,
181
+           const char *str, size_t len),
182
+       void *data)
183
+{
184
+   return pw_conf_section_for_each(&context->conf->dict, section, callback, data);
185
+}
186
+
187
+
188
+SPA_EXPORT
189
+int pw_context_parse_conf_section(struct pw_context *context,
190
+       struct pw_properties *conf, const char *section)
191
+{
192
+   struct data data = { .context = context };
193
+   int res;
194
+
195
+   if (spa_streq(section, "context.spa-libs"))
196
+       res = pw_context_conf_section_for_each(context, section,
197
+               parse_spa_libs, &data);
198
+   else if (spa_streq(section, "context.modules"))
199
+       res = pw_context_conf_section_for_each(context, section,
200
+               parse_modules, &data);
201
+   else if (spa_streq(section, "context.objects"))
202
+       res = pw_context_conf_section_for_each(context, section,
203
+               parse_objects, &data);
204
+   else if (spa_streq(section, "context.exec"))
205
+       res = pw_context_conf_section_for_each(context, section,
206
+               parse_exec, &data);
207
+   else
208
+       res = -EINVAL;
209
+
210
+   return res == 0 ? data.count : res;
211
+}
212
+
213
+SPA_EXPORT
214
+int pw_context_conf_section_match_rules(struct pw_context *context, const char *section,
215
+       const struct spa_dict *props,
216
+       int (*callback) (void *data, const char *location, const char *action,
217
+           const char *str, size_t len),
218
+       void *data)
219
+{
220
+   return pw_conf_section_match_rules(&context->conf->dict, section,
221
+           props, callback, data);
222
+}
223
pipewire-0.3.69.tar.gz/src/pipewire/conf.h -> pipewire-0.3.70.tar.gz/src/pipewire/conf.h Changed
27
 
1
@@ -18,12 +18,25 @@
2
 int pw_conf_load_state(const char *prefix, const char *name, struct pw_properties *conf);
3
 int pw_conf_save_state(const char *prefix, const char *name, const struct pw_properties *conf);
4
 
5
+int pw_conf_section_update_props(const struct spa_dict *conf,
6
+       const char *section, struct pw_properties *props);
7
+
8
+int pw_conf_section_for_each(const struct spa_dict *conf, const char *section,
9
+       int (*callback) (void *data, const char *location, const char *section,
10
+           const char *str, size_t len),
11
+       void *data);
12
+
13
 int pw_conf_match_rules(const char *str, size_t len, const char *location,
14
        const struct spa_dict *props,
15
        int (*callback) (void *data, const char *location, const char *action,
16
            const char *str, size_t len),
17
        void *data);
18
 
19
+int pw_conf_section_match_rules(const struct spa_dict *conf, const char *section,
20
+       const struct spa_dict *props,
21
+       int (*callback) (void *data, const char *location, const char *action,
22
+           const char *str, size_t len),
23
+       void *data);
24
 /**
25
  * \}
26
  */
27
pipewire-0.3.69.tar.gz/src/pipewire/context.c -> pipewire-0.3.70.tar.gz/src/pipewire/context.c Changed
39
 
1
@@ -788,9 +788,6 @@
2
    struct pw_impl_port *p;
3
    struct pw_impl_link *l;
4
 
5
-   if (!node->runnable)
6
-       return 0;
7
-
8
    pw_log_debug("node %p: '%s'", node, node->name);
9
 
10
    spa_list_for_each(p, &node->input_ports, link) {
11
@@ -885,7 +882,7 @@
12
 
13
                pw_impl_link_prepare(l);
14
 
15
-               if (!l->prepared || (t != n && t->visited))
16
+               if (!l->prepared)
17
                    continue;
18
 
19
                if (!l->passive)
20
@@ -906,7 +903,7 @@
21
 
22
                pw_impl_link_prepare(l);
23
 
24
-               if (!l->prepared || (t != n && t->visited))
25
+               if (!l->prepared)
26
                    continue;
27
 
28
                if (!l->passive)
29
@@ -936,7 +933,8 @@
30
        pw_log_debug(" next node %p: '%s' runnable:%u", n, n->name, n->runnable);
31
    }
32
    spa_list_for_each(n, collect, sort_link)
33
-       run_nodes(context, n, collect);
34
+       if (!n->driver && n->runnable)
35
+           run_nodes(context, n, collect);
36
 
37
    return 0;
38
 }
39
pipewire-0.3.69.tar.gz/src/pipewire/extensions/metadata.h -> pipewire-0.3.70.tar.gz/src/pipewire/extensions/metadata.h Changed
9
 
1
@@ -80,6 +80,7 @@
2
 #define pw_metadata_clear(c)           pw_metadata_method(c,clear,0)
3
 
4
 #define PW_KEY_METADATA_NAME       "metadata.name"
5
+#define PW_KEY_METADATA_VALUES     "metadata.values"
6
 
7
 /**
8
  * \}
9
pipewire-0.3.69.tar.gz/src/pipewire/properties.c -> pipewire-0.3.70.tar.gz/src/pipewire/properties.c Changed
192
 
1
@@ -4,6 +4,8 @@
2
 
3
 #include <stdio.h>
4
 #include <stdarg.h>
5
+
6
+#include <spa/utils/ansi.h>
7
 #include <spa/utils/json.h>
8
 #include <spa/utils/string.h>
9
 
10
@@ -640,12 +642,30 @@
11
    return pw_array_get_unchecked(&impl->items, index, struct spa_dict_item)->key;
12
 }
13
 
14
-static int encode_string(FILE *f, const char *val)
15
+#define NORMAL(c)  ((c)->colors ? SPA_ANSI_RESET : "")
16
+#define LITERAL(c) ((c)->colors ? SPA_ANSI_BRIGHT_MAGENTA : "")
17
+#define NUMBER(c)  ((c)->colors ? SPA_ANSI_BRIGHT_CYAN : "")
18
+#define STRING(c)  ((c)->colors ? SPA_ANSI_BRIGHT_GREEN : "")
19
+#define KEY(c)     ((c)->colors ? SPA_ANSI_BRIGHT_BLUE : "")
20
+#define CONTAINER(c)   ((c)->colors ? SPA_ANSI_BRIGHT_YELLOW : "")
21
+
22
+struct dump_config {
23
+   FILE *file;
24
+   int indent;
25
+   const char *sep;
26
+   bool colors;
27
+   bool recurse;
28
+};
29
+
30
+static int encode_string(struct dump_config *c, const char *before,
31
+       const char *val, int size, const char *after)
32
 {
33
-   int len = 0;
34
-   len += fprintf(f, "\"");
35
-   while (*val) {
36
-       switch (*val) {
37
+   FILE *f = c->file;
38
+   int i, len = 0;
39
+   len += fprintf(f, "%s\"", before);
40
+   for (i = 0; i < size; i++) {
41
+       char v = vali;
42
+       switch (v) {
43
        case '\n':
44
            len += fprintf(f, "\\n");
45
            break;
46
@@ -661,53 +681,117 @@
47
        case '\f':
48
            len += fprintf(f, "\\f");
49
            break;
50
-       case '\\':
51
-       case '"':
52
-           len += fprintf(f, "\\%c", *val);
53
-       break;
54
+       case '\\': case '"':
55
+           len += fprintf(f, "\\%c", v);
56
+           break;
57
        default:
58
-           if (*val > 0 && *val < 0x20)
59
-               len += fprintf(f, "\\u%04x", *val);
60
+           if (v > 0 && v < 0x20)
61
+               len += fprintf(f, "\\u%04x", v);
62
            else
63
-               len += fprintf(f, "%c", *val);
64
+               len += fprintf(f, "%c", v);
65
            break;
66
        }
67
-       val++;
68
    }
69
-   len += fprintf(f, "\"");
70
+   len += fprintf(f, "\"%s", after);
71
    return len-1;
72
 }
73
 
74
+static int dump(struct dump_config *c, int indent, struct spa_json *it, const char *value, int len)
75
+{
76
+   FILE *file = c->file;
77
+   struct spa_json sub;
78
+   int count = 0;
79
+   char key1024;
80
+
81
+   if (value == NULL || len == 0) {
82
+       fprintf(file, "%snull%s", LITERAL(c), NORMAL(c));
83
+   } else if (spa_json_is_container(value, len) && !c->recurse) {
84
+       len = spa_json_container_len(it, value, len);
85
+       fprintf(file, "%s%.*s%s", CONTAINER(c), len, value, NORMAL(c));
86
+   } else if (spa_json_is_array(value, len)) {
87
+       fprintf(file, "");
88
+       spa_json_enter(it, &sub);
89
+       indent += c->indent;
90
+       while ((len = spa_json_next(&sub, &value)) > 0) {
91
+           fprintf(file, "%s%s%*s", count++ > 0 ? "," : "",
92
+                   c->sep, indent, "");
93
+           dump(c, indent, &sub, value, len);
94
+       }
95
+       indent -= c->indent;
96
+       fprintf(file, "%s%*s", count > 0 ? c->sep : "",
97
+               count > 0 ? indent : 0, "");
98
+   } else if (spa_json_is_object(value, len)) {
99
+       fprintf(file, "{");
100
+       spa_json_enter(it, &sub);
101
+       indent += c->indent;
102
+       while (spa_json_get_string(&sub, key, sizeof(key)) > 0) {
103
+           fprintf(file, "%s%s%*s",
104
+                   count++ > 0 ? "," : "",
105
+                   c->sep, indent, "");
106
+           encode_string(c, KEY(c), key, strlen(key), NORMAL(c));
107
+           fprintf(file, ": ");
108
+           if ((len = spa_json_next(&sub, &value)) <= 0)
109
+               break;
110
+           dump(c, indent, &sub, value, len);
111
+       }
112
+       indent -= c->indent;
113
+       fprintf(file, "%s%*s}", count > 0 ? c->sep : "",
114
+               count > 0 ? indent : 0, "");
115
+   } else if (spa_json_is_null(value, len) ||
116
+       spa_json_is_bool(value, len)) {
117
+       fprintf(file, "%s%.*s%s", LITERAL(c), len, value, NORMAL(c));
118
+   } else if (spa_json_is_int(value, len) ||
119
+       spa_json_is_float(value, len)) {
120
+       fprintf(file, "%s%.*s%s", NUMBER(c), len, value, NORMAL(c));
121
+   } else if (spa_json_is_string(value, len)) {
122
+       fprintf(file, "%s%.*s%s", STRING(c), len, value, NORMAL(c));
123
+   } else {
124
+       encode_string(c, STRING(c), value, len, NORMAL(c));
125
+   }
126
+   return 0;
127
+}
128
+
129
 SPA_EXPORT
130
 int pw_properties_serialize_dict(FILE *f, const struct spa_dict *dict, uint32_t flags)
131
 {
132
    const struct spa_dict_item *it;
133
    int count = 0;
134
-   char key1024;
135
+   struct dump_config cfg = {
136
+       .file = f,
137
+       .indent = flags & PW_PROPERTIES_FLAG_NL ? 2 : 0,
138
+       .sep = flags & PW_PROPERTIES_FLAG_NL ? "\n" : " ",
139
+       .colors = SPA_FLAG_IS_SET(flags, PW_PROPERTIES_FLAG_COLORS),
140
+       .recurse = SPA_FLAG_IS_SET(flags, PW_PROPERTIES_FLAG_RECURSE),
141
+   }, *c = &cfg;
142
+   const char *enc = flags & PW_PROPERTIES_FLAG_ARRAY ? "" : "{}";
143
+
144
+   if (SPA_FLAG_IS_SET(flags, PW_PROPERTIES_FLAG_ENCLOSE))
145
+       fprintf(f, "%c", enc0);
146
 
147
    spa_dict_for_each(it, dict) {
148
-       size_t len = it->value ? strlen(it->value) : 0;
149
+       char key1024;
150
+       int len;
151
+       const char *value;
152
+       struct spa_json sub;
153
 
154
-       if (spa_json_encode_string(key, sizeof(key)-1, it->key) >= (int)sizeof(key)-1)
155
-           continue;
156
+       fprintf(f, "%s%s%*s", count == 0 ? "" : ",", c->sep, c->indent, "");
157
 
158
-       fprintf(f, "%s%s %s: ",
159
-               count == 0 ? "" : ",",
160
-               flags & PW_PROPERTIES_FLAG_NL ? "\n" : "",
161
-               key);
162
-
163
-       if (it->value == NULL) {
164
-           fprintf(f, "null");
165
-       } else if (spa_json_is_null(it->value, len) ||
166
-           spa_json_is_float(it->value, len) ||
167
-           spa_json_is_bool(it->value, len) ||
168
-           spa_json_is_container(it->value, len) ||
169
-           spa_json_is_string(it->value, len)) {
170
-           fprintf(f, "%s", it->value);
171
-       } else {
172
-           encode_string(f, it->value);
173
+       if (!(flags & PW_PROPERTIES_FLAG_ARRAY)) {
174
+           if (spa_json_encode_string(key, sizeof(key)-1, it->key) >= (int)sizeof(key)-1)
175
+               continue;
176
+           fprintf(f, "%s%s%s: ", KEY(c), key, NORMAL(c));
177
        }
178
+       value = it->value;
179
+
180
+       len = value ? strlen(value) : 0;
181
+       spa_json_init(&sub, value, len);
182
+       if ((len = spa_json_next(&sub, &value)) < 0)
183
+           break;
184
+
185
+       dump(c, c->indent, &sub, value, len);
186
        count++;
187
    }
188
+   if (SPA_FLAG_IS_SET(flags, PW_PROPERTIES_FLAG_ENCLOSE))
189
+       fprintf(f, "%s%c", c->sep, enc1);
190
    return count;
191
 }
192
pipewire-0.3.69.tar.gz/src/pipewire/properties.h -> pipewire-0.3.70.tar.gz/src/pipewire/properties.h Changed
14
 
1
@@ -136,7 +136,11 @@
2
 const char *
3
 pw_properties_iterate(const struct pw_properties *properties, void **state);
4
 
5
-#define PW_PROPERTIES_FLAG_NL  (1<<0)
6
+#define PW_PROPERTIES_FLAG_NL      (1<<0)
7
+#define PW_PROPERTIES_FLAG_RECURSE (1<<1)
8
+#define PW_PROPERTIES_FLAG_ENCLOSE (1<<2)
9
+#define PW_PROPERTIES_FLAG_ARRAY   (1<<3)
10
+#define PW_PROPERTIES_FLAG_COLORS  (1<<4)
11
 int pw_properties_serialize_dict(FILE *f, const struct spa_dict *dict, uint32_t flags);
12
 
13
 static inline bool pw_properties_parse_bool(const char *value) {
14
pipewire-0.3.69.tar.gz/src/pipewire/stream.c -> pipewire-0.3.70.tar.gz/src/pipewire/stream.c Changed
58
 
1
@@ -156,7 +156,7 @@
2
    unsigned int driving:1;
3
    unsigned int using_trigger:1;
4
    unsigned int trigger:1;
5
-   int in_set_control;
6
+   int in_set_param;
7
 };
8
 
9
 static int get_param_index(uint32_t id)
10
@@ -566,7 +566,7 @@
11
    if (id != SPA_PARAM_Props)
12
        return -ENOTSUP;
13
 
14
-   if (impl->in_set_control == 0)
15
+   if (impl->in_set_param == 0)
16
        pw_stream_emit_param_changed(stream, id, param);
17
 
18
    return 0;
19
@@ -2138,6 +2138,27 @@
20
    return res;
21
 }
22
 
23
+static inline int stream_set_param(struct stream *impl, uint32_t id, const struct spa_pod *param)
24
+{
25
+   int res = 0;
26
+   impl->in_set_param++;
27
+   res = pw_impl_node_set_param(impl->node, id, 0, param);
28
+   impl->in_set_param--;
29
+   return res;
30
+}
31
+
32
+SPA_EXPORT
33
+int pw_stream_set_param(struct pw_stream *stream, uint32_t id, const struct spa_pod *param)
34
+{
35
+   struct stream *impl = SPA_CONTAINER_OF(stream, struct stream, this);
36
+   ensure_loop(impl->context->main_loop, return -EIO);
37
+
38
+   if (impl->node == NULL)
39
+       return -EIO;
40
+
41
+   return stream_set_param(impl, id, param);
42
+}
43
+
44
 SPA_EXPORT
45
 int pw_stream_set_control(struct pw_stream *stream, uint32_t id, uint32_t n_values, float *values, ...)
46
 {
47
@@ -2194,9 +2215,7 @@
48
 
49
    va_end(varargs);
50
 
51
-   impl->in_set_control++;
52
-   pw_impl_node_set_param(impl->node, SPA_PARAM_Props, 0, pod);
53
-   impl->in_set_control--;
54
+   stream_set_param(impl, SPA_PARAM_Props, pod);
55
 
56
    return 0;
57
 }
58
pipewire-0.3.69.tar.gz/src/pipewire/stream.h -> pipewire-0.3.70.tar.gz/src/pipewire/stream.h Changed
30
 
1
@@ -447,12 +447,7 @@
2
            const char *error,      /**< an error message */
3
            ...) SPA_PRINTF_FUNC(3, 4);
4
 
5
-/** Complete the negotiation process with result code \a res
6
- *
7
- * This function should be called after notification of the format.
8
-
9
- * When \a res indicates success, \a params contain the parameters for the
10
- * allocation state.  */
11
+/** Update the param exposed on the stream. */
12
 int
13
 pw_stream_update_params(struct pw_stream *stream,  /**< a \ref pw_stream */
14
            const struct spa_pod **params,  /**< an array of params. The params should
15
@@ -460,6 +455,14 @@
16
                              *  buffer allocation. */
17
            uint32_t n_params       /**< number of elements in \a params */);
18
 
19
+/**
20
+ * Set a parameter on the stream. This is like pw_stream_set_control() but with
21
+ * a complete spa_pod param. It can also be called from the param_changed event handler
22
+ * to intercept and modify the param for the adapter. Since 0.3.70 */
23
+int pw_stream_set_param(struct pw_stream *stream,  /**< a \ref pw_stream */
24
+           uint32_t id,            /**< the id of the param */
25
+           const struct spa_pod *param /**< the params to set */);
26
+
27
 /** Get control values */
28
 const struct pw_stream_control *pw_stream_get_control(struct pw_stream *stream, uint32_t id);
29
 
30
pipewire-0.3.69.tar.gz/src/tools/meson.build -> pipewire-0.3.70.tar.gz/src/tools/meson.build Changed
8
 
1
@@ -1,5 +1,6 @@
2
 tools_sources = 
3
    'pw-mon',  'pw-mon.c'  ,
4
+   'pw-config',  'pw-config.c'  ,
5
    'pw-dot',  'pw-dot.c'  ,
6
    'pw-dump',  'pw-dump.c'  ,
7
    'pw-profiler',  'pw-profiler.c'  ,
8
pipewire-0.3.70.tar.gz/src/tools/pw-config.c Added
256
 
1
@@ -0,0 +1,254 @@
2
+/* PipeWire */
3
+/* SPDX-FileCopyrightText: Copyright © 2023 Wim Taymans */
4
+/* SPDX-License-Identifier: MIT */
5
+
6
+#include "config.h"
7
+
8
+#include <getopt.h>
9
+#include <signal.h>
10
+#include <locale.h>
11
+#include <unistd.h>
12
+
13
+#include <spa/utils/result.h>
14
+#include <spa/utils/json.h>
15
+
16
+#include "pipewire/pipewire.h"
17
+#include "pipewire/log.h"
18
+
19
+#define DEFAULT_NAME   "pipewire.conf"
20
+#define DEFAULT_PREFIX ""
21
+
22
+struct data {
23
+   const char *opt_name;
24
+   const char *opt_prefix;
25
+   const char *opt_cmd;
26
+   bool opt_recurse;
27
+   bool opt_newline;
28
+   bool opt_colors;
29
+   struct pw_properties *conf;
30
+   struct pw_properties *assemble;
31
+   int count;
32
+   bool array;
33
+};
34
+
35
+static void print_all_properties(struct data *d, struct pw_properties *props)
36
+{
37
+   pw_properties_serialize_dict(stdout,
38
+           &props->dict,
39
+           (d->opt_newline ? PW_PROPERTIES_FLAG_NL : 0) |
40
+           (d->opt_recurse ? PW_PROPERTIES_FLAG_RECURSE : 0) |
41
+           (d->opt_colors ? PW_PROPERTIES_FLAG_COLORS : 0) |
42
+           (d->array ? PW_PROPERTIES_FLAG_ARRAY : 0) |
43
+           PW_PROPERTIES_FLAG_ENCLOSE);
44
+   fprintf(stdout, "\n");
45
+}
46
+
47
+static void list_paths(struct data *d)
48
+{
49
+   const struct spa_dict_item *it;
50
+
51
+   spa_dict_for_each(it, &d->conf->dict) {
52
+       if (spa_strstartswith(it->key, "config.path")) {
53
+           pw_properties_set(d->assemble, it->key, it->value);
54
+       }
55
+       if (spa_strstartswith(it->key, "override.") &&
56
+           spa_strendswith(it->key, ".config.path")) {
57
+           pw_properties_set(d->assemble, it->key, it->value);
58
+       }
59
+   }
60
+}
61
+
62
+static int do_merge_section(void *data, const char *location, const char *section,
63
+           const char *str, size_t len)
64
+{
65
+   struct data *d = data;
66
+   struct spa_json it2;
67
+   int l;
68
+   const char *value;
69
+
70
+        spa_json_init(&it0, str, len);
71
+   if ((l = spa_json_next(&it0, &value)) <= 0)
72
+       return 0;
73
+
74
+   if (spa_json_is_array(value, l)) {
75
+       char key128;
76
+
77
+       spa_json_enter(&it0, &it1);
78
+       while ((l = spa_json_next(&it1, &value)) > 0) {
79
+           if (spa_json_is_container(value, l))
80
+               l = spa_json_container_len(&it1, value, l);
81
+
82
+           snprintf(key, sizeof(key), "%d-%s", d->count++, location);
83
+           pw_properties_setf(d->assemble, key, "%.*s", l, value);
84
+       }
85
+       d->array = true;
86
+   }
87
+   else if (spa_json_is_object(value, l)) {
88
+       pw_properties_update_string(d->assemble, str, len);
89
+   }
90
+   return 0;
91
+}
92
+
93
+static int do_list_section(void *data, const char *location, const char *section,
94
+           const char *str, size_t len)
95
+{
96
+   struct data *d = data;
97
+   char key128;
98
+   snprintf(key, sizeof(key), "%d-%s", d->count++, location);
99
+   pw_properties_setf(d->assemble, key, "%.*s", (int)len, str);
100
+   return 0;
101
+}
102
+
103
+static void section_for_each(struct data *d, const char *section,
104
+       int (*callback) (void *data, const char *location, const char *section,
105
+           const char *str, size_t len))
106
+{
107
+   const char *str;
108
+   char key128;
109
+
110
+   pw_conf_section_for_each(&d->conf->dict, section, callback, d);
111
+   str = pw_properties_get(d->assemble, "config.ext");
112
+   if (str != NULL) {
113
+       snprintf(key, sizeof(key), "%s.%s", section, str);
114
+       pw_conf_section_for_each(&d->conf->dict, key, callback, d);
115
+   }
116
+}
117
+
118
+static void show_help(const char *name, bool error)
119
+{
120
+        fprintf(error ? stderr : stdout, "%1$s : PipeWire config manager.\n"
121
+       "Usage:\n"
122
+       "  %1$s options paths                  List config paths (default action)\n"
123
+       "  %1$s options list SECTION         List config section\n"
124
+       "  %1$s options merge SECTION          Merge a config section\n\n"
125
+       "Options:\n"
126
+       "  -h, --help                            Show this help\n"
127
+       "      --version                         Show version\n"
128
+       "  -n, --name                            Config Name (default '%2$s')\n"
129
+       "  -p, --prefix                          Config Prefix (default '%3$s')\n"
130
+       "  -L, --no-newline                      Omit newlines after values\n"
131
+       "  -r, --recurse                         Reformat config sections recursively\n"
132
+       "  -N, --no-colors                       disable color output\n"
133
+       "  -C, --color=WHEN                    whether to enable color support. WHEN is `never`, `always`, or `auto`\n",
134
+       name, DEFAULT_NAME, DEFAULT_PREFIX);
135
+}
136
+
137
+int main(int argc, char *argv)
138
+{
139
+   struct data d = { 0, };
140
+   struct pw_properties *props = NULL;
141
+   int res = 0, c;
142
+   static const struct option long_options = {
143
+       { "help",   no_argument,        NULL, 'h' },
144
+       { "version",    no_argument,        NULL, 'V' },
145
+       { "name",   required_argument,  NULL, 'n' },
146
+       { "prefix", required_argument,  NULL, 'p' },
147
+       { "no-newline", no_argument,        NULL, 'L' },
148
+       { "recurse",    no_argument,        NULL, 'r' },
149
+       { "no-colors",  no_argument,        NULL, 'N' },
150
+       { "color",  optional_argument,  NULL, 'C' },
151
+       { NULL, 0, NULL, 0}
152
+   };
153
+
154
+   d.opt_name = DEFAULT_NAME;
155
+   d.opt_prefix = NULL;
156
+   d.opt_recurse = false;
157
+   d.opt_newline = true;
158
+   if (isatty(fileno(stdout)) && getenv("NO_COLOR") == NULL)
159
+       d.opt_colors = true;
160
+   d.opt_cmd = "paths";
161
+
162
+   pw_init(&argc, &argv);
163
+
164
+   while ((c = getopt_long(argc, argv, "hVn:p:LrNC", long_options, NULL)) != -1) {
165
+       switch (c) {
166
+       case 'h':
167
+           show_help(argv0, false);
168
+           return 0;
169
+       case 'V':
170
+           printf("%s\n"
171
+               "Compiled with libpipewire %s\n"
172
+               "Linked with libpipewire %s\n",
173
+               argv0,
174
+               pw_get_headers_version(),
175
+               pw_get_library_version());
176
+           return 0;
177
+       case 'n':
178
+           d.opt_name = optarg;
179
+           break;
180
+       case 'p':
181
+           d.opt_prefix = optarg;
182
+           break;
183
+       case 'L':
184
+           d.opt_newline = false;
185
+           break;
186
+       case 'r':
187
+           d.opt_recurse = true;
188
+           break;
189
+       case 'N' :
190
+           d.opt_colors = false;
191
+           break;
192
+       case 'C' :
193
+           if (optarg == NULL || !strcmp(optarg, "auto"))
194
+               break; /* nothing to do, tty detection was done
195
+                     before parsing options */
196
+           else if (!strcmp(optarg, "never"))
197
+               d.opt_colors = false;
198
+           else if (!strcmp(optarg, "always"))
199
+               d.opt_colors = true;
200
+           else {
201
+               fprintf(stderr, "Unknown color: %s\n", optarg);
202
+               show_help(argv0, true);
203
+               return -1;
204
+           }
205
+           break;
206
+       default:
207
+           show_help(argv0, true);
208
+           return -1;
209
+       }
210
+   }
211
+
212
+   if (optind < argc)
213
+       d.opt_cmd = argvoptind++;
214
+
215
+   props = pw_properties_new(
216
+           PW_KEY_CONFIG_NAME, d.opt_name,
217
+           PW_KEY_CONFIG_PREFIX, d.opt_prefix,
218
+           NULL);
219
+
220
+   d.conf = pw_properties_new(NULL, NULL);
221
+   if ((res = pw_conf_load_conf_for_context (props, d.conf)) < 0) {
222
+       fprintf(stderr, "error loading config: %s\n", spa_strerror(res));
223
+       goto done;
224
+   }
225
+
226
+   d.assemble = pw_properties_new(NULL, NULL);
227
+
228
+   if (spa_streq(d.opt_cmd, "paths")) {
229
+       list_paths(&d);
230
+   }
231
+   else if (spa_streq(d.opt_cmd, "list")) {
232
+       if (optind == argc) {
233
+           pw_properties_update(d.assemble, &d.conf->dict);
234
+       } else {
235
+           section_for_each(&d, argvoptind++, do_list_section);
236
+       }
237
+   }
238
+   else if (spa_streq(d.opt_cmd, "merge")) {
239
+       if (optind == argc) {
240
+           fprintf(stderr, "%s requires a section\n", d.opt_cmd);
241
+           res = -EINVAL;
242
+           goto done;
243
+       }
244
+       section_for_each(&d, argvoptind++, do_merge_section);
245
+   }
246
+   print_all_properties(&d, d.assemble);
247
+
248
+done:
249
+   pw_properties_free(d.conf);
250
+   pw_properties_free(d.assemble);
251
+   pw_properties_free(props);
252
+
253
+   pw_deinit();
254
+   return res;
255
+}
256
pipewire-0.3.69.tar.gz/src/tools/pw-metadata.c -> pipewire-0.3.70.tar.gz/src/tools/pw-metadata.c Changed
92
 
1
@@ -21,6 +21,7 @@
2
 
3
    const char *opt_remote;
4
    const char *opt_name;
5
+   bool opt_list;
6
    bool opt_monitor;
7
    bool opt_delete;
8
    uint32_t opt_id;
9
@@ -48,6 +49,9 @@
10
 {
11
    struct data *d = data;
12
 
13
+   if (d->opt_list)
14
+       return 0;
15
+
16
    if ((d->opt_id == SPA_ID_INVALID || d->opt_id == id) &&
17
        (d->opt_key == NULL || spa_streq(d->opt_key, key))) {
18
        if (key == NULL) {
19
@@ -72,22 +76,30 @@
20
                  const struct spa_dict *props)
21
 {
22
    struct data *d = data;
23
-   const char *str;
24
+   const char *name;
25
 
26
    if (!spa_streq(type, PW_TYPE_INTERFACE_Metadata))
27
        return;
28
 
29
-   if (props != NULL &&
30
-       (str = spa_dict_lookup(props, PW_KEY_METADATA_NAME)) != NULL &&
31
-       !spa_streq(str, d->opt_name))
32
+   if (props == NULL)
33
+       return;
34
+
35
+   name = spa_dict_lookup(props, PW_KEY_METADATA_NAME);
36
+   if (name == NULL)
37
+       return;
38
+
39
+   if (d->opt_name && !spa_streq(name, d->opt_name))
40
        return;
41
 
42
-   if (d->metadata != NULL) {
43
+   if (!d->opt_list && d->metadata != NULL) {
44
        pw_log_warn("Multiple metadata: ignoring metadata %d", id);
45
        return;
46
    }
47
 
48
-   printf("Found \"%s\" metadata %d\n", d->opt_name, id);
49
+   printf("Found \"%s\" metadata %d\n", name, id);
50
+   if (d->opt_list)
51
+       return;
52
+
53
    d->metadata = pw_registry_bind(d->registry,
54
            id, type, PW_VERSION_METADATA, 0);
55
 
56
@@ -157,6 +169,7 @@
57
        "  -h, --help                            Show this help\n"
58
        "      --version                         Show version\n"
59
        "  -r, --remote                          Remote daemon name\n"
60
+       "  -l, --list                            List available metadata\n"
61
        "  -m, --monitor                         Monitor metadata\n"
62
        "  -d, --delete                          Delete metadata\n"
63
        "  -n, --name                            Metadata name (default: \"%s\")\n",
64
@@ -171,6 +184,7 @@
65
        { "help",   no_argument,        NULL, 'h' },
66
        { "version",    no_argument,        NULL, 'V' },
67
        { "remote", required_argument,  NULL, 'r' },
68
+       { "list",   no_argument,        NULL, 'l' },
69
        { "monitor",    no_argument,        NULL, 'm' },
70
        { "delete", no_argument,        NULL, 'd' },
71
        { "name",   required_argument,  NULL, 'n' },
72
@@ -184,7 +198,7 @@
73
 
74
    data.opt_name = "default";
75
 
76
-   while ((c = getopt_long(argc, argv, "hVr:mdn:", long_options, NULL)) != -1) {
77
+   while ((c = getopt_long(argc, argv, "hVr:lmdn:", long_options, NULL)) != -1) {
78
        switch (c) {
79
        case 'h':
80
            show_help(&data, argv0, false);
81
@@ -200,6 +214,10 @@
82
        case 'r':
83
            data.opt_remote = optarg;
84
            break;
85
+       case 'l':
86
+           data.opt_name = NULL;
87
+           data.opt_list = true;
88
+           break;
89
        case 'm':
90
            data.opt_monitor = true;
91
            break;
92
Refresh

No build results available

Refresh

No rpmlint results available

Request History
Bjørn Lie's avatar

zaitor created request almost 2 years ago

New upstream release


Bjørn Lie's avatar

zaitor accepted request almost 2 years ago

Xin