Overview

Request 5692 (accepted)

New upstream release

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

pipewire-aptx.changes Changed
x
 
1
@@ -1,4 +1,9 @@
2
 -------------------------------------------------------------------
3
+Mon Mar 13 12:30:13 UTC 2023 - Bjørn Lie <zaitor@opensuse.org>
4
+
5
+- Update to version 0.3.66
6
+
7
+-------------------------------------------------------------------
8
 Thu Jan 26 20:45:06 UTC 2023 - Bjørn Lie <zaitor@opensuse.org>
9
 
10
 - Update to version 0.3.65
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.65
6
+Version:        0.3.66
7
 Release:        0
8
 Summary:        PipeWire Bluetooth aptX codec plugin
9
 License:        MIT
10
pipewire-0.3.65.tar.gz/spa/plugins/libcamera/libcamera-client.c Deleted
249
 
1
@@ -1,247 +0,0 @@
2
-/* Spa libcamera client
3
- *
4
- * Copyright (C) 2020, Collabora Ltd.
5
- *     Author: Raghavendra Rao Sidlagatta <raghavendra.rao@collabora.com>
6
- *
7
- * libcamera-client.c
8
- *
9
- * Permission is hereby granted, free of charge, to any person obtaining a
10
- * copy of this software and associated documentation files (the "Software"),
11
- * to deal in the Software without restriction, including without limitation
12
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
13
- * and/or sell copies of the Software, and to permit persons to whom the
14
- * Software is furnished to do so, subject to the following conditions:
15
- *
16
- * The above copyright notice and this permission notice (including the next
17
- * paragraph) shall be included in all copies or substantial portions of the
18
- * Software.
19
- *
20
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
23
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
25
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
26
- * DEALINGS IN THE SOFTWARE.
27
- */
28
-
29
-#include <errno.h>
30
-#include <stddef.h>
31
-#include <stdio.h>
32
-#include <sys/types.h>
33
-#include <sys/stat.h>
34
-#include <fcntl.h>
35
-
36
-#include <spa/support/log.h>
37
-#include <spa/support/loop.h>
38
-#include <spa/support/plugin.h>
39
-#include <spa/utils/type.h>
40
-#include <spa/utils/keys.h>
41
-#include <spa/utils/names.h>
42
-#include <spa/utils/string.h>
43
-#include <spa/monitor/device.h>
44
-#include <spa/monitor/utils.h>
45
-
46
-#include "libcamera.h"
47
-
48
-struct impl {
49
-   struct spa_handle handle;
50
-   struct spa_device device;
51
-
52
-   struct spa_log *log;
53
-   struct spa_loop *main_loop;
54
-
55
-   struct spa_hook_list hooks;
56
-
57
-   uint64_t info_all;
58
-   struct spa_device_info info;
59
-
60
-   struct spa_source source;
61
-};
62
-
63
-static int emit_object_info(struct impl *this, uint32_t id)
64
-{
65
-   struct spa_device_object_info info;
66
-   struct spa_dict_item items20;
67
-   uint32_t n_items = 0;
68
-
69
-   info = SPA_DEVICE_OBJECT_INFO_INIT();
70
-
71
-   info.type = SPA_TYPE_INTERFACE_Device;
72
-   info.factory_name = SPA_NAME_API_LIBCAMERA_DEVICE;
73
-   info.change_mask = (SPA_DEVICE_OBJECT_CHANGE_MASK_FLAGS |
74
-       SPA_DEVICE_OBJECT_CHANGE_MASK_PROPS);
75
-   info.flags = 0;
76
-
77
-   itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_DEVICE_ENUM_API,"libcamera-client");
78
-   itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_DEVICE_API, "libcamera");
79
-   itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_MEDIA_CLASS, "Video/Device");
80
-
81
-   info.props = &SPA_DICT_INIT(items, n_items);
82
-   spa_device_emit_object_info(&this->hooks, id, &info);
83
-
84
-   return 1;
85
-}
86
-
87
-static const struct spa_dict_item device_info_items = {
88
-   { SPA_KEY_DEVICE_API, "libcamera" },
89
-   { SPA_KEY_DEVICE_NICK, "libcamera-client" },
90
-   { SPA_KEY_API_UDEV_MATCH, "libcamera" },
91
-};
92
-
93
-
94
-static void emit_device_info(struct impl *this, bool full)
95
-{
96
-   uint64_t old = full ? this->info.change_mask : 0;
97
-   if (full)
98
-       this->info.change_mask = this->info_all;
99
-   if (this->info.change_mask) {
100
-       this->info.props = &SPA_DICT_INIT_ARRAY(device_info_items);
101
-       spa_device_emit_info(&this->hooks, &this->info);
102
-       this->info.change_mask = old;
103
-   }
104
-}
105
-
106
-static void impl_hook_removed(struct spa_hook *hook)
107
-{
108
-   return;
109
-}
110
-
111
-static int
112
-impl_device_add_listener(void *object, struct spa_hook *listener,
113
-       const struct spa_device_events *events, void *data)
114
-{
115
-   struct impl *this = object;
116
-   struct spa_hook_list save;
117
-
118
-   spa_return_val_if_fail(this != NULL, -EINVAL);
119
-   spa_return_val_if_fail(events != NULL, -EINVAL);
120
-
121
-   spa_hook_list_isolate(&this->hooks, &save, listener, events, data);
122
-
123
-   emit_device_info(this, true);
124
-
125
-   emit_object_info(this, 0);
126
-
127
-   spa_hook_list_join(&this->hooks, &save);
128
-
129
-   listener->removed = impl_hook_removed;
130
-   listener->priv = this;
131
-
132
-   return 0;
133
-}
134
-
135
-static const struct spa_device_methods impl_device = {
136
-   SPA_VERSION_DEVICE_METHODS,
137
-   .add_listener = impl_device_add_listener,
138
-};
139
-
140
-static int impl_get_interface(struct spa_handle *handle, const char *type, void **interface)
141
-{
142
-   struct impl *this;
143
-
144
-   spa_return_val_if_fail(handle != NULL, -EINVAL);
145
-   spa_return_val_if_fail(interface != NULL, -EINVAL);
146
-
147
-   this = (struct impl *) handle;
148
-
149
-   if (spa_streq(type, SPA_TYPE_INTERFACE_Device))
150
-       *interface = &this->device;
151
-   else
152
-       return -ENOENT;
153
-
154
-   return 0;
155
-}
156
-
157
-static int impl_clear(struct spa_handle *handle)
158
-{
159
-   struct impl *this = (struct impl *) handle;
160
-
161
-   if(this->dev.camera) {
162
-       deleteLibCamera(this->dev.camera);
163
-       this->dev.camera = NULL;
164
-   }
165
-   return 0;
166
-}
167
-
168
-static size_t
169
-impl_get_size(const struct spa_handle_factory *factory,
170
-         const struct spa_dict *params)
171
-{
172
-   return sizeof(struct impl);
173
-}
174
-
175
-static int
176
-impl_init(const struct spa_handle_factory *factory,
177
-     struct spa_handle *handle,
178
-     const struct spa_dict *info,
179
-     const struct spa_support *support,
180
-     uint32_t n_support)
181
-{
182
-   struct impl *this;
183
-
184
-   spa_return_val_if_fail(factory != NULL, -EINVAL);
185
-   spa_return_val_if_fail(handle != NULL, -EINVAL);
186
-
187
-   handle->get_interface = impl_get_interface;
188
-   handle->clear = impl_clear;
189
-
190
-   this = (struct impl *) handle;
191
-
192
-   this->log = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_Log);
193
-   libcamera_log_topic_init(this->log);
194
-
195
-   this->main_loop = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_Loop);
196
-
197
-   if (this->main_loop == NULL) {
198
-       spa_log_error(this->log, "a main-loop is needed");
199
-       return -EINVAL;
200
-   }
201
-   spa_hook_list_init(&this->hooks);
202
-
203
-   this->device.iface = SPA_INTERFACE_INIT(
204
-           SPA_TYPE_INTERFACE_Device,
205
-           SPA_VERSION_DEVICE,
206
-           &impl_device, this);
207
-
208
-   this->info = SPA_DEVICE_INFO_INIT();
209
-   this->info_all = SPA_DEVICE_CHANGE_MASK_FLAGS |
210
-           SPA_DEVICE_CHANGE_MASK_PROPS;
211
-   this->info.flags = 0;
212
-
213
-   if(this->dev.camera == NULL)
214
-       this->dev.camera = (LibCamera*)newLibCamera();
215
-   if(this->dev.camera != NULL)
216
-       libcamera_set_log(this->dev.camera, this->dev.log);
217
-
218
-   return 0;
219
-}
220
-
221
-static const struct spa_interface_info impl_interfaces = {
222
-   {SPA_TYPE_INTERFACE_Device,},
223
-};
224
-
225
-static int
226
-impl_enum_interface_info(const struct spa_handle_factory *factory,
227
-            const struct spa_interface_info **info,
228
-            uint32_t *index)
229
-{
230
-   spa_return_val_if_fail(factory != NULL, -EINVAL);
231
-   spa_return_val_if_fail(info != NULL, -EINVAL);
232
-   spa_return_val_if_fail(index != NULL, -EINVAL);
233
-
234
-   if (*index >= SPA_N_ELEMENTS(impl_interfaces))
235
-       return 0;
236
-
237
-   *info = &impl_interfaces(*index)++;
238
-   return 1;
239
-}
240
-
241
-const struct spa_handle_factory spa_libcamera_client_factory = {
242
-   SPA_VERSION_HANDLE_FACTORY,
243
-   SPA_NAME_API_LIBCAMERA_ENUM_CLIENT,
244
-   NULL,
245
-   impl_get_size,
246
-   impl_init,
247
-   impl_enum_interface_info,
248
-};
249
pipewire-0.3.65.tar.gz/.gitignore -> pipewire-0.3.66.tar.gz/.gitignore Changed
13
 
1
@@ -15,6 +15,11 @@
2
 subprojects/wireplumber
3
 subprojects/media-session
4
 subprojects/packagecache
5
+subprojects/googletest*
6
+subprojects/gtest.wrap
7
+subprojects/libyaml.wrap
8
+subprojects/libyaml
9
+subprojects/libcamera
10
 
11
 # Created by https://www.gitignore.io/api/vim
12
 
13
pipewire-0.3.65.tar.gz/.gitlab-ci.yml -> pipewire-0.3.66.tar.gz/.gitlab-ci.yml Changed
18
 
1
@@ -25,7 +25,7 @@
2
 .fedora:
3
   variables:
4
     # Update this tag when you want to trigger a rebuild
5
-    FDO_DISTRIBUTION_TAG: '2022-11-07.0'
6
+    FDO_DISTRIBUTION_TAG: '2023-01-18.0'
7
     FDO_DISTRIBUTION_VERSION: '35'
8
     FDO_DISTRIBUTION_PACKAGES: >-
9
       alsa-lib-devel
10
@@ -46,6 +46,7 @@
11
       jack-audio-connection-kit-devel
12
       libcanberra-devel
13
       libldac-devel
14
+      libmysofa-devel
15
       libsndfile-devel
16
       libusb-devel
17
       lilv-devel
18
pipewire-0.3.65.tar.gz/NEWS -> pipewire-0.3.66.tar.gz/NEWS Changed
93
 
1
@@ -1,3 +1,81 @@
2
+# PipeWire 0.3.66 (2023-02-16)
3
+
4
+This is a bugfix release that is API and ABI compatible with previous
5
+0.3.x releases.
6
+
7
+## Highlights
8
+  - Fix a regression in the pulseaudio module-combine-stream because the new
9
+    module-combine-stream was not installed.
10
+  - PipeWire can now generate a limits.d config file with our recommended
11
+    settings for priorities and memlock.
12
+
13
+
14
+## PipeWire
15
+  - Avoid rate switches when the graph is idle.
16
+  - The rate selection algorithm was improved. This ensures minimal performance
17
+    and quality loss when resampling.
18
+  - The default min.quantum was set to 32 again after it got erronously changed
19
+    to (the too low) 16 in version 0.3.45.
20
+  - Fix compilation issues with rust bindings because of macros in defines.
21
+    Work around it for now. (#2952)
22
+  - Invalid file mappings are now refused (#2617 #2914 #3007)
23
+  - Modules, exec and objects can now be loaded depending on conditions. One
24
+    example is the X11-bell module that can now be disabled with a custom
25
+    property override.
26
+  - Filter now also supports _trigger_process() to drive the graph.
27
+  - TID is now added to the journald log.
28
+  - PipeWire generates and installs */etc/security/limits.d/25-pw-rlimits.conf*
29
+    that by default contains project's recommended settings. Creation of the
30
+    pipewire group is left to the distro or user ( `groupadd -r pipewire` ).
31
+    See the rlimits-* Meson options for controlling this behavior.
32
+  - Additionally there is now by default disabled Meson option that will
33
+    install */etc/security/limits.d/20-pw-defaults.conf* with the current Linux
34
+    default memlock value. Distros with only kernels >=5.16 or always using
35
+    systemd v251 or newer do not need this. But all other builds should set the
36
+    `-Dpam-defaults-install=true` Meson option to ensure that the memlock value
37
+    is always large enough. Thanks to Rickie Schroeder for pointing out that
38
+    the default Linux memlock value has been somewhat recently increased.
39
+
40
+## modules
41
+  - Install module-combine-stream.
42
+  - RTP source now has support for custom channel names.
43
+  - RTP source will now stop when inactive.
44
+  - There is now
45
+  - Filter-chain has a new mysofa based spacializer plugin.
46
+  - The RTP modules can now use direct clock timestamps to send and receive
47
+    packets. This makes it possible to synchronize sender and receiver with
48
+    a PTP clock, for example.
49
+  - Filter-chain now has an invert plugin to invert the polarity of a
50
+    signal. (#3008)
51
+
52
+## SPA
53
+  - There is now an option to set the channels used for probing Pro Audio
54
+    devices. This could unlock more samplerates for some devices when they are
55
+    probed with fewer channels. (#2990)
56
+  - Support was added for other clocks than the MONOTONIC clock in the
57
+    driver nodes. This can be used to synchronize the graph to a PTP clock,
58
+    for example.
59
+  - The ALSA source has some more headroom when rate matching to avoid
60
+    stuttering when following another driver.
61
+  - libcamera controls are now mapped to standard PipeWire property values.
62
+  - The channelmixer has seen some improvements. MONO and undefined channel
63
+    layouts are now upmixed and downmixed more correctly. (#3010)
64
+
65
+## Bluetooth
66
+  - Many BAP support fixes.
67
+
68
+## GStreamer
69
+  - The gstreamer elements now support buffer video metadata so that strides
70
+    are correctly handled.
71
+  - pipewiresrc will now error out correctly in more cases. (#2935)
72
+
73
+## JACK
74
+  - The frame to/from time functions are improved to also work with negative
75
+    time and frame offsets.
76
+
77
+Older versions:
78
+
79
+
80
 # PipeWire 0.3.65 (2023-01-26)
81
 
82
 This is a bugfix release that is API and ABI compatible with previous
83
@@ -109,9 +187,6 @@
84
     this.
85
   - pipewiresrc will now always be a live source unless told otherwise.
86
 
87
-Older versions:
88
-
89
-
90
 # PipeWire 0.3.64 (2023-01-12)
91
 
92
 This is a bugfix release that is API and ABI compatible with previous
93
pipewire-0.3.65.tar.gz/meson.build -> pipewire-0.3.66.tar.gz/meson.build Changed
18
 
1
@@ -1,5 +1,5 @@
2
 project('pipewire', 'c' ,
3
-  version : '0.3.65',
4
+  version : '0.3.66',
5
   license :  'MIT', 'LGPL-2.1-or-later', 'GPL-2.0-only' ,
6
   meson_version : '>= 0.59.0',
7
   default_options :  'warning_level=3',
8
@@ -285,6 +285,9 @@
9
 sndfile_dep = dependency('sndfile', version : '>= 1.0.20', required : get_option('sndfile'))
10
 summary({'sndfile': sndfile_dep.found()}, bool_yn: true, section: 'pw-cat/pw-play/pw-dump/filter-chain')
11
 cdata.set('HAVE_SNDFILE', sndfile_dep.found())
12
+libmysofa_dep = dependency('libmysofa', required : get_option('libmysofa'))
13
+summary({'libmysofa': libmysofa_dep.found()}, bool_yn: true, section: 'filter-chain')
14
+cdata.set('HAVE_LIBMYSOFA', libmysofa_dep.found())
15
 pulseaudio_dep = dependency('libpulse', required : get_option('libpulse'))
16
 summary({'libpulse': pulseaudio_dep.found()}, bool_yn: true, section: 'Streaming between daemons')
17
 avahi_dep = dependency('avahi-client', required : get_option('avahi'))
18
pipewire-0.3.65.tar.gz/meson_options.txt -> pipewire-0.3.66.tar.gz/meson_options.txt Changed
49
 
1
@@ -213,6 +213,10 @@
2
        description: 'Enable code that depends on libsndfile',
3
        type: 'feature',
4
        value: 'auto')
5
+option('libmysofa',
6
+       description: 'Enable code that depends on libmysofa',
7
+       type: 'feature',
8
+       value: 'auto')
9
 option('libpulse',
10
        description: 'Enable code that depends on libpulse',
11
        type: 'feature',
12
@@ -281,3 +285,36 @@
13
        description: 'Enable ALSA Compress-Offload support',
14
        type: 'feature',
15
        value: 'disabled')
16
+option('pam-defaults-install',
17
+       description: 'Install limits.d file modifying defaults for all PAM users. Only for old kernels/systemd!',
18
+       type: 'boolean',
19
+       value: 'false')
20
+option('pam-memlock-default',
21
+       description : 'The default memlock value for any PAM user in kilobytes. Multiples of 64 recommended.',
22
+       type : 'integer',
23
+       min: 640,
24
+       value: 8192)
25
+option('rlimits-install',
26
+       description: 'Install PAM limits.d file. Voids all following rlimits-* options, if false',
27
+       type: 'boolean',
28
+       value: 'true')
29
+option('rlimits-match',
30
+       description : 'PAM match rule for the generated limits.d file. @<name> denotes matching a group.',
31
+       type : 'string',
32
+       value: '@pipewire')
33
+option('rlimits-rtprio',
34
+       description : 'RR and FIFO scheduler priority permitted for realtime threads of the matching user(s)',
35
+       type : 'integer',
36
+       min: 11,
37
+       max: 99,
38
+       value: 95)
39
+option('rlimits-memlock',
40
+       description : 'kB of memory each process of the user matched by the rule can lock. Can be unlimited .',
41
+       type : 'string',
42
+       value: '4194304')
43
+option('rlimits-nice',
44
+       description : 'Not niceness permitted for non-realtime threads of the matching user(s)',
45
+       type : 'integer',
46
+       min: -20,
47
+       max: -1,
48
+       value: -19)
49
pipewire-0.3.65.tar.gz/pipewire-jack/src/pipewire-jack.c -> pipewire-0.3.66.tar.gz/pipewire-jack/src/pipewire-jack.c Changed
46
 
1
@@ -5784,15 +5784,18 @@
2
 {
3
    struct client *c = (struct client *) client;
4
    struct spa_io_position *pos;
5
-   double df;
6
 
7
    spa_return_val_if_fail(c != NULL, -EINVAL);
8
 
9
-   if (SPA_UNLIKELY((pos = c->rt.position) == NULL))
10
+   if (SPA_UNLIKELY((pos = c->rt.position) == NULL) || c->buffer_frames == 0)
11
        return 0;
12
 
13
-   df = (frames - pos->clock.position) * (double)SPA_NSEC_PER_SEC / c->sample_rate;
14
-   return (pos->clock.nsec + (int64_t)rint(df)) / SPA_NSEC_PER_USEC;
15
+   uint32_t nf = (uint32_t)pos->clock.position;
16
+   uint64_t w = pos->clock.nsec/SPA_NSEC_PER_USEC;
17
+   uint64_t nw = pos->clock.next_nsec/SPA_NSEC_PER_USEC;
18
+   int32_t df = frames - nf;
19
+   int64_t dp = nw - w;
20
+   return w + (int64_t)rint((double) df * (double) dp / c->buffer_frames);
21
 }
22
 
23
 SPA_EXPORT
24
@@ -5800,15 +5803,18 @@
25
 {
26
    struct client *c = (struct client *) client;
27
    struct spa_io_position *pos;
28
-   double du;
29
 
30
    spa_return_val_if_fail(c != NULL, -EINVAL);
31
 
32
    if (SPA_UNLIKELY((pos = c->rt.position) == NULL))
33
        return 0;
34
 
35
-   du = (usecs - pos->clock.nsec/SPA_NSEC_PER_USEC) * (double)c->sample_rate / SPA_USEC_PER_SEC;
36
-   return pos->clock.position + (int32_t)rint(du);
37
+   uint32_t nf = (uint32_t)pos->clock.position;
38
+   uint64_t w = pos->clock.nsec/SPA_NSEC_PER_USEC;
39
+   uint64_t nw = pos->clock.next_nsec/SPA_NSEC_PER_USEC;
40
+   int64_t du = usecs - w;
41
+   int64_t dp = nw - w;
42
+   return nf + (int32_t)rint((double)du / (double)dp * c->buffer_frames);
43
 }
44
 
45
 SPA_EXPORT
46
pipewire-0.3.65.tar.gz/po/oc.po -> pipewire-0.3.66.tar.gz/po/oc.po Changed
850
 
1
@@ -1,4 +1,4 @@
2
-# French translation of pipewire.
3
+# Occitan translation of pipewire.
4
 # Copyright (C) 2006-2008 Lennart Poettering
5
 # This file is distributed under the same license as the pipewire package.
6
 # Robert-André Mauchin <zebob.m@pengzone.org>, 2008.
7
@@ -8,24 +8,25 @@
8
 # Thomas Canniot <mrtom@fedoraproject.org>, 2009, 2012.
9
 # Cédric Valmary (Tot en Òc) <cvalmary@yahoo.fr>, 2015.
10
 # Cédric Valmary (totenoc.eu) <cvalmary@yahoo.fr>, 2016.
11
+# Quentin PAGÈS, 2023.
12
 msgid ""
13
 msgstr ""
14
 "Project-Id-Version: pipewire trunk\n"
15
 "Report-Msgid-Bugs-To: https://gitlab.freedesktop.org/pipewire/pipewire/"
16
 "issues/new\n"
17
-"POT-Creation-Date: 2021-04-18 16:54+0800\n"
18
-"PO-Revision-Date: 2016-10-12 22:20+0200\n"
19
-"Last-Translator: Cédric Valmary (totenoc.eu) <cvalmary@yahoo.fr>\n"
20
+"POT-Creation-Date: 2022-06-30 12:50+0200\n"
21
+"PO-Revision-Date: 2023-02-11 00:11+0100\n"
22
+"Last-Translator: Quentin PAGÈS\n"
23
 "Language-Team: Tot En Òc\n"
24
 "Language: oc\n"
25
 "MIME-Version: 1.0\n"
26
 "Content-Type: text/plain; charset=UTF-8\n"
27
 "Content-Transfer-Encoding: 8bit\n"
28
 "Plural-Forms: nplurals=2; plural=(n > 1);\n"
29
-"X-Generator: Virtaal 0.7.1\n"
30
+"X-Generator: Poedit 3.2.2\n"
31
 "X-Launchpad-Export-Date: 2016-10-12 20:12+0000\n"
32
 
33
-#: src/daemon/pipewire.c:43
34
+#: src/daemon/pipewire.c:46
35
 #, c-format
36
 msgid ""
37
 "%s options\n"
38
@@ -33,40 +34,56 @@
39
 "      --version                         Show version\n"
40
 "  -c, --config                          Load config (Default %s)\n"
41
 msgstr ""
42
+"%s opcions\n"
43
+"  -h, --help                            Afichar aquesta ajuda\n"
44
+"      --version                         Afichar la version\n"
45
+"  -c, --config                          Cargar la conf. (Defaut %s)\n"
46
 
47
-#: src/daemon/pipewire.desktop.in:4
48
-msgid "PipeWire Media System"
49
-msgstr ""
50
-
51
-#: src/daemon/pipewire.desktop.in:5
52
-msgid "Start the PipeWire Media System"
53
-msgstr ""
54
+#: src/modules/module-protocol-pulse/modules/module-tunnel-sink.c:180
55
+#: src/modules/module-protocol-pulse/modules/module-tunnel-source.c:180
56
+#, c-format
57
+msgid "Tunnel to %s/%s"
58
+msgstr "Tunèl cap a %s/%s"
59
 
60
-#: src/examples/media-session/alsa-monitor.c:526
61
-#: spa/plugins/alsa/acp/compat.c:187
62
-msgid "Built-in Audio"
63
-msgstr "Àudio integrat"
64
+#: src/modules/module-fallback-sink.c:51
65
+msgid "Dummy Output"
66
+msgstr "Sortida factícia"
67
 
68
-#: src/examples/media-session/alsa-monitor.c:530
69
-#: spa/plugins/alsa/acp/compat.c:192
70
-msgid "Modem"
71
-msgstr "Modèm"
72
+#: src/modules/module-pulse-tunnel.c:662
73
+#, c-format
74
+msgid "Tunnel for %s@%s"
75
+msgstr "Tunèl per %s@%s"
76
 
77
-#: src/examples/media-session/alsa-monitor.c:539
78
+#: src/modules/module-zeroconf-discover.c:332
79
 msgid "Unknown device"
80
-msgstr ""
81
+msgstr "Periferic desconegut"
82
+
83
+#: src/modules/module-zeroconf-discover.c:344
84
+#, c-format
85
+msgid "%s on %s@%s"
86
+msgstr "%s sus %s@%s"
87
 
88
-#: src/tools/pw-cat.c:991
89
+#: src/modules/module-zeroconf-discover.c:348
90
+#, c-format
91
+msgid "%s on %s"
92
+msgstr "%s sus %s"
93
+
94
+#: src/tools/pw-cat.c:784
95
 #, c-format
96
 msgid ""
97
-"%s options <file>\n"
98
+"%s options <file>|-\n"
99
 "  -h, --help                            Show this help\n"
100
 "      --version                         Show version\n"
101
 "  -v, --verbose                         Enable verbose operations\n"
102
 "\n"
103
 msgstr ""
104
+"%s opcions <file>|-\n"
105
+"  -h, --help                            Afichar aquesta ajuda\n"
106
+"      --version                         Afichar la version\n"
107
+"  -v, --verbose                         Activar las operacions verbosas\n"
108
+"\n"
109
 
110
-#: src/tools/pw-cat.c:998
111
+#: src/tools/pw-cat.c:791
112
 #, c-format
113
 msgid ""
114
 "  -R, --remote                          Remote daemon name\n"
115
@@ -80,11 +97,11 @@
116
 "                                          or direct samples (256)\n"
117
 "                                          the rate is the one of the source "
118
 "file\n"
119
-"      --list-targets                    List available targets for --target\n"
120
+"  -P  --properties                      Set node properties\n"
121
 "\n"
122
 msgstr ""
123
 
124
-#: src/tools/pw-cat.c:1016
125
+#: src/tools/pw-cat.c:809
126
 #, c-format
127
 msgid ""
128
 "      --rate                            Sample rate (req. for rec) (default "
129
@@ -104,15 +121,21 @@
130
 "\n"
131
 msgstr ""
132
 
133
-#: src/tools/pw-cat.c:1033
134
+#: src/tools/pw-cat.c:826
135
 msgid ""
136
 "  -p, --playback                        Playback mode\n"
137
 "  -r, --record                          Recording mode\n"
138
 "  -m, --midi                            Midi mode\n"
139
+"  -d, --dsd                             DSD mode\n"
140
 "\n"
141
 msgstr ""
142
+"  -p, --playback                        Mòde lectura\n"
143
+"  -r, --record                          Mòde enregistrament\n"
144
+"  -m, --midi                            Mòde Midi\n"
145
+"  -d, --dsd                             Mòde DSD\n"
146
+"\n"
147
 
148
-#: src/tools/pw-cli.c:2932
149
+#: src/tools/pw-cli.c:2250
150
 #, c-format
151
 msgid ""
152
 "%s options command\n"
153
@@ -122,360 +145,353 @@
154
 "  -r, --remote                          Remote daemon name\n"
155
 "\n"
156
 msgstr ""
157
+"%s opcions comanda\n"
158
+"  -h, --help                           Afichar aquesta ajuda\n"
159
+"      --version                         Afichar la version\n"
160
+"  -d, --daemon                          Aviar coma demòni (Per defaut "
161
+"false)\n"
162
+"  -r, --remote                          Remote daemon name\n"
163
+"\n"
164
 
165
-#: spa/plugins/alsa/acp/acp.c:290
166
+#: spa/plugins/alsa/acp/acp.c:321
167
 msgid "Pro Audio"
168
 msgstr ""
169
 
170
-#: spa/plugins/alsa/acp/acp.c:411 spa/plugins/alsa/acp/alsa-mixer.c:4704
171
-#: spa/plugins/bluez5/bluez5-device.c:1000
172
+#: spa/plugins/alsa/acp/acp.c:444 spa/plugins/alsa/acp/alsa-mixer.c:4648
173
+#: spa/plugins/bluez5/bluez5-device.c:1236
174
 msgid "Off"
175
 msgstr "Atudat"
176
 
177
-#: spa/plugins/alsa/acp/channelmap.h:466
178
-msgid "(invalid)"
179
-msgstr "(invalid)"
180
-
181
-#: spa/plugins/alsa/acp/alsa-mixer.c:2709
182
+#: spa/plugins/alsa/acp/alsa-mixer.c:2652
183
 msgid "Input"
184
 msgstr "Entrada"
185
 
186
-#: spa/plugins/alsa/acp/alsa-mixer.c:2710
187
+#: spa/plugins/alsa/acp/alsa-mixer.c:2653
188
 msgid "Docking Station Input"
189
 msgstr "Entrada de l'estacion d'acuèlh"
190
 
191
-#: spa/plugins/alsa/acp/alsa-mixer.c:2711
192
+#: spa/plugins/alsa/acp/alsa-mixer.c:2654
193
 msgid "Docking Station Microphone"
194
 msgstr "Microfòn de l'estacion d'acuèlh"
195
 
196
-#: spa/plugins/alsa/acp/alsa-mixer.c:2712
197
+#: spa/plugins/alsa/acp/alsa-mixer.c:2655
198
 msgid "Docking Station Line In"
199
 msgstr "Entrada linha de l'estacion d'acuèlh"
200
 
201
-#: spa/plugins/alsa/acp/alsa-mixer.c:2713
202
-#: spa/plugins/alsa/acp/alsa-mixer.c:2804
203
+#: spa/plugins/alsa/acp/alsa-mixer.c:2656
204
+#: spa/plugins/alsa/acp/alsa-mixer.c:2747
205
 msgid "Line In"
206
 msgstr "Entrada linha"
207
 
208
-#: spa/plugins/alsa/acp/alsa-mixer.c:2714
209
-#: spa/plugins/alsa/acp/alsa-mixer.c:2798
210
-#: spa/plugins/bluez5/bluez5-device.c:1145
211
+#: spa/plugins/alsa/acp/alsa-mixer.c:2657
212
+#: spa/plugins/alsa/acp/alsa-mixer.c:2741
213
+#: spa/plugins/bluez5/bluez5-device.c:1454
214
 msgid "Microphone"
215
-msgstr "Micrò"
216
+msgstr "Microfòn"
217
 
218
-#: spa/plugins/alsa/acp/alsa-mixer.c:2715
219
-#: spa/plugins/alsa/acp/alsa-mixer.c:2799
220
+#: spa/plugins/alsa/acp/alsa-mixer.c:2658
221
+#: spa/plugins/alsa/acp/alsa-mixer.c:2742
222
 msgid "Front Microphone"
223
 msgstr "Microfòn avant"
224
 
225
-#: spa/plugins/alsa/acp/alsa-mixer.c:2716
226
-#: spa/plugins/alsa/acp/alsa-mixer.c:2800
227
+#: spa/plugins/alsa/acp/alsa-mixer.c:2659
228
+#: spa/plugins/alsa/acp/alsa-mixer.c:2743
229
 msgid "Rear Microphone"
230
 msgstr "Microfòn arrièr"
231
 
232
-#: spa/plugins/alsa/acp/alsa-mixer.c:2717
233
+#: spa/plugins/alsa/acp/alsa-mixer.c:2660
234
 msgid "External Microphone"
235
 msgstr "Microfòn extèrne"
236
 
237
-#: spa/plugins/alsa/acp/alsa-mixer.c:2718
238
-#: spa/plugins/alsa/acp/alsa-mixer.c:2802
239
+#: spa/plugins/alsa/acp/alsa-mixer.c:2661
240
+#: spa/plugins/alsa/acp/alsa-mixer.c:2745
241
 msgid "Internal Microphone"
242
 msgstr "Microfòn intèrne"
243
 
244
-#: spa/plugins/alsa/acp/alsa-mixer.c:2719
245
-#: spa/plugins/alsa/acp/alsa-mixer.c:2805
246
+#: spa/plugins/alsa/acp/alsa-mixer.c:2662
247
+#: spa/plugins/alsa/acp/alsa-mixer.c:2748
248
 msgid "Radio"
249
 msgstr "Ràdio"
250
 
251
-#: spa/plugins/alsa/acp/alsa-mixer.c:2720
252
-#: spa/plugins/alsa/acp/alsa-mixer.c:2806
253
+#: spa/plugins/alsa/acp/alsa-mixer.c:2663
254
+#: spa/plugins/alsa/acp/alsa-mixer.c:2749
255
 msgid "Video"
256
 msgstr "Vidèo"
257
 
258
-#: spa/plugins/alsa/acp/alsa-mixer.c:2721
259
+#: spa/plugins/alsa/acp/alsa-mixer.c:2664
260
 msgid "Automatic Gain Control"
261
 msgstr "Contraròtle automatic del ganh"
262
 
263
-#: spa/plugins/alsa/acp/alsa-mixer.c:2722
264
+#: spa/plugins/alsa/acp/alsa-mixer.c:2665
265
 msgid "No Automatic Gain Control"
266
 msgstr "Pas de contraròtle automatic del ganh"
267
 
268
-#: spa/plugins/alsa/acp/alsa-mixer.c:2723
269
+#: spa/plugins/alsa/acp/alsa-mixer.c:2666
270
 msgid "Boost"
271
 msgstr "Boost"
272
 
273
-#: spa/plugins/alsa/acp/alsa-mixer.c:2724
274
+#: spa/plugins/alsa/acp/alsa-mixer.c:2667
275
 msgid "No Boost"
276
 msgstr "Sens boost"
277
 
278
-#: spa/plugins/alsa/acp/alsa-mixer.c:2725
279
+#: spa/plugins/alsa/acp/alsa-mixer.c:2668
280
 msgid "Amplifier"
281
 msgstr "Amplificador"
282
 
283
-#: spa/plugins/alsa/acp/alsa-mixer.c:2726
284
+#: spa/plugins/alsa/acp/alsa-mixer.c:2669
285
 msgid "No Amplifier"
286
 msgstr "Pas d'amplificador"
287
 
288
-#: spa/plugins/alsa/acp/alsa-mixer.c:2727
289
+#: spa/plugins/alsa/acp/alsa-mixer.c:2670
290
 msgid "Bass Boost"
291
 msgstr "Amplificacion bassas"
292
 
293
-#: spa/plugins/alsa/acp/alsa-mixer.c:2728
294
+#: spa/plugins/alsa/acp/alsa-mixer.c:2671
295
 msgid "No Bass Boost"
296
 msgstr "Pas d'amplificacion de las bassas"
297
 
298
-#: spa/plugins/alsa/acp/alsa-mixer.c:2729
299
-#: spa/plugins/bluez5/bluez5-device.c:1150
300
+#: spa/plugins/alsa/acp/alsa-mixer.c:2672
301
+#: spa/plugins/bluez5/bluez5-device.c:1460
302
 msgid "Speaker"
303
 msgstr "Nautparlaire"
304
 
305
-#: spa/plugins/alsa/acp/alsa-mixer.c:2730
306
-#: spa/plugins/alsa/acp/alsa-mixer.c:2808
307
+#: spa/plugins/alsa/acp/alsa-mixer.c:2673
308
+#: spa/plugins/alsa/acp/alsa-mixer.c:2751
309
 msgid "Headphones"
310
 msgstr "Escotadors"
311
 
312
-#: spa/plugins/alsa/acp/alsa-mixer.c:2797
313
+#: spa/plugins/alsa/acp/alsa-mixer.c:2740
314
 msgid "Analog Input"
315
 msgstr "Entrada analogica"
316
 
317
-#: spa/plugins/alsa/acp/alsa-mixer.c:2801
318
+#: spa/plugins/alsa/acp/alsa-mixer.c:2744
319
 msgid "Dock Microphone"
320
 msgstr "Microfòn de l'estacion d'acuèlh"
321
 
322
-#: spa/plugins/alsa/acp/alsa-mixer.c:2803
323
+#: spa/plugins/alsa/acp/alsa-mixer.c:2746
324
 msgid "Headset Microphone"
325
 msgstr "Micro-casc"
326
 
327
-#: spa/plugins/alsa/acp/alsa-mixer.c:2807
328
+#: spa/plugins/alsa/acp/alsa-mixer.c:2750
329
 msgid "Analog Output"
330
 msgstr "Sortida analogica"
331
 
332
-#: spa/plugins/alsa/acp/alsa-mixer.c:2809
333
-#, fuzzy
334
+#: spa/plugins/alsa/acp/alsa-mixer.c:2752
335
 msgid "Headphones 2"
336
-msgstr "Escotadors"
337
+msgstr "Casc àudio 2"
338
 
339
-#: spa/plugins/alsa/acp/alsa-mixer.c:2810
340
-#, fuzzy
341
+#: spa/plugins/alsa/acp/alsa-mixer.c:2753
342
 msgid "Headphones Mono Output"
343
-msgstr "Sortida Analogica Monò"
344
+msgstr "Sortida casc àudio analogica mono"
345
 
346
-#: spa/plugins/alsa/acp/alsa-mixer.c:2811
347
+#: spa/plugins/alsa/acp/alsa-mixer.c:2754
348
 msgid "Line Out"
349
 msgstr "Sortida linha"
350
 
351
-#: spa/plugins/alsa/acp/alsa-mixer.c:2812
352
+#: spa/plugins/alsa/acp/alsa-mixer.c:2755
353
 msgid "Analog Mono Output"
354
-msgstr "Sortida Analogica Monò"
355
+msgstr "Sortida analogica mono"
356
 
357
-#: spa/plugins/alsa/acp/alsa-mixer.c:2813
358
+#: spa/plugins/alsa/acp/alsa-mixer.c:2756
359
 msgid "Speakers"
360
 msgstr "Nauts parlaires"
361
 
362
-#: spa/plugins/alsa/acp/alsa-mixer.c:2814
363
+#: spa/plugins/alsa/acp/alsa-mixer.c:2757
364
 msgid "HDMI / DisplayPort"
365
 msgstr "HDMI / DisplayPort"
366
 
367
-#: spa/plugins/alsa/acp/alsa-mixer.c:2815
368
+#: spa/plugins/alsa/acp/alsa-mixer.c:2758
369
 msgid "Digital Output (S/PDIF)"
370
 msgstr "Sortida numerica (S/PDIF)"
371
 
372
-#: spa/plugins/alsa/acp/alsa-mixer.c:2816
373
+#: spa/plugins/alsa/acp/alsa-mixer.c:2759
374
 msgid "Digital Input (S/PDIF)"
375
 msgstr "Entrada numerica (S/PDIF)"
376
 
377
-#: spa/plugins/alsa/acp/alsa-mixer.c:2817
378
+#: spa/plugins/alsa/acp/alsa-mixer.c:2760
379
 msgid "Multichannel Input"
380
-msgstr ""
381
+msgstr "Entrada multicanal"
382
 
383
-#: spa/plugins/alsa/acp/alsa-mixer.c:2818
384
+#: spa/plugins/alsa/acp/alsa-mixer.c:2761
385
 msgid "Multichannel Output"
386
-msgstr ""
387
+msgstr "Sortida multicanal"
388
 
389
-#: spa/plugins/alsa/acp/alsa-mixer.c:2819
390
-#, fuzzy
391
+#: spa/plugins/alsa/acp/alsa-mixer.c:2762
392
 msgid "Game Output"
393
-msgstr "%s Sortida"
394
+msgstr "Sortida jòc"
395
 
396
-#: spa/plugins/alsa/acp/alsa-mixer.c:2820
397
-#: spa/plugins/alsa/acp/alsa-mixer.c:2821
398
-#, fuzzy
399
+#: spa/plugins/alsa/acp/alsa-mixer.c:2763
400
+#: spa/plugins/alsa/acp/alsa-mixer.c:2764
401
 msgid "Chat Output"
402
-msgstr "%s Sortida"
403
+msgstr "Sortida messatjariá"
404
 
405
-#: spa/plugins/alsa/acp/alsa-mixer.c:2822
406
-#, fuzzy
407
+#: spa/plugins/alsa/acp/alsa-mixer.c:2765
408
 msgid "Chat Input"
409
-msgstr "%s Entrada"
410
+msgstr "Entrada messatjariá"
411
 
412
-#: spa/plugins/alsa/acp/alsa-mixer.c:2823
413
-#, fuzzy
414
+#: spa/plugins/alsa/acp/alsa-mixer.c:2766
415
 msgid "Virtual Surround 7.1"
416
-msgstr "Collector ambiofonic virtual"
417
+msgstr "Surround 7.1 virtual"
418
 
419
-#: spa/plugins/alsa/acp/alsa-mixer.c:4527
420
+#: spa/plugins/alsa/acp/alsa-mixer.c:4471
421
 msgid "Analog Mono"
422
-msgstr "Monò analogic"
423
+msgstr "Mono analogic"
424
 
425
-#: spa/plugins/alsa/acp/alsa-mixer.c:4528
426
-#, fuzzy
427
+#: spa/plugins/alsa/acp/alsa-mixer.c:4472
428
 msgid "Analog Mono (Left)"
429
-msgstr "Monò analogic"
430
+msgstr "Mono analogic (esquèrra)"
431
 
432
-#: spa/plugins/alsa/acp/alsa-mixer.c:4529
433
-#, fuzzy
434
+#: spa/plugins/alsa/acp/alsa-mixer.c:4473
435
 msgid "Analog Mono (Right)"
436
-msgstr "Monò analogic"
437
+msgstr "Mono analogic (drecha)"
438
 
439
 #. Note: Not translated to "Analog Stereo Input", because the source
440
 #. * name gets "Input" appended to it automatically, so adding "Input"
441
 #. * here would lead to the source name to become "Analog Stereo Input
442
 #. * Input". The same logic applies to analog-stereo-output,
443
 #. * multichannel-input and multichannel-output.
444
-#: spa/plugins/alsa/acp/alsa-mixer.c:4530
445
-#: spa/plugins/alsa/acp/alsa-mixer.c:4538
446
-#: spa/plugins/alsa/acp/alsa-mixer.c:4539
447
+#: spa/plugins/alsa/acp/alsa-mixer.c:4474
448
+#: spa/plugins/alsa/acp/alsa-mixer.c:4482
449
+#: spa/plugins/alsa/acp/alsa-mixer.c:4483
450
 msgid "Analog Stereo"
451
-msgstr "Esterèo analogic"
452
+msgstr "Estereo analogic"
453
 
454
-#: spa/plugins/alsa/acp/alsa-mixer.c:4531
455
+#: spa/plugins/alsa/acp/alsa-mixer.c:4475
456
 msgid "Mono"
457
 msgstr "Mono"
458
 
459
-#: spa/plugins/alsa/acp/alsa-mixer.c:4532
460
+#: spa/plugins/alsa/acp/alsa-mixer.c:4476
461
 msgid "Stereo"
462
-msgstr "Esterèo"
463
+msgstr "Estereo"
464
 
465
-#: spa/plugins/alsa/acp/alsa-mixer.c:4540
466
-#: spa/plugins/alsa/acp/alsa-mixer.c:4698
467
-#: spa/plugins/bluez5/bluez5-device.c:1135
468
+#: spa/plugins/alsa/acp/alsa-mixer.c:4484
469
+#: spa/plugins/alsa/acp/alsa-mixer.c:4642
470
+#: spa/plugins/bluez5/bluez5-device.c:1442
471
 msgid "Headset"
472
 msgstr "Casc àudio"
473
 
474
-#: spa/plugins/alsa/acp/alsa-mixer.c:4541
475
-#: spa/plugins/alsa/acp/alsa-mixer.c:4699
476
-#, fuzzy
477
+#: spa/plugins/alsa/acp/alsa-mixer.c:4485
478
+#: spa/plugins/alsa/acp/alsa-mixer.c:4643
479
 msgid "Speakerphone"
480
 msgstr "Nautparlaire"
481
 
482
-#: spa/plugins/alsa/acp/alsa-mixer.c:4542
483
-#: spa/plugins/alsa/acp/alsa-mixer.c:4543
484
+#: spa/plugins/alsa/acp/alsa-mixer.c:4486
485
+#: spa/plugins/alsa/acp/alsa-mixer.c:4487
486
 msgid "Multichannel"
487
-msgstr ""
488
+msgstr "Multicanal"
489
 
490
-#: spa/plugins/alsa/acp/alsa-mixer.c:4544
491
+#: spa/plugins/alsa/acp/alsa-mixer.c:4488
492
 msgid "Analog Surround 2.1"
493
 msgstr "Surround analogic 2.1"
494
 
495
-#: spa/plugins/alsa/acp/alsa-mixer.c:4545
496
+#: spa/plugins/alsa/acp/alsa-mixer.c:4489
497
 msgid "Analog Surround 3.0"
498
 msgstr "Surround analogic 3.0"
499
 
500
-#: spa/plugins/alsa/acp/alsa-mixer.c:4546
501
+#: spa/plugins/alsa/acp/alsa-mixer.c:4490
502
 msgid "Analog Surround 3.1"
503
 msgstr "Surround analogic 3.1"
504
 
505
-#: spa/plugins/alsa/acp/alsa-mixer.c:4547
506
+#: spa/plugins/alsa/acp/alsa-mixer.c:4491
507
 msgid "Analog Surround 4.0"
508
 msgstr "Surround analogic 4.0"
509
 
510
-#: spa/plugins/alsa/acp/alsa-mixer.c:4548
511
+#: spa/plugins/alsa/acp/alsa-mixer.c:4492
512
 msgid "Analog Surround 4.1"
513
 msgstr "Surround analogic 4.1"
514
 
515
-#: spa/plugins/alsa/acp/alsa-mixer.c:4549
516
+#: spa/plugins/alsa/acp/alsa-mixer.c:4493
517
 msgid "Analog Surround 5.0"
518
 msgstr "Surround analogic 5.0"
519
 
520
-#: spa/plugins/alsa/acp/alsa-mixer.c:4550
521
+#: spa/plugins/alsa/acp/alsa-mixer.c:4494
522
 msgid "Analog Surround 5.1"
523
 msgstr "Surround analogic 5.1"
524
 
525
-#: spa/plugins/alsa/acp/alsa-mixer.c:4551
526
+#: spa/plugins/alsa/acp/alsa-mixer.c:4495
527
 msgid "Analog Surround 6.0"
528
 msgstr "Surround analogic 6.0"
529
 
530
-#: spa/plugins/alsa/acp/alsa-mixer.c:4552
531
+#: spa/plugins/alsa/acp/alsa-mixer.c:4496
532
 msgid "Analog Surround 6.1"
533
 msgstr "Surround analogic 6.1"
534
 
535
-#: spa/plugins/alsa/acp/alsa-mixer.c:4553
536
+#: spa/plugins/alsa/acp/alsa-mixer.c:4497
537
 msgid "Analog Surround 7.0"
538
 msgstr "Surround analogic 7.0"
539
 
540
-#: spa/plugins/alsa/acp/alsa-mixer.c:4554
541
+#: spa/plugins/alsa/acp/alsa-mixer.c:4498
542
 msgid "Analog Surround 7.1"
543
 msgstr "Surround analogic 7.1"
544
 
545
-#: spa/plugins/alsa/acp/alsa-mixer.c:4555
546
+#: spa/plugins/alsa/acp/alsa-mixer.c:4499
547
 msgid "Digital Stereo (IEC958)"
548
-msgstr "Esterèo numeric (IEC958)"
549
+msgstr "Estereo numeric (IEC958)"
550
 
551
-#: spa/plugins/alsa/acp/alsa-mixer.c:4556
552
+#: spa/plugins/alsa/acp/alsa-mixer.c:4500
553
 msgid "Digital Surround 4.0 (IEC958/AC3)"
554
 msgstr "Surround numeric 4.0 (IEC958/AC3)"
555
 
556
-#: spa/plugins/alsa/acp/alsa-mixer.c:4557
557
+#: spa/plugins/alsa/acp/alsa-mixer.c:4501
558
 msgid "Digital Surround 5.1 (IEC958/AC3)"
559
 msgstr "Surround numeric 5.1 (IEC958/AC3)"
560
 
561
-#: spa/plugins/alsa/acp/alsa-mixer.c:4558
562
+#: spa/plugins/alsa/acp/alsa-mixer.c:4502
563
 msgid "Digital Surround 5.1 (IEC958/DTS)"
564
 msgstr "Digital Surround 5.1 (IEC958/DTS)"
565
 
566
-#: spa/plugins/alsa/acp/alsa-mixer.c:4559
567
+#: spa/plugins/alsa/acp/alsa-mixer.c:4503
568
 msgid "Digital Stereo (HDMI)"
569
-msgstr "Esterèo numeric (HDMI)"
570
+msgstr "Estereo numeric (HDMI)"
571
 
572
-#: spa/plugins/alsa/acp/alsa-mixer.c:4560
573
+#: spa/plugins/alsa/acp/alsa-mixer.c:4504
574
 msgid "Digital Surround 5.1 (HDMI)"
575
 msgstr "Digital Surround 5.1 (HDMI)"
576
 
577
-#: spa/plugins/alsa/acp/alsa-mixer.c:4561
578
+#: spa/plugins/alsa/acp/alsa-mixer.c:4505
579
 msgid "Chat"
580
-msgstr ""
581
+msgstr "Messatjariá instantanèa"
582
 
583
-#: spa/plugins/alsa/acp/alsa-mixer.c:4562
584
+#: spa/plugins/alsa/acp/alsa-mixer.c:4506
585
 msgid "Game"
586
-msgstr ""
587
+msgstr "Jòc"
588
 
589
-#: spa/plugins/alsa/acp/alsa-mixer.c:4696
590
+#: spa/plugins/alsa/acp/alsa-mixer.c:4640
591
 msgid "Analog Mono Duplex"
592
 msgstr "Duplèx Mono analogic"
593
 
594
-#: spa/plugins/alsa/acp/alsa-mixer.c:4697
595
+#: spa/plugins/alsa/acp/alsa-mixer.c:4641
596
 msgid "Analog Stereo Duplex"
597
 msgstr "Duplèx esterèo analogic"
598
 
599
-#: spa/plugins/alsa/acp/alsa-mixer.c:4700
600
+#: spa/plugins/alsa/acp/alsa-mixer.c:4644
601
 msgid "Digital Stereo Duplex (IEC958)"
602
 msgstr "Duplèx estèreo numeric (IEC958)"
603
 
604
-#: spa/plugins/alsa/acp/alsa-mixer.c:4701
605
+#: spa/plugins/alsa/acp/alsa-mixer.c:4645
606
 msgid "Multichannel Duplex"
607
-msgstr ""
608
+msgstr "Duplèx multicanal"
609
 
610
-#: spa/plugins/alsa/acp/alsa-mixer.c:4702
611
-#, fuzzy
612
+#: spa/plugins/alsa/acp/alsa-mixer.c:4646
613
 msgid "Stereo Duplex"
614
-msgstr "Duplèx esterèo analogic"
615
+msgstr "Duplèx estereo"
616
 
617
-#: spa/plugins/alsa/acp/alsa-mixer.c:4703
618
+#: spa/plugins/alsa/acp/alsa-mixer.c:4647
619
 msgid "Mono Chat + 7.1 Surround"
620
-msgstr ""
621
+msgstr "Messatjariá mono + Surround 7.1"
622
 
623
-#: spa/plugins/alsa/acp/alsa-mixer.c:4806
624
+#: spa/plugins/alsa/acp/alsa-mixer.c:4754
625
 #, c-format
626
 msgid "%s Output"
627
 msgstr "%s Sortida"
628
 
629
-#: spa/plugins/alsa/acp/alsa-mixer.c:4813
630
+#: spa/plugins/alsa/acp/alsa-mixer.c:4761
631
 #, c-format
632
 msgid "%s Input"
633
 msgstr "%s Entrada"
634
 
635
-#: spa/plugins/alsa/acp/alsa-util.c:1175 spa/plugins/alsa/acp/alsa-util.c:1269
636
-#, fuzzy, c-format
637
+#: spa/plugins/alsa/acp/alsa-util.c:1187 spa/plugins/alsa/acp/alsa-util.c:1281
638
+#, c-format
639
 msgid ""
640
 "snd_pcm_avail() returned a value that is exceptionally large: %lu byte (%lu "
641
 "ms).\n"
642
@@ -487,18 +503,18 @@
643
 "Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
644
 "to the ALSA developers."
645
 msgstr0 ""
646
-"snd_pcm_avail() a tornat una valor qu'es excepcionalament larga : %lu octets "
647
+"snd_pcm_avail() a tornat una valor qu'es excepcionalament larga : %lu octet "
648
 "(%lu ms).\n"
649
-"S'agís fòrt probablament d'un bug dins lo pilòt ALSA « %s ». Raportatz "
650
-"aqueste problèma als desvolopaires d'ALSA."
651
+"Es fòrt probablament un bug dins lo pilòt ALSA « %s ». Senhalatz-lo als "
652
+"desvolopaires d’ALSA."
653
 msgstr1 ""
654
 "snd_pcm_avail() a tornat una valor qu'es excepcionalament larga : %lu octets "
655
 "(%lu ms).\n"
656
-"S'agís fòrt probablament d'un bug dins lo pilòt ALSA « %s ». Raportatz "
657
-"aqueste problèma als desvolopaires d'ALSA."
658
+"Es fòrt probablament un bug dins lo pilòt ALSA « %s ». Senhalatz-lo als "
659
+"desvolopaires d’ALSA."
660
 
661
-#: spa/plugins/alsa/acp/alsa-util.c:1241
662
-#, fuzzy, c-format
663
+#: spa/plugins/alsa/acp/alsa-util.c:1253
664
+#, c-format
665
 msgid ""
666
 "snd_pcm_delay() returned a value that is exceptionally large: %li byte (%s"
667
 "%lu ms).\n"
668
@@ -510,17 +526,17 @@
669
 "Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
670
 "to the ALSA developers."
671
 msgstr0 ""
672
-"snd_pcm_delay() a tornat una valor qu'es excepcionalament larga : %li octets "
673
+"snd_pcm_delay() a tornat una valor qu'es excepcionalament larga : %li octet "
674
 "%s%lu ms).\n"
675
-"S'agís fòrt probablament d'un bug dins lo pilòt ALSA « %s ». Raportatz "
676
-"aqueste problèma als desvolopaires d'ALSA."
677
+"Es fòrt probablament un bug dins lo pilòt ALSA « %s ». Senhalatz-lo als "
678
+"desvolopaires d’ALSA."
679
 msgstr1 ""
680
 "snd_pcm_delay() a tornat una valor qu'es excepcionalament larga : %li octets "
681
 "%s%lu ms).\n"
682
-"S'agís fòrt probablament d'un bug dins lo pilòt ALSA « %s ». Raportatz "
683
-"aqueste problèma als desvolopaires d'ALSA."
684
+"Es fòrt probablament un bug dins lo pilòt ALSA « %s ». Senhalatz-lo als "
685
+"desvolopaires d’ALSA."
686
 
687
-#: spa/plugins/alsa/acp/alsa-util.c:1288
688
+#: spa/plugins/alsa/acp/alsa-util.c:1300
689
 #, c-format
690
 msgid ""
691
 "snd_pcm_avail_delay() returned strange values: delay %lu is less than avail "
692
@@ -530,11 +546,11 @@
693
 msgstr ""
694
 "snd_pcm_avail_delay() a tornat de resultats anormals : lo relambi %lu es mai "
695
 "pichon que %lu.\n"
696
-"Es fòrt probablament un bug dins lo pilòt ALSA '%s'. Senhalatz-lo als "
697
-"desvolopaires d'ALSA."
698
+"Es fòrt probablament un bug dins lo pilòt ALSA « %s ». Senhalatz-lo als "
699
+"desvolopaires d’ALSA."
700
 
701
-#: spa/plugins/alsa/acp/alsa-util.c:1331
702
-#, fuzzy, c-format
703
+#: spa/plugins/alsa/acp/alsa-util.c:1343
704
+#, c-format
705
 msgid ""
706
 "snd_pcm_mmap_begin() returned a value that is exceptionally large: %lu byte "
707
 "(%lu ms).\n"
708
@@ -547,71 +563,113 @@
709
 "to the ALSA developers."
710
 msgstr0 ""
711
 "snd_pcm_mmap_begin() a tornat una valor qu'es excepcionalament larga : %lu "
712
-"octets (%lu·ms)..\n"
713
-"S'agís fòrt probablament d'un bug dins lo pilòt ALSA « %s ». Raportatz "
714
-"aqueste problèma als desvolopaires d'ALSA."
715
+"octet (%lu ms).\n"
716
+"Es fòrt probablament un bug dins lo pilòt ALSA « %s ». Senhalatz-lo als "
717
+"desvolopaires d’ALSA."
718
 msgstr1 ""
719
 "snd_pcm_mmap_begin() a tornat una valor qu'es excepcionalament larga : %lu "
720
-"octets (%lu·ms)..\n"
721
-"S'agís fòrt probablament d'un bug dins lo pilòt ALSA « %s ». Raportatz "
722
-"aqueste problèma als desvolopaires d'ALSA."
723
+"octet (%lu ms).\n"
724
+"Es fòrt probablament un bug dins lo pilòt ALSA « %s ». Senhalatz-lo als "
725
+"desvolopaires d’ALSA."
726
+
727
+#: spa/plugins/alsa/acp/channelmap.h:457
728
+msgid "(invalid)"
729
+msgstr "(invalid)"
730
+
731
+#: spa/plugins/alsa/acp/compat.c:189
732
+msgid "Built-in Audio"
733
+msgstr "Àudio integrat"
734
+
735
+#: spa/plugins/alsa/acp/compat.c:194
736
+msgid "Modem"
737
+msgstr "Modèm"
738
 
739
-#: spa/plugins/bluez5/bluez5-device.c:1010
740
+#: spa/plugins/bluez5/bluez5-device.c:1247
741
 msgid "Audio Gateway (A2DP Source & HSP/HFP AG)"
742
-msgstr ""
743
+msgstr "Palanca àudio (Font A2DP & HSP/HFP AG)"
744
 
745
-#: spa/plugins/bluez5/bluez5-device.c:1033
746
+#: spa/plugins/bluez5/bluez5-device.c:1272
747
 #, c-format
748
 msgid "High Fidelity Playback (A2DP Sink, codec %s)"
749
-msgstr ""
750
+msgstr "Lectura nauta fidelitat (A2DP Sink, codec %s)"
751
 
752
-#: spa/plugins/bluez5/bluez5-device.c:1035
753
+#: spa/plugins/bluez5/bluez5-device.c:1275
754
 #, c-format
755
 msgid "High Fidelity Duplex (A2DP Source/Sink, codec %s)"
756
-msgstr ""
757
+msgstr "Duplèx nauta fidelitat (A2DP Source/Sink, codec %s)"
758
 
759
-#: spa/plugins/bluez5/bluez5-device.c:1041
760
+#: spa/plugins/bluez5/bluez5-device.c:1283
761
 msgid "High Fidelity Playback (A2DP Sink)"
762
-msgstr ""
763
+msgstr "Lectura nauta fidelitat (A2DP Sink)"
764
 
765
-#: spa/plugins/bluez5/bluez5-device.c:1043
766
+#: spa/plugins/bluez5/bluez5-device.c:1285
767
 msgid "High Fidelity Duplex (A2DP Source/Sink)"
768
-msgstr ""
769
+msgstr "Duplèx nauta fidelitat (A2DP Source/Sink)"
770
+
771
+#: spa/plugins/bluez5/bluez5-device.c:1322
772
+#, c-format
773
+msgid "High Fidelity Playback (BAP Sink, codec %s)"
774
+msgstr "Lectura nauta fidelitat (A2DP Sink, codec %s)"
775
 
776
-#: spa/plugins/bluez5/bluez5-device.c:1070
777
+#: spa/plugins/bluez5/bluez5-device.c:1326
778
+#, c-format
779
+msgid "High Fidelity Input (BAP Source, codec %s)"
780
+msgstr "Duplèx nauta fidelitat (Font BAP, codec %s)"
781
+
782
+#: spa/plugins/bluez5/bluez5-device.c:1330
783
+#, c-format
784
+msgid "High Fidelity Duplex (BAP Source/Sink, codec %s)"
785
+msgstr "Duplèx nauta fidelitat (Font BAP/Sink, codec %s)"
786
+
787
+#: spa/plugins/bluez5/bluez5-device.c:1359
788
 #, c-format
789
 msgid "Headset Head Unit (HSP/HFP, codec %s)"
790
 msgstr ""
791
 
792
-#: spa/plugins/bluez5/bluez5-device.c:1074
793
+#: spa/plugins/bluez5/bluez5-device.c:1364
794
 msgid "Headset Head Unit (HSP/HFP)"
795
 msgstr ""
796
 
797
-#: spa/plugins/bluez5/bluez5-device.c:1140
798
+#: spa/plugins/bluez5/bluez5-device.c:1443
799
+#: spa/plugins/bluez5/bluez5-device.c:1448
800
+#: spa/plugins/bluez5/bluez5-device.c:1455
801
+#: spa/plugins/bluez5/bluez5-device.c:1461
802
+#: spa/plugins/bluez5/bluez5-device.c:1467
803
+#: spa/plugins/bluez5/bluez5-device.c:1473
804
+#: spa/plugins/bluez5/bluez5-device.c:1479
805
+#: spa/plugins/bluez5/bluez5-device.c:1485
806
+#: spa/plugins/bluez5/bluez5-device.c:1491
807
 msgid "Handsfree"
808
 msgstr "Mans liuras"
809
 
810
-#: spa/plugins/bluez5/bluez5-device.c:1155
811
+#: spa/plugins/bluez5/bluez5-device.c:1449
812
+msgid "Handsfree (HFP)"
813
+msgstr "Mans liuras (HFP)"
814
+
815
+#: spa/plugins/bluez5/bluez5-device.c:1466
816
 msgid "Headphone"
817
-msgstr "Escotadors"
818
+msgstr "Escotador"
819
 
820
-#: spa/plugins/bluez5/bluez5-device.c:1160
821
+#: spa/plugins/bluez5/bluez5-device.c:1472
822
 msgid "Portable"
823
 msgstr "Portable"
824
 
825
-#: spa/plugins/bluez5/bluez5-device.c:1165
826
+#: spa/plugins/bluez5/bluez5-device.c:1478
827
 msgid "Car"
828
 msgstr "Telefòn de veitura"
829
 
830
-#: spa/plugins/bluez5/bluez5-device.c:1170
831
+#: spa/plugins/bluez5/bluez5-device.c:1484
832
 msgid "HiFi"
833
 msgstr "HiFi"
834
 
835
-#: spa/plugins/bluez5/bluez5-device.c:1175
836
+#: spa/plugins/bluez5/bluez5-device.c:1490
837
 msgid "Phone"
838
 msgstr "Telefòn"
839
 
840
-#: spa/plugins/bluez5/bluez5-device.c:1181
841
-#, fuzzy
842
+#: spa/plugins/bluez5/bluez5-device.c:1497
843
 msgid "Bluetooth"
844
-msgstr "Entrada Bluetooth"
845
+msgstr "Bluetooth"
846
+
847
+#: spa/plugins/bluez5/bluez5-device.c:1498
848
+msgid "Bluetooth (HFP)"
849
+msgstr "Bluetooth (HFP)"
850
pipewire-0.3.65.tar.gz/po/uk.po -> pipewire-0.3.66.tar.gz/po/uk.po Changed
381
 
1
@@ -1,14 +1,14 @@
2
 # Copyright (C) 2009 Free Software Foundation, Inc.
3
 # This file is distributed under the same license as the pipewire package.
4
 #
5
-# Yuri Chornoivan <yurchor@ukr.net>, 2009-2021, 2022.
6
+# Yuri Chornoivan <yurchor@ukr.net>, 2009-2021, 2022, 2023.
7
 msgid ""
8
 msgstr ""
9
 "Project-Id-Version: pipewire\n"
10
-"Report-Msgid-Bugs-To: https://gitlab.freedesktop.org/pipewire/pipewire/-/issue"
11
-"s\n"
12
-"POT-Creation-Date: 2022-05-20 15:26+0000\n"
13
-"PO-Revision-Date: 2022-06-18 13:07+0300\n"
14
+"Report-Msgid-Bugs-To: https://gitlab.freedesktop.org/pipewire/pipewire/-/issu"
15
+"es\n"
16
+"POT-Creation-Date: 2023-02-06 15:27+0000\n"
17
+"PO-Revision-Date: 2023-02-11 17:42+0200\n"
18
 "Last-Translator: Yuri Chornoivan <yurchor@ukr.net>\n"
19
 "Language-Team: Ukrainian <trans-uk@lists.fedoraproject.org>\n"
20
 "Language: uk\n"
21
@@ -41,8 +41,8 @@
22
 msgid "Start the PipeWire Media System"
23
 msgstr "Запустити мультимедійну систему PipeWire"
24
 
25
-#: src/modules/module-protocol-pulse/modules/module-tunnel-sink.c:183
26
-#: src/modules/module-protocol-pulse/modules/module-tunnel-source.c:183
27
+#: src/modules/module-protocol-pulse/modules/module-tunnel-sink.c:179
28
+#: src/modules/module-protocol-pulse/modules/module-tunnel-source.c:179
29
 #, c-format
30
 msgid "Tunnel to %s/%s"
31
 msgstr "Тунель до %s/%s"
32
@@ -51,33 +51,27 @@
33
 msgid "Dummy Output"
34
 msgstr "Фіктивний вихід"
35
 
36
-#: src/modules/module-pulse-tunnel.c:639
37
+#: src/modules/module-pulse-tunnel.c:695
38
 #, c-format
39
 msgid "Tunnel for %s@%s"
40
 msgstr "Тунель для %s@%s"
41
 
42
-#: src/modules/module-zeroconf-discover.c:332
43
+#: src/modules/module-zeroconf-discover.c:335
44
 msgid "Unknown device"
45
 msgstr "Невідомий пристрій"
46
 
47
-#: src/modules/module-zeroconf-discover.c:344
48
+#: src/modules/module-zeroconf-discover.c:347
49
 #, c-format
50
 msgid "%s on %s@%s"
51
 msgstr "%s на %s@%s"
52
 
53
-#: src/modules/module-zeroconf-discover.c:348
54
+#: src/modules/module-zeroconf-discover.c:351
55
 #, c-format
56
 msgid "%s on %s"
57
 msgstr "%s на %s"
58
 
59
-#: src/tools/pw-cat.c:872
60
+#: src/tools/pw-cat.c:940
61
 #, c-format
62
-#| msgid ""
63
-#| "%s options <file>\n"
64
-#| "  -h, --help                            Show this help\n"
65
-#| "      --version                         Show version\n"
66
-#| "  -v, --verbose                         Enable verbose operations\n"
67
-#| "\n"
68
 msgid ""
69
 "%s options <file>|-\n"
70
 "  -h, --help                            Show this help\n"
71
@@ -92,7 +86,7 @@
72
 "інформації\n"
73
 "\n"
74
 
75
-#: src/tools/pw-cat.c:879
76
+#: src/tools/pw-cat.c:947
77
 #, c-format
78
 #| msgid ""
79
 #| "  -R, --remote                          Remote daemon name\n"
80
@@ -106,13 +100,15 @@
81
 #| "                                          or direct samples (256)\n"
82
 #| "                                          the rate is the one of the "
83
 #| "source file\n"
84
+#| "  -P  --properties                      Set node properties\n"
85
 #| "\n"
86
 msgid ""
87
 "  -R, --remote                          Remote daemon name\n"
88
 "      --media-type                      Set media type (default %s)\n"
89
 "      --media-category                  Set media category (default %s)\n"
90
 "      --media-role                      Set media role (default %s)\n"
91
-"      --target                          Set node target (default %s)\n"
92
+"      --target                          Set node target serial or name "
93
+"(default %s)\n"
94
 "                                          0 means don't link\n"
95
 "      --latency                         Set node latency (default %s)\n"
96
 "                                          Xunit (unit = s, ms, us, ns)\n"
97
@@ -129,7 +125,8 @@
98
 "(типово, %s)\n"
99
 "      --media-role                      встановити роль мультимедіа (типово, "
100
 "%s)\n"
101
-"      --target                          встановити ціль вузла (типово, %s)\n"
102
+"      --target                          встановити назву або серійний номер"
103
+" цілі вузла (типово, %s)\n"
104
 "                                          0 — не пов'язувати\n"
105
 "      --latency                         встановити затримку вузла (типово, "
106
 "%s)\n"
107
@@ -140,7 +137,7 @@
108
 "  -P  --properties                      встановити властивості вузла\n"
109
 "\n"
110
 
111
-#: src/tools/pw-cat.c:897
112
+#: src/tools/pw-cat.c:965
113
 #, c-format
114
 msgid ""
115
 "      --rate                            Sample rate (req. for rec) (default "
116
@@ -176,28 +173,44 @@
117
 "(типово, %d)\n"
118
 "\n"
119
 
120
-#: src/tools/pw-cat.c:914
121
+#: src/tools/pw-cat.c:982
122
+#| msgid ""
123
+#| "  -p, --playback                        Playback mode\n"
124
+#| "  -r, --record                          Recording mode\n"
125
+#| "  -m, --midi                            Midi mode\n"
126
+#| "  -d, --dsd                             DSD mode\n"
127
+#| "\n"
128
 msgid ""
129
 "  -p, --playback                        Playback mode\n"
130
 "  -r, --record                          Recording mode\n"
131
 "  -m, --midi                            Midi mode\n"
132
 "  -d, --dsd                             DSD mode\n"
133
+"  -o, --encoded\t\t\t      Encoded mode\n"
134
 "\n"
135
 msgstr ""
136
 "  -p, --playback                        режим відтворення\n"
137
 "  -r, --record                          режим запису\n"
138
 "  -m, --midi                            режим MIDI\n"
139
 "  -d, --dsd                             режим DSD\n"
140
+"  -o, --encoded\t\t\t      закодований режим\n"
141
 "\n"
142
 
143
-#: src/tools/pw-cli.c:3139
144
+#: src/tools/pw-cli.c:2236
145
 #, c-format
146
+#| msgid ""
147
+#| "%s options command\n"
148
+#| "  -h, --help                            Show this help\n"
149
+#| "      --version                         Show version\n"
150
+#| "  -d, --daemon                          Start as daemon (Default false)\n"
151
+#| "  -r, --remote                          Remote daemon name\n"
152
+#| "\n"
153
 msgid ""
154
 "%s options command\n"
155
 "  -h, --help                            Show this help\n"
156
 "      --version                         Show version\n"
157
 "  -d, --daemon                          Start as daemon (Default false)\n"
158
 "  -r, --remote                          Remote daemon name\n"
159
+"  -m, --monitor                         Monitor activity\n"
160
 "\n"
161
 msgstr ""
162
 "%s параметри команда\n"
163
@@ -206,14 +219,15 @@
164
 "  -d, --daemon                          запустити як фонову службу (типово, "
165
 "false)\n"
166
 "  -r, --remote                          назва віддаленої фонової служби\n"
167
+"  -m, --monitor                         спостерігати за діями\n"
168
 "\n"
169
 
170
-#: spa/plugins/alsa/acp/acp.c:321
171
+#: spa/plugins/alsa/acp/acp.c:323
172
 msgid "Pro Audio"
173
 msgstr "Професійний звук"
174
 
175
-#: spa/plugins/alsa/acp/acp.c:444 spa/plugins/alsa/acp/alsa-mixer.c:4648
176
-#: spa/plugins/bluez5/bluez5-device.c:1161
177
+#: spa/plugins/alsa/acp/acp.c:447 spa/plugins/alsa/acp/alsa-mixer.c:4648
178
+#: spa/plugins/bluez5/bluez5-device.c:1303
179
 msgid "Off"
180
 msgstr "Вимкнено"
181
 
182
@@ -240,7 +254,7 @@
183
 
184
 #: spa/plugins/alsa/acp/alsa-mixer.c:2657
185
 #: spa/plugins/alsa/acp/alsa-mixer.c:2741
186
-#: spa/plugins/bluez5/bluez5-device.c:1330
187
+#: spa/plugins/bluez5/bluez5-device.c:1536
188
 msgid "Microphone"
189
 msgstr "Мікрофон"
190
 
191
@@ -306,7 +320,7 @@
192
 msgstr "Без підсилення"
193
 
194
 #: spa/plugins/alsa/acp/alsa-mixer.c:2672
195
-#: spa/plugins/bluez5/bluez5-device.c:1335
196
+#: spa/plugins/bluez5/bluez5-device.c:1542
197
 msgid "Speaker"
198
 msgstr "Динамік"
199
 
200
@@ -421,7 +435,7 @@
201
 
202
 #: spa/plugins/alsa/acp/alsa-mixer.c:4484
203
 #: spa/plugins/alsa/acp/alsa-mixer.c:4642
204
-#: spa/plugins/bluez5/bluez5-device.c:1320
205
+#: spa/plugins/bluez5/bluez5-device.c:1524
206
 msgid "Headset"
207
 msgstr "Гарнітура"
208
 
209
@@ -545,7 +559,7 @@
210
 msgid "%s Input"
211
 msgstr "%s-вхід"
212
 
213
-#: spa/plugins/alsa/acp/alsa-util.c:1173 spa/plugins/alsa/acp/alsa-util.c:1267
214
+#: spa/plugins/alsa/acp/alsa-util.c:1187 spa/plugins/alsa/acp/alsa-util.c:1281
215
 #, c-format
216
 msgid ""
217
 "snd_pcm_avail() returned a value that is exceptionally large: %lu byte (%lu "
218
@@ -573,7 +587,7 @@
219
 "Ймовірно, ви натрапили на помилку у драйвері ALSA «%s». Будь ласка, "
220
 "повідомте про цю помилку розробникам ALSA."
221
 
222
-#: spa/plugins/alsa/acp/alsa-util.c:1239
223
+#: spa/plugins/alsa/acp/alsa-util.c:1253
224
 #, c-format
225
 msgid ""
226
 "snd_pcm_delay() returned a value that is exceptionally large: %li byte (%s"
227
@@ -601,7 +615,7 @@
228
 "Ймовірно, ви натрапили на помилку у драйвері ALSA «%s». Будь ласка, "
229
 "повідомте про цю помилку розробникам ALSA."
230
 
231
-#: spa/plugins/alsa/acp/alsa-util.c:1286
232
+#: spa/plugins/alsa/acp/alsa-util.c:1300
233
 #, c-format
234
 msgid ""
235
 "snd_pcm_avail_delay() returned strange values: delay %lu is less than avail "
236
@@ -614,7 +628,7 @@
237
 "Ймовірно, це пов’язано з помилкою у драйвері ALSA «%s». Будь ласка, "
238
 "повідомте про цю помилку розробникам ALSA."
239
 
240
-#: spa/plugins/alsa/acp/alsa-util.c:1329
241
+#: spa/plugins/alsa/acp/alsa-util.c:1343
242
 #, c-format
243
 msgid ""
244
 "snd_pcm_mmap_begin() returned a value that is exceptionally large: %lu byte "
245
@@ -642,7 +656,7 @@
246
 "Ймовірно, ви натрапили на помилку у драйвері ALSA «%s». Будь ласка, "
247
 "повідомте про цю помилку розробникам ALSA."
248
 
249
-#: spa/plugins/alsa/acp/channelmap.h:464
250
+#: spa/plugins/alsa/acp/channelmap.h:457
251
 msgid "(invalid)"
252
 msgstr "(некоректний)"
253
 
254
@@ -654,61 +668,112 @@
255
 msgid "Modem"
256
 msgstr "Модем"
257
 
258
-#: spa/plugins/bluez5/bluez5-device.c:1172
259
+#: spa/plugins/bluez5/bluez5-device.c:1314
260
 msgid "Audio Gateway (A2DP Source & HSP/HFP AG)"
261
 msgstr "Звуковий шлюз (джерело A2DP і HSP/HFP AG)"
262
 
263
-#: spa/plugins/bluez5/bluez5-device.c:1197
264
+#: spa/plugins/bluez5/bluez5-device.c:1339
265
 #, c-format
266
 msgid "High Fidelity Playback (A2DP Sink, codec %s)"
267
 msgstr "Високоточне відтворення (приймач A2DP, кодек %s)"
268
 
269
-#: spa/plugins/bluez5/bluez5-device.c:1200
270
+#: spa/plugins/bluez5/bluez5-device.c:1342
271
 #, c-format
272
 msgid "High Fidelity Duplex (A2DP Source/Sink, codec %s)"
273
 msgstr "Двобічний високоточний обмін (джерело/приймач A2DP, кодек %s)"
274
 
275
-#: spa/plugins/bluez5/bluez5-device.c:1208
276
+#: spa/plugins/bluez5/bluez5-device.c:1350
277
 msgid "High Fidelity Playback (A2DP Sink)"
278
 msgstr "Високоточне відтворення (приймач A2DP)"
279
 
280
-#: spa/plugins/bluez5/bluez5-device.c:1210
281
+#: spa/plugins/bluez5/bluez5-device.c:1352
282
 msgid "High Fidelity Duplex (A2DP Source/Sink)"
283
 msgstr "Двобічний високоточний обмін (джерело/приймач A2DP)"
284
 
285
-#: spa/plugins/bluez5/bluez5-device.c:1238
286
+#: spa/plugins/bluez5/bluez5-device.c:1391
287
+#, c-format
288
+#| msgid "High Fidelity Playback (A2DP Sink, codec %s)"
289
+msgid "High Fidelity Playback (BAP Sink, codec %s)"
290
+msgstr "Високоточне відтворення (приймач BAP, кодек %s)"
291
+
292
+#: spa/plugins/bluez5/bluez5-device.c:1395
293
+#, c-format
294
+#| msgid "High Fidelity Duplex (A2DP Source/Sink, codec %s)"
295
+msgid "High Fidelity Input (BAP Source, codec %s)"
296
+msgstr "Двобічний високоточний вхід (джерело BAP, кодек %s)"
297
+
298
+#: spa/plugins/bluez5/bluez5-device.c:1399
299
+#, c-format
300
+#| msgid "High Fidelity Duplex (A2DP Source/Sink, codec %s)"
301
+msgid "High Fidelity Duplex (BAP Source/Sink, codec %s)"
302
+msgstr "Двобічний високоточний обмін (джерело/приймач BAP, кодек %s)"
303
+
304
+#: spa/plugins/bluez5/bluez5-device.c:1407
305
+#| msgid "High Fidelity Playback (A2DP Sink)"
306
+msgid "High Fidelity Playback (BAP Sink)"
307
+msgstr "Високоточне відтворення (приймач BAP)"
308
+
309
+#: spa/plugins/bluez5/bluez5-device.c:1410
310
+#| msgid "High Fidelity Duplex (A2DP Source/Sink)"
311
+msgid "High Fidelity Input (BAP Source)"
312
+msgstr "Двобічний високоточний вхід (джерело BAP)"
313
+
314
+#: spa/plugins/bluez5/bluez5-device.c:1413
315
+#| msgid "High Fidelity Duplex (A2DP Source/Sink)"
316
+msgid "High Fidelity Duplex (BAP Source/Sink)"
317
+msgstr "Двобічний високоточний обмін (джерело/приймач BAP)"
318
+
319
+#: spa/plugins/bluez5/bluez5-device.c:1441
320
 #, c-format
321
 msgid "Headset Head Unit (HSP/HFP, codec %s)"
322
 msgstr "Головний модуль гарнітури (HSP/HFP, кодек %s)"
323
 
324
-#: spa/plugins/bluez5/bluez5-device.c:1243
325
+#: spa/plugins/bluez5/bluez5-device.c:1446
326
 msgid "Headset Head Unit (HSP/HFP)"
327
 msgstr "Головний модуль гарнітури (HSP/HFP)"
328
 
329
-#: spa/plugins/bluez5/bluez5-device.c:1325
330
+#: spa/plugins/bluez5/bluez5-device.c:1525
331
+#: spa/plugins/bluez5/bluez5-device.c:1530
332
+#: spa/plugins/bluez5/bluez5-device.c:1537
333
+#: spa/plugins/bluez5/bluez5-device.c:1543
334
+#: spa/plugins/bluez5/bluez5-device.c:1549
335
+#: spa/plugins/bluez5/bluez5-device.c:1555
336
+#: spa/plugins/bluez5/bluez5-device.c:1561
337
+#: spa/plugins/bluez5/bluez5-device.c:1567
338
+#: spa/plugins/bluez5/bluez5-device.c:1573
339
 msgid "Handsfree"
340
 msgstr "Hands-Free пристрій"
341
 
342
-#: spa/plugins/bluez5/bluez5-device.c:1340
343
+#: spa/plugins/bluez5/bluez5-device.c:1531
344
+#| msgid "Handsfree"
345
+msgid "Handsfree (HFP)"
346
+msgstr "Hands-Free пристрій (HFP)"
347
+
348
+#: spa/plugins/bluez5/bluez5-device.c:1548
349
 msgid "Headphone"
350
 msgstr "Навушники"
351
 
352
-#: spa/plugins/bluez5/bluez5-device.c:1345
353
+#: spa/plugins/bluez5/bluez5-device.c:1554
354
 msgid "Portable"
355
 msgstr "Портативна акустика"
356
 
357
-#: spa/plugins/bluez5/bluez5-device.c:1350
358
+#: spa/plugins/bluez5/bluez5-device.c:1560
359
 msgid "Car"
360
 msgstr "Автомобільна акустика"
361
 
362
-#: spa/plugins/bluez5/bluez5-device.c:1355
363
+#: spa/plugins/bluez5/bluez5-device.c:1566
364
 msgid "HiFi"
365
 msgstr "Hi-Fi"
366
 
367
-#: spa/plugins/bluez5/bluez5-device.c:1360
368
+#: spa/plugins/bluez5/bluez5-device.c:1572
369
 msgid "Phone"
370
 msgstr "Телефон"
371
 
372
-#: spa/plugins/bluez5/bluez5-device.c:1366
373
+#: spa/plugins/bluez5/bluez5-device.c:1579
374
 msgid "Bluetooth"
375
 msgstr "Bluetooth"
376
+
377
+#: spa/plugins/bluez5/bluez5-device.c:1580
378
+#| msgid "Bluetooth"
379
+msgid "Bluetooth (HFP)"
380
+msgstr "Bluetooth (HFP)"
381
pipewire-0.3.65.tar.gz/spa/include/spa/param/port-config.h -> pipewire-0.3.66.tar.gz/spa/include/spa/param/port-config.h Changed
10
 
1
@@ -48,7 +48,7 @@
2
 /** properties for SPA_TYPE_OBJECT_ParamPortConfig */
3
 enum spa_param_port_config {
4
    SPA_PARAM_PORT_CONFIG_START,
5
-   SPA_PARAM_PORT_CONFIG_direction,    /**< direction, input/output (Id enum spa_direction) */
6
+   SPA_PARAM_PORT_CONFIG_direction,    /**< (Id enum spa_direction) direction */
7
    SPA_PARAM_PORT_CONFIG_mode,     /**< (Id enum spa_param_port_config_mode) mode */
8
    SPA_PARAM_PORT_CONFIG_monitor,      /**< (Bool) enable monitor output ports on input ports */
9
    SPA_PARAM_PORT_CONFIG_control,      /**< (Bool) enable control ports */
10
pipewire-0.3.65.tar.gz/spa/include/spa/utils/defs.h -> pipewire-0.3.66.tar.gz/spa/include/spa/utils/defs.h Changed
10
 
1
@@ -142,7 +142,7 @@
2
    for ((ptr) = arr; (void*)(ptr) < SPA_PTROFF(arr, sizeof(arr), void); (ptr)++)
3
 
4
 #define SPA_FOR_EACH_ELEMENT_VAR(arr, var) \
5
-   for (__typeof__((arr)0)* (var) = arr; (void*)(var) < SPA_PTROFF(arr, sizeof(arr), void); (var)++)
6
+   for (__typeof__((arr)0)* var = arr; (void*)(var) < SPA_PTROFF(arr, sizeof(arr), void); (var)++)
7
 
8
 #define SPA_ABS(a)         \
9
 ({                 \
10
pipewire-0.3.65.tar.gz/spa/meson.build -> pipewire-0.3.66.tar.gz/spa/meson.build Changed
28
 
1
@@ -41,14 +41,23 @@
2
   # plugin-specific dependencies
3
   alsa_dep = dependency('alsa', required: get_option('alsa'))
4
   summary({'ALSA': alsa_dep.found()}, bool_yn: true, section: 'Backend')
5
+
6
   bluez_dep = dependency('bluez', version : '>= 4.101', required: get_option('bluez5'))
7
   gio_dep = dependency('gio-2.0', required : get_option('bluez5'))
8
   gio_unix_dep = dependency('gio-unix-2.0', required : get_option('bluez5'))
9
-  bluez_deps_found = bluez_dep.found() and gio_dep.found() and gio_unix_dep.found()
10
+  bluez_glib2_dep = dependency('glib-2.0', required : get_option('bluez5'))
11
+  sbc_dep = dependency('sbc', required: get_option('bluez5'))
12
+  summary({'SBC': sbc_dep.found()}, bool_yn: true, section: 'Bluetooth audio codecs')
13
+  bluez5_deps =  mathlib, dbus_dep, sbc_dep, bluez_dep, bluez_glib2_dep, gio_dep, gio_unix_dep 
14
+  bluez_deps_found = get_option('bluez5').allowed()
15
+  foreach dep: bluez5_deps
16
+      if get_option('bluez5').enabled() and not dep.found()
17
+        error('bluez5 enabled, but dependency not found: ' + dep.name())
18
+      endif
19
+      bluez_deps_found = bluez_deps_found and dep.found()
20
+  endforeach
21
   summary({'Bluetooth audio': bluez_deps_found}, bool_yn: true, section: 'Backend')
22
   if bluez_deps_found
23
-    sbc_dep = dependency('sbc', required: get_option('bluez5'))
24
-    summary({'SBC': sbc_dep.found()}, bool_yn: true, section: 'Bluetooth audio codecs')
25
     ldac_dep = dependency('ldacBT-enc', required : get_option('bluez5-codec-ldac'))
26
     summary({'LDAC': ldac_dep.found()}, bool_yn: true, section: 'Bluetooth audio codecs')
27
     ldac_abr_dep = dependency('ldacBT-abr', required : get_option('bluez5-codec-ldac'))
28
pipewire-0.3.65.tar.gz/spa/plugins/alsa/acp/acp.c -> pipewire-0.3.66.tar.gz/spa/plugins/alsa/acp/acp.c Changed
27
 
1
@@ -315,7 +315,7 @@
2
 
3
    ss.format = PA_SAMPLE_S32LE;
4
    ss.rate = impl->rate;
5
-   ss.channels = 64;
6
+   ss.channels = impl->pro_channels;
7
 
8
    ap = pa_xnew0(pa_alsa_profile, 1);
9
    ap->profile_set = ps;
10
@@ -1564,6 +1564,7 @@
11
    impl->auto_port = true;
12
    impl->ignore_dB = false;
13
    impl->rate = DEFAULT_RATE;
14
+   impl->pro_channels = 64;
15
 
16
    if (props) {
17
        if ((s = acp_dict_lookup(props, "api.alsa.use-ucm")) != NULL)
18
@@ -1582,6 +1583,8 @@
19
            impl->auto_port = spa_atob(s);
20
        if ((s = acp_dict_lookup(props, "api.acp.probe-rate")) != NULL)
21
            impl->rate = atoi(s);
22
+       if ((s = acp_dict_lookup(props, "api.acp.pro-channels")) != NULL)
23
+           impl->pro_channels = atoi(s);
24
    }
25
 
26
    impl->ucm.default_sample_spec.format = PA_SAMPLE_S16NE;
27
pipewire-0.3.65.tar.gz/spa/plugins/alsa/acp/card.h -> pipewire-0.3.66.tar.gz/spa/plugins/alsa/acp/card.h Changed
9
 
1
@@ -48,6 +48,7 @@
2
    bool auto_port;
3
    bool ignore_dB;
4
    uint32_t rate;
5
+   uint32_t pro_channels;
6
 
7
    pa_alsa_ucm_config ucm;
8
    pa_alsa_profile_set *profile_set;
9
pipewire-0.3.65.tar.gz/spa/plugins/alsa/alsa-pcm.c -> pipewire-0.3.66.tar.gz/spa/plugins/alsa/alsa-pcm.c Changed
10
 
1
@@ -1879,6 +1879,8 @@
2
    } else {
3
        *delay = avail;
4
        *target = SPA_MAX(*target, state->read_size);
5
+       if (state->matching)
6
+           *target += 32;
7
    }
8
    *target = SPA_CLAMP(*target, state->min_delay, state->buffer_frames);
9
    return 0;
10
pipewire-0.3.65.tar.gz/spa/plugins/audioconvert/channelmix-ops.c -> pipewire-0.3.66.tar.gz/spa/plugins/audioconvert/channelmix-ops.c Changed
623
 
1
@@ -142,18 +142,67 @@
2
 #define MATRIX_DOLBY   1
3
 #define MATRIX_DPLII   2
4
 
5
-#define _CH(ch)        ((SPA_AUDIO_CHANNEL_ ## ch)-3)
6
+#define _SH        2
7
+#define _CH(ch)        ((SPA_AUDIO_CHANNEL_ ## ch)-_SH)
8
 #define _MASK(ch)  (1ULL << _CH(ch))
9
 #define FRONT      (_MASK(FC))
10
 #define STEREO     (_MASK(FL)|_MASK(FR))
11
 #define REAR       (_MASK(RL)|_MASK(RR))
12
 #define SIDE       (_MASK(SL)|_MASK(SR))
13
 
14
+static uint32_t mask_to_ch(struct channelmix *mix, uint64_t mask)
15
+{
16
+   uint32_t ch = 0;
17
+   while (mask > 1) {
18
+       ch++;
19
+       mask >>= 1;
20
+   }
21
+   return ch;
22
+}
23
+
24
+static void distribute_mix(struct channelmix *mix,
25
+       float matrixSPA_AUDIO_MAX_CHANNELSSPA_AUDIO_MAX_CHANNELS,
26
+       uint64_t mask)
27
+{
28
+   uint32_t i, ch = mask_to_ch(mix, mask);
29
+   for (i = 0; i < SPA_AUDIO_MAX_CHANNELS; i++)
30
+       matrixich= 1.0f;
31
+}
32
+static void average_mix(struct channelmix *mix,
33
+       float matrixSPA_AUDIO_MAX_CHANNELSSPA_AUDIO_MAX_CHANNELS,
34
+       uint64_t mask)
35
+{
36
+   uint32_t i, ch = mask_to_ch(mix, mask);
37
+   for (i = 0; i < SPA_AUDIO_MAX_CHANNELS; i++)
38
+       matrixchi= 1.0f;
39
+}
40
+static void pair_mix(float matrixSPA_AUDIO_MAX_CHANNELSSPA_AUDIO_MAX_CHANNELS)
41
+{
42
+   uint32_t i;
43
+   for (i = 0; i < SPA_AUDIO_MAX_CHANNELS; i++)
44
+       matrixii= 1.0f;
45
+}
46
+static bool match_mix(struct channelmix *mix,
47
+       float matrixSPA_AUDIO_MAX_CHANNELSSPA_AUDIO_MAX_CHANNELS,
48
+       uint64_t src_mask, uint64_t dst_mask)
49
+{
50
+   bool matched = false;
51
+   uint32_t i;
52
+   for (i = 0; i < SPA_AUDIO_MAX_CHANNELS; i++) {
53
+       if ((src_mask & dst_mask & (1ULL << i))) {
54
+           spa_log_info(mix->log, "matched channel %u (%f)", i, 1.0f);
55
+           matrixii = 1.0f;
56
+           matched = true;
57
+       }
58
+   }
59
+   return matched;
60
+}
61
+
62
 static int make_matrix(struct channelmix *mix)
63
 {
64
    float matrixSPA_AUDIO_MAX_CHANNELSSPA_AUDIO_MAX_CHANNELS = {{ 0.0f }};
65
-   uint64_t src_mask = mix->src_mask;
66
-   uint64_t dst_mask = mix->dst_mask;
67
+   uint64_t src_mask = mix->src_mask, src_paired;
68
+   uint64_t dst_mask = mix->dst_mask, dst_paired;
69
    uint32_t src_chan = mix->src_chan;
70
    uint32_t dst_chan = mix->dst_chan;
71
    uint64_t unassigned, keep;
72
@@ -162,89 +211,110 @@
73
    float slev = SQRT1_2;
74
    float llev = 0.5f;
75
    float maxsum = 0.0f;
76
-   bool filter_fc = false, filter_lfe = false;
77
+   bool filter_fc = false, filter_lfe = false, matched = false, normalize;
78
 #define _MATRIX(s,d)   matrix_CH(s)_CH(d)
79
 
80
+   normalize =  SPA_FLAG_IS_SET(mix->options, CHANNELMIX_OPTION_NORMALIZE);
81
+
82
    spa_log_debug(mix->log, "src-mask:%08"PRIx64" dst-mask:%08"PRIx64
83
            " options:%08x", src_mask, dst_mask, mix->options);
84
 
85
-   /* move the MONO mask to FRONT so that the lower bits can be shifted
86
-    * away. */
87
-   if ((src_mask & (1Ull << SPA_AUDIO_CHANNEL_MONO)) != 0) {
88
-       if (src_chan == 1)
89
-           src_mask = 0;
90
-       else
91
-           src_mask |= (1ULL << SPA_AUDIO_CHANNEL_FC);
92
-   }
93
-   if ((dst_mask & (1Ull << SPA_AUDIO_CHANNEL_MONO)) != 0)
94
-       dst_mask |= (1ULL << SPA_AUDIO_CHANNEL_FC);
95
+   /* shift so that bit 0 is MONO */
96
+   src_mask >>= _SH;
97
+   dst_mask >>= _SH;
98
 
99
-   /* shift so that bit 0 is FL */
100
-   src_mask >>= 3;
101
-   dst_mask >>= 3;
102
+   if (src_chan > 1 && (src_mask & _MASK(MONO)))
103
+       src_mask = 0;
104
+   if (dst_chan > 1 && (dst_mask & _MASK(MONO)))
105
+       dst_mask = 0;
106
 
107
-   /* unknown channels or just 1 channel */
108
+   src_paired = src_mask;
109
+   dst_paired = dst_mask;
110
+
111
+   /* unknown channels */
112
    if (src_mask == 0 || dst_mask == 0) {
113
        if (src_chan == 1) {
114
-           /* one FC/MONO src goes everywhere */
115
-           spa_log_debug(mix->log, "distribute FC/MONO (%f)", 1.0f);
116
-           for (i = 0; i < SPA_AUDIO_MAX_CHANNELS; i++)
117
-               matrixi0= 1.0f;
118
+           /* one src channel goes everywhere */
119
+           spa_log_info(mix->log, "distribute UNK (%f) %"PRIu64, 1.0f, src_mask);
120
+           distribute_mix(mix, matrix, src_mask);
121
        } else if (dst_chan == 1) {
122
-           /* one FC/MONO dst get average of everything */
123
-           spa_log_debug(mix->log, "average FC/MONO (%f)", 1.0f / src_chan);
124
-           for (i = 0; i < SPA_AUDIO_MAX_CHANNELS; i++)
125
-               matrix0i= 1.0f / src_chan;
126
+           /* one dst channel get average of everything */
127
+           spa_log_info(mix->log, "average UNK (%f) %"PRIu64, 1.0f / src_chan, dst_mask);
128
+           average_mix(mix, matrix, dst_mask);
129
+           normalize = true;
130
        } else {
131
            /* just pair channels */
132
-           spa_log_debug(mix->log, "pairing channels (%f)", 1.0f);
133
-           for (i = 0; i < SPA_AUDIO_MAX_CHANNELS; i++)
134
-               matrixii= 1.0f;
135
+           spa_log_info(mix->log, "pairing UNK channels (%f)", 1.0f);
136
+           if (src_mask == 0)
137
+               src_paired = dst_mask;
138
+           else if (dst_mask == 0)
139
+               dst_paired = src_mask;
140
+           pair_mix(matrix);
141
        }
142
-       if (dst_mask & FRONT)
143
-           filter_fc = true;
144
-       if (dst_mask & _MASK(LFE))
145
-           filter_lfe = true;
146
-       src_mask = dst_mask = ~0LU;
147
        goto done;
148
    } else {
149
        spa_log_debug(mix->log, "matching channels");
150
-       for (i = 0; i < SPA_AUDIO_MAX_CHANNELS; i++) {
151
-           if ((src_mask & dst_mask & (1ULL << i))) {
152
-               spa_log_debug(mix->log, "matched channel %u (%f)", i, 1.0f);
153
-               matrixii= 1.0f;
154
-           }
155
-       }
156
+       matched = match_mix(mix, matrix, src_mask, dst_mask);
157
    }
158
 
159
    unassigned = src_mask & ~dst_mask;
160
    keep = dst_mask & ~src_mask;
161
 
162
    if (!SPA_FLAG_IS_SET(mix->options, CHANNELMIX_OPTION_UPMIX)) {
163
+       /* upmix completely disabled */
164
        keep = 0;
165
    } else {
166
+       /* some upmixing (FC and LFE) enabled. */
167
        if (mix->upmix == CHANNELMIX_UPMIX_NONE)
168
            keep = 0;
169
-       keep |= FRONT;
170
+       if (mix->fc_cutoff > 0.0f)
171
+           keep |= FRONT;
172
+       else
173
+           keep &= ~FRONT;
174
        if (mix->lfe_cutoff > 0.0f)
175
            keep |= _MASK(LFE);
176
        else
177
            keep &= ~_MASK(LFE);
178
    }
179
+   /* if we have no channel matched, try to upmix or keep the stereo
180
+    * pair or else we might end up with silence. */
181
+   if (dst_mask & STEREO && !matched)
182
+       keep |= STEREO;
183
+
184
+   spa_log_info(mix->log, "unassigned downmix %08" PRIx64 " %08" PRIx64, unassigned, keep);
185
+
186
+   if (unassigned & _MASK(MONO)) {
187
+       if ((dst_mask & STEREO) == STEREO) {
188
+           spa_log_info(mix->log, "assign MONO to STEREO (%f)", 1.0f);
189
+           _MATRIX(FL,MONO) += 1.0f;
190
+           _MATRIX(FR,MONO) += 1.0f;
191
+           keep &= ~STEREO;
192
+       } else if ((dst_mask & FRONT) == FRONT) {
193
+           spa_log_info(mix->log, "assign MONO to FRONT (%f)", 1.0f);
194
+           _MATRIX(FC,MONO) += 1.0f;
195
+           normalize = true;
196
+       } else {
197
+           spa_log_warn(mix->log, "can't assign MONO");
198
+       }
199
+   }
200
 
201
-   spa_log_debug(mix->log, "unassigned downmix %08" PRIx64 " %08" PRIx64, unassigned, keep);
202
-
203
-   if (unassigned & FRONT){
204
+   if (unassigned & FRONT) {
205
        if ((dst_mask & STEREO) == STEREO){
206
-           if(src_mask & STEREO) {
207
-               spa_log_debug(mix->log, "assign FC to STEREO (%f)", clev);
208
+           if (src_mask & STEREO) {
209
+               spa_log_info(mix->log, "assign FC to STEREO (%f)", clev);
210
                _MATRIX(FL,FC) += clev;
211
                _MATRIX(FR,FC) += clev;
212
            } else {
213
-               spa_log_debug(mix->log, "assign FC to STEREO (%f)", SQRT1_2);
214
+               spa_log_info(mix->log, "assign FC to STEREO (%f)", SQRT1_2);
215
                _MATRIX(FL,FC) += SQRT1_2;
216
                _MATRIX(FR,FC) += SQRT1_2;
217
            }
218
+           keep &= ~STEREO;
219
+       } else if (dst_mask & _MASK(MONO)){
220
+           spa_log_info(mix->log, "assign FC to MONO (%f)", 1.0f);
221
+           for (i = 0; i < SPA_AUDIO_MAX_CHANNELS; i++)
222
+               matrixi_CH(FC)= 1.0f;
223
+           normalize = true;
224
        } else {
225
            spa_log_warn(mix->log, "can't assign FC");
226
        }
227
@@ -252,14 +322,21 @@
228
 
229
    if (unassigned & STEREO){
230
        if (dst_mask & FRONT) {
231
-           spa_log_debug(mix->log, "assign STEREO to FC (%f)", SQRT1_2);
232
+           spa_log_info(mix->log, "assign STEREO to FC (%f)", SQRT1_2);
233
            _MATRIX(FC,FL) += SQRT1_2;
234
            _MATRIX(FC,FR) += SQRT1_2;
235
            if (src_mask & FRONT) {
236
-               spa_log_debug(mix->log, "assign FC to FC (%f)", clev * SQRT2);
237
+               spa_log_info(mix->log, "assign FC to FC (%f)", clev * SQRT2);
238
                _MATRIX(FC,FC) = clev * SQRT2;
239
            }
240
            keep &= ~FRONT;
241
+       } else if ((dst_mask & _MASK(MONO))){
242
+           spa_log_info(mix->log, "assign STEREO to MONO (%f)", 1.0f);
243
+           for (i = 0; i < SPA_AUDIO_MAX_CHANNELS; i++) {
244
+               matrixi_CH(FL)= 1.0f;
245
+               matrixi_CH(FR)= 1.0f;
246
+           }
247
+           normalize = true;
248
        } else {
249
            spa_log_warn(mix->log, "can't assign STEREO");
250
        }
251
@@ -267,15 +344,15 @@
252
 
253
    if (unassigned & _MASK(RC)) {
254
        if (dst_mask & REAR){
255
-           spa_log_debug(mix->log, "assign RC to RL+RR (%f)", SQRT1_2);
256
+           spa_log_info(mix->log, "assign RC to RL+RR (%f)", SQRT1_2);
257
            _MATRIX(RL,RC) += SQRT1_2;
258
            _MATRIX(RR,RC) += SQRT1_2;
259
        } else if (dst_mask & SIDE) {
260
-           spa_log_debug(mix->log, "assign RC to SL+SR (%f)", SQRT1_2);
261
+           spa_log_info(mix->log, "assign RC to SL+SR (%f)", SQRT1_2);
262
            _MATRIX(SL,RC) += SQRT1_2;
263
            _MATRIX(SR,RC) += SQRT1_2;
264
        } else if(dst_mask & STEREO) {
265
-           spa_log_debug(mix->log, "assign RC to FL+FR");
266
+           spa_log_info(mix->log, "assign RC to FL+FR");
267
            if (matrix_encoding == MATRIX_DOLBY ||
268
                matrix_encoding == MATRIX_DPLII) {
269
                if (unassigned & (_MASK(RL)|_MASK(RR))) {
270
@@ -290,8 +367,13 @@
271
                _MATRIX(FR,RC) += slev * SQRT1_2;
272
            }
273
        } else if (dst_mask & FRONT) {
274
-           spa_log_debug(mix->log, "assign RC to FC (%f)", slev * SQRT1_2);
275
+           spa_log_info(mix->log, "assign RC to FC (%f)", slev * SQRT1_2);
276
            _MATRIX(FC,RC) += slev * SQRT1_2;
277
+       } else if (dst_mask & _MASK(MONO)){
278
+           spa_log_info(mix->log, "assign RC to MONO (%f)", 1.0f);
279
+           for (i = 0; i < SPA_AUDIO_MAX_CHANNELS; i++)
280
+               matrixi_CH(RC)= 1.0f;
281
+           normalize = true;
282
        } else {
283
            spa_log_warn(mix->log, "can't assign RC");
284
        }
285
@@ -299,11 +381,11 @@
286
 
287
    if (unassigned & REAR) {
288
        if (dst_mask & _MASK(RC)) {
289
-           spa_log_debug(mix->log, "assign RL+RR to RC");
290
+           spa_log_info(mix->log, "assign RL+RR to RC");
291
            _MATRIX(RC,RL) += SQRT1_2;
292
            _MATRIX(RC,RR) += SQRT1_2;
293
        } else if (dst_mask & SIDE) {
294
-           spa_log_debug(mix->log, "assign RL+RR to SL+SR");
295
+           spa_log_info(mix->log, "assign RL+RR to SL+SR");
296
            if (src_mask & SIDE) {
297
                _MATRIX(SL,RL) += SQRT1_2;
298
                _MATRIX(SR,RR) += SQRT1_2;
299
@@ -313,7 +395,7 @@
300
            }
301
            keep &= ~SIDE;
302
        } else if (dst_mask & STEREO) {
303
-           spa_log_debug(mix->log, "assign RL+RR to FL+FR (%f)", slev);
304
+           spa_log_info(mix->log, "assign RL+RR to FL+FR (%f)", slev);
305
            if (matrix_encoding == MATRIX_DOLBY) {
306
                _MATRIX(FL,RL) -= slev * SQRT1_2;
307
                _MATRIX(FL,RR) -= slev * SQRT1_2;
308
@@ -329,10 +411,17 @@
309
                _MATRIX(FR,RR) += slev;
310
            }
311
        } else if (dst_mask & FRONT) {
312
-           spa_log_debug(mix->log, "assign RL+RR to FC (%f)",
313
+           spa_log_info(mix->log, "assign RL+RR to FC (%f)",
314
                    slev * SQRT1_2);
315
            _MATRIX(FC,RL)+= slev * SQRT1_2;
316
            _MATRIX(FC,RR)+= slev * SQRT1_2;
317
+       } else if (dst_mask & _MASK(MONO)){
318
+           spa_log_info(mix->log, "assign RL+RR to MONO (%f)", 1.0f);
319
+           for (i = 0; i < SPA_AUDIO_MAX_CHANNELS; i++) {
320
+               matrixi_CH(RL)= 1.0f;
321
+               matrixi_CH(RR)= 1.0f;
322
+           }
323
+           normalize = true;
324
        } else {
325
            spa_log_warn(mix->log, "can't assign RL");
326
        }
327
@@ -341,43 +430,50 @@
328
    if (unassigned & SIDE) {
329
        if (dst_mask & REAR) {
330
            if (src_mask & _MASK(RL)) {
331
-               spa_log_debug(mix->log, "assign SL+SR to RL+RR (%f)", SQRT1_2);
332
+               spa_log_info(mix->log, "assign SL+SR to RL+RR (%f)", SQRT1_2);
333
                _MATRIX(RL,SL) += SQRT1_2;
334
                _MATRIX(RR,SR) += SQRT1_2;
335
            } else {
336
-               spa_log_debug(mix->log, "assign SL+SR to RL+RR (%f)", 1.0f);
337
+               spa_log_info(mix->log, "assign SL+SR to RL+RR (%f)", 1.0f);
338
                _MATRIX(RL,SL) += 1.0f;
339
                _MATRIX(RR,SR) += 1.0f;
340
            }
341
            keep &= ~REAR;
342
        } else if (dst_mask & _MASK(RC)) {
343
-           spa_log_debug(mix->log, "assign SL+SR to RC (%f)", SQRT1_2);
344
+           spa_log_info(mix->log, "assign SL+SR to RC (%f)", SQRT1_2);
345
            _MATRIX(RC,SL)+= SQRT1_2;
346
            _MATRIX(RC,SR)+= SQRT1_2;
347
        } else if (dst_mask & STEREO) {
348
            if (matrix_encoding == MATRIX_DOLBY) {
349
-               spa_log_debug(mix->log, "assign SL+SR to FL+FR (%f)",
350
+               spa_log_info(mix->log, "assign SL+SR to FL+FR (%f)",
351
                        slev * SQRT1_2);
352
                _MATRIX(FL,SL) -= slev * SQRT1_2;
353
                _MATRIX(FL,SR) -= slev * SQRT1_2;
354
                _MATRIX(FR,SL) += slev * SQRT1_2;
355
                _MATRIX(FR,SR) += slev * SQRT1_2;
356
            } else if (matrix_encoding == MATRIX_DPLII) {
357
-               spa_log_debug(mix->log, "assign SL+SR to FL+FR (%f / %f)",
358
+               spa_log_info(mix->log, "assign SL+SR to FL+FR (%f / %f)",
359
                        slev * SQRT3_2, slev * SQRT1_2);
360
                _MATRIX(FL,SL) -= slev * SQRT3_2;
361
                _MATRIX(FL,SR) -= slev * SQRT1_2;
362
                _MATRIX(FR,SL) += slev * SQRT1_2;
363
                _MATRIX(FR,SR) += slev * SQRT3_2;
364
            } else {
365
-               spa_log_debug(mix->log, "assign SL+SR to FL+FR (%f)", slev);
366
+               spa_log_info(mix->log, "assign SL+SR to FL+FR (%f)", slev);
367
                _MATRIX(FL,SL) += slev;
368
                _MATRIX(FR,SR) += slev;
369
            }
370
        } else if (dst_mask & FRONT) {
371
-           spa_log_debug(mix->log, "assign SL+SR to FC (%f)", slev * SQRT1_2);
372
+           spa_log_info(mix->log, "assign SL+SR to FC (%f)", slev * SQRT1_2);
373
            _MATRIX(FC,SL) += slev * SQRT1_2;
374
            _MATRIX(FC,SR) += slev * SQRT1_2;
375
+       } else if (dst_mask & _MASK(MONO)){
376
+           spa_log_info(mix->log, "assign SL+SR to MONO (%f)", 1.0f);
377
+           for (i = 0; i < SPA_AUDIO_MAX_CHANNELS; i++) {
378
+               matrixi_CH(SL)= 1.0f;
379
+               matrixi_CH(SR)= 1.0f;
380
+           }
381
+           normalize = true;
382
        } else {
383
            spa_log_warn(mix->log, "can't assign SL");
384
        }
385
@@ -385,13 +481,20 @@
386
 
387
    if (unassigned & _MASK(FLC)) {
388
        if (dst_mask & STEREO) {
389
-           spa_log_debug(mix->log, "assign FLC+FRC to FL+FR (%f)", 1.0f);
390
+           spa_log_info(mix->log, "assign FLC+FRC to FL+FR (%f)", 1.0f);
391
            _MATRIX(FL,FLC)+= 1.0f;
392
            _MATRIX(FR,FRC)+= 1.0f;
393
        } else if(dst_mask & FRONT) {
394
-           spa_log_debug(mix->log, "assign FLC+FRC to FC (%f)", SQRT1_2);
395
+           spa_log_info(mix->log, "assign FLC+FRC to FC (%f)", SQRT1_2);
396
            _MATRIX(FC,FLC)+= SQRT1_2;
397
            _MATRIX(FC,FRC)+= SQRT1_2;
398
+       } else if (dst_mask & _MASK(MONO)){
399
+           spa_log_info(mix->log, "assign FLC+FRC to MONO (%f)", 1.0f);
400
+           for (i = 0; i < SPA_AUDIO_MAX_CHANNELS; i++) {
401
+               matrixi_CH(FLC)= 1.0f;
402
+               matrixi_CH(FRC)= 1.0f;
403
+           }
404
+           normalize = true;
405
        } else {
406
            spa_log_warn(mix->log, "can't assign FLC");
407
        }
408
@@ -399,13 +502,18 @@
409
    if (unassigned & _MASK(LFE) &&
410
        SPA_FLAG_IS_SET(mix->options, CHANNELMIX_OPTION_MIX_LFE)) {
411
        if (dst_mask & FRONT) {
412
-           spa_log_debug(mix->log, "assign LFE to FC (%f)", llev);
413
+           spa_log_info(mix->log, "assign LFE to FC (%f)", llev);
414
            _MATRIX(FC,LFE) += llev;
415
        } else if (dst_mask & STEREO) {
416
-           spa_log_debug(mix->log, "assign LFE to FL+FR (%f)",
417
+           spa_log_info(mix->log, "assign LFE to FL+FR (%f)",
418
                    llev * SQRT1_2);
419
            _MATRIX(FL,LFE) += llev * SQRT1_2;
420
            _MATRIX(FR,LFE) += llev * SQRT1_2;
421
+       } else if ((dst_mask & _MASK(MONO))){
422
+           spa_log_info(mix->log, "assign LFE to MONO (%f)", 1.0f);
423
+           for (i = 0; i < SPA_AUDIO_MAX_CHANNELS; i++)
424
+               matrixi_CH(LFE)= 1.0f;
425
+           normalize = true;
426
        } else {
427
            spa_log_warn(mix->log, "can't assign LFE");
428
        }
429
@@ -413,117 +521,148 @@
430
 
431
    unassigned = dst_mask & ~src_mask & keep;
432
 
433
-   spa_log_debug(mix->log, "unassigned upmix %08"PRIx64" lfe:%f",
434
+   spa_log_info(mix->log, "unassigned upmix %08"PRIx64" lfe:%f",
435
            unassigned, mix->lfe_cutoff);
436
 
437
    if (unassigned & STEREO) {
438
        if ((src_mask & FRONT) == FRONT) {
439
-           spa_log_debug(mix->log, "produce STEREO from FC (%f)", clev);
440
+           spa_log_info(mix->log, "produce STEREO from FC (%f)", clev);
441
            _MATRIX(FL,FC) += clev;
442
            _MATRIX(FR,FC) += clev;
443
+       } else if (src_mask & _MASK(MONO)) {
444
+           spa_log_info(mix->log, "produce STEREO from MONO (%f)", 1.0f);
445
+           _MATRIX(FL,MONO) += 1.0f;
446
+           _MATRIX(FR,MONO) += 1.0f;
447
        } else {
448
            spa_log_warn(mix->log, "can't produce STEREO");
449
        }
450
    }
451
    if (unassigned & FRONT) {
452
        if ((src_mask & STEREO) == STEREO) {
453
-           spa_log_debug(mix->log, "produce FC from STEREO (%f)", clev);
454
+           spa_log_info(mix->log, "produce FC from STEREO (%f)", clev);
455
            _MATRIX(FC,FL) += clev;
456
            _MATRIX(FC,FR) += clev;
457
            filter_fc = true;
458
+       } else if (src_mask & _MASK(MONO)) {
459
+           spa_log_info(mix->log, "produce FC from MONO (%f)", 1.0f);
460
+           _MATRIX(FC,MONO) += 1.0f;
461
+           filter_fc = true;
462
        } else {
463
            spa_log_warn(mix->log, "can't produce FC");
464
        }
465
    }
466
    if (unassigned & _MASK(LFE)) {
467
        if ((src_mask & STEREO) == STEREO) {
468
-           spa_log_debug(mix->log, "produce LFE from STEREO (%f)", llev);
469
+           spa_log_info(mix->log, "produce LFE from STEREO (%f)", llev);
470
            _MATRIX(LFE,FL) += llev;
471
            _MATRIX(LFE,FR) += llev;
472
            filter_lfe = true;
473
        } else if ((src_mask & FRONT) == FRONT) {
474
-           spa_log_debug(mix->log, "produce LFE from FC (%f)", llev);
475
+           spa_log_info(mix->log, "produce LFE from FC (%f)", llev);
476
            _MATRIX(LFE,FC) += llev;
477
            filter_lfe = true;
478
+       } else if (src_mask & _MASK(MONO)) {
479
+           spa_log_info(mix->log, "produce LFE from MONO (%f)", 1.0f);
480
+           _MATRIX(LFE,MONO) += 1.0f;
481
+           filter_lfe = true;
482
        } else {
483
            spa_log_warn(mix->log, "can't produce LFE");
484
        }
485
    }
486
    if (unassigned & SIDE) {
487
        if ((src_mask & REAR) == REAR) {
488
-           spa_log_debug(mix->log, "produce SIDE from REAR (%f)", 1.0f);
489
+           spa_log_info(mix->log, "produce SIDE from REAR (%f)", 1.0f);
490
            _MATRIX(SL,RL) += 1.0f;
491
            _MATRIX(SR,RR) += 1.0f;
492
        } else if ((src_mask & STEREO) == STEREO) {
493
-           spa_log_debug(mix->log, "produce SIDE from STEREO (%f)", slev);
494
+           spa_log_info(mix->log, "produce SIDE from STEREO (%f)", slev);
495
            _MATRIX(SL,FL) += slev;
496
            _MATRIX(SR,FR) += slev;
497
        } else if ((src_mask & FRONT) == FRONT &&
498
            mix->upmix == CHANNELMIX_UPMIX_SIMPLE) {
499
-           spa_log_debug(mix->log, "produce SIDE from FC (%f)", clev);
500
+           spa_log_info(mix->log, "produce SIDE from FC (%f)", clev);
501
            _MATRIX(SL,FC) += clev;
502
            _MATRIX(SR,FC) += clev;
503
+       } else if (src_mask & _MASK(MONO) &&
504
+           mix->upmix == CHANNELMIX_UPMIX_SIMPLE) {
505
+           spa_log_info(mix->log, "produce SIDE from MONO (%f)", 1.0f);
506
+           _MATRIX(SL,MONO) += 1.0f;
507
+           _MATRIX(SR,MONO) += 1.0f;
508
        } else {
509
-           spa_log_debug(mix->log, "won't produce SIDE");
510
+           spa_log_info(mix->log, "won't produce SIDE");
511
        }
512
    }
513
    if (unassigned & REAR) {
514
        if ((src_mask & SIDE) == SIDE) {
515
-           spa_log_debug(mix->log, "produce REAR from SIDE (%f)", 1.0f);
516
+           spa_log_info(mix->log, "produce REAR from SIDE (%f)", 1.0f);
517
            _MATRIX(RL,SL) += 1.0f;
518
            _MATRIX(RR,SR) += 1.0f;
519
        } else if ((src_mask & STEREO) == STEREO) {
520
-           spa_log_debug(mix->log, "produce REAR from STEREO (%f)", slev);
521
+           spa_log_info(mix->log, "produce REAR from STEREO (%f)", slev);
522
            _MATRIX(RL,FL) += slev;
523
            _MATRIX(RR,FR) += slev;
524
        } else if ((src_mask & FRONT) == FRONT &&
525
            mix->upmix == CHANNELMIX_UPMIX_SIMPLE) {
526
-           spa_log_debug(mix->log, "produce REAR from FC (%f)", clev);
527
+           spa_log_info(mix->log, "produce REAR from FC (%f)", clev);
528
            _MATRIX(RL,FC) += clev;
529
            _MATRIX(RR,FC) += clev;
530
+       } else if (src_mask & _MASK(MONO) &&
531
+           mix->upmix == CHANNELMIX_UPMIX_SIMPLE) {
532
+           spa_log_info(mix->log, "produce REAR from MONO (%f)", 1.0f);
533
+           _MATRIX(RL,MONO) += 1.0f;
534
+           _MATRIX(RR,MONO) += 1.0f;
535
        } else {
536
-           spa_log_debug(mix->log, "won't produce SIDE");
537
+           spa_log_info(mix->log, "won't produce SIDE");
538
        }
539
    }
540
    if (unassigned & _MASK(RC)) {
541
        if ((src_mask & REAR) == REAR) {
542
-           spa_log_debug(mix->log, "produce RC from REAR (%f)", 0.5f);
543
+           spa_log_info(mix->log, "produce RC from REAR (%f)", 0.5f);
544
            _MATRIX(RC,RL) += 0.5f;
545
            _MATRIX(RC,RR) += 0.5f;
546
        } else if ((src_mask & SIDE) == SIDE) {
547
-           spa_log_debug(mix->log, "produce RC from SIDE (%f)", 0.5f);
548
+           spa_log_info(mix->log, "produce RC from SIDE (%f)", 0.5f);
549
            _MATRIX(RC,SL) += 0.5f;
550
            _MATRIX(RC,SR) += 0.5f;
551
        } else if ((src_mask & STEREO) == STEREO) {
552
-           spa_log_debug(mix->log, "produce RC from STEREO (%f)", 0.5f);
553
+           spa_log_info(mix->log, "produce RC from STEREO (%f)", 0.5f);
554
            _MATRIX(RC,FL) += 0.5f;
555
            _MATRIX(RC,FR) += 0.5f;
556
        } else if ((src_mask & FRONT) == FRONT &&
557
            mix->upmix == CHANNELMIX_UPMIX_SIMPLE) {
558
-           spa_log_debug(mix->log, "produce RC from FC (%f)", slev);
559
+           spa_log_info(mix->log, "produce RC from FC (%f)", slev);
560
            _MATRIX(RC,FC) += slev;
561
+       } else if (src_mask & _MASK(MONO) &&
562
+           mix->upmix == CHANNELMIX_UPMIX_SIMPLE) {
563
+           spa_log_info(mix->log, "produce RC from MONO (%f)", 1.0f);
564
+           _MATRIX(RC,MONO) += 1.0f;
565
        } else {
566
-           spa_log_debug(mix->log, "won't produce RC");
567
+           spa_log_info(mix->log, "won't produce RC");
568
        }
569
    }
570
 
571
 done:
572
+   if (dst_paired == 0)
573
+       dst_paired = ~0LU;
574
+   if (src_paired == 0)
575
+       src_paired = ~0LU;
576
+
577
    for (jc = 0, ic = 0, i = 0; i < SPA_AUDIO_MAX_CHANNELS; i++) {
578
        float sum = 0.0f;
579
        char str1024, str21024;
580
        int idx = 0, idx2 = 0;
581
-       if ((dst_mask & (1UL << i)) == 0)
582
+       if ((dst_paired & (1UL << i)) == 0)
583
            continue;
584
        for (jc = 0, j = 0; j < SPA_AUDIO_MAX_CHANNELS; j++) {
585
-           if ((src_mask & (1UL << j)) == 0)
586
+           if ((src_paired & (1UL << j)) == 0)
587
                continue;
588
            if (ic >= dst_chan || jc >= src_chan)
589
                continue;
590
 
591
            if (ic == 0)
592
                idx2 += snprintf(str2 + idx2, sizeof(str2) - idx2, "%-4.4s  ",
593
-                       src_mask == ~0LU ? "MONO" :
594
-                       spa_debug_type_find_short_name(spa_type_audio_channel, j + 3));
595
+                       src_mask == 0 ? "UNK" :
596
+                       spa_debug_type_find_short_name(spa_type_audio_channel, j + _SH));
597
 
598
            mix->matrix_origicjc++ = matrixij;
599
            sum += fabs(matrixij);
600
@@ -537,8 +676,8 @@
601
            spa_log_info(mix->log, "     %s", str2);
602
        if (idx > 0) {
603
            spa_log_info(mix->log, "%-4.4s %s   %f",
604
-                   dst_mask == ~0LU ? "MONO" :
605
-                   spa_debug_type_find_short_name(spa_type_audio_channel, i + 3),
606
+                   dst_mask == 0 ? "UNK" :
607
+                   spa_debug_type_find_short_name(spa_type_audio_channel, i + _SH),
608
                    str, sum);
609
        }
610
 
611
@@ -554,9 +693,8 @@
612
        }
613
        ic++;
614
    }
615
-   if (SPA_FLAG_IS_SET(mix->options, CHANNELMIX_OPTION_NORMALIZE) &&
616
-       maxsum > 1.0f) {
617
-       spa_log_debug(mix->log, "normalize %f", maxsum);
618
+   if (normalize && maxsum > 1.0f) {
619
+       spa_log_info(mix->log, "normalize %f", maxsum);
620
        for (i = 0; i < dst_chan; i++)
621
            for (j = 0; j < src_chan; j++)
622
                        mix->matrix_origij /= maxsum;
623
pipewire-0.3.65.tar.gz/spa/plugins/audioconvert/test-audioconvert.c -> pipewire-0.3.66.tar.gz/spa/plugins/audioconvert/test-audioconvert.c Changed
22
 
1
@@ -38,6 +38,7 @@
2
 #include <spa/node/node.h>
3
 #include <spa/node/io.h>
4
 #include <spa/debug/mem.h>
5
+#include <spa/debug/log.h>
6
 #include <spa/support/log-impl.h>
7
 
8
 SPA_LOG_IMPL(logger);
9
@@ -682,8 +683,10 @@
10
            res = memcmp(b->datasj.data, out_data->datak, out_data->size);
11
            if (res != 0) {
12
                fprintf(stderr, "error port %d plane %d\n", i, j);
13
-               spa_debug_mem(0, b->datasj.data, out_data->size);
14
-               spa_debug_mem(0, out_data->datak, out_data->size);
15
+               spa_debug_log_mem(&logger.log, SPA_LOG_LEVEL_WARN,
16
+                       0, b->datasj.data, out_data->size);
17
+               spa_debug_log_mem(&logger.log, SPA_LOG_LEVEL_WARN,
18
+                       2, out_data->datak, out_data->size);
19
            }
20
            spa_assert_se(res == 0);
21
 
22
pipewire-0.3.65.tar.gz/spa/plugins/audioconvert/test-channelmix.c -> pipewire-0.3.66.tar.gz/spa/plugins/audioconvert/test-channelmix.c Changed
135
 
1
@@ -72,6 +72,8 @@
2
    mix.src_mask = src_mask;
3
    mix.dst_mask = dst_mask;
4
    mix.log = &logger.log;
5
+   mix.fc_cutoff = 120.0f;
6
+   mix.lfe_cutoff = 12000.0f;
7
 
8
    spa_assert_se(channelmix_init(&mix) == 0);
9
    channelmix_set_volume(&mix, 1.0f, false, 0, NULL);
10
@@ -83,11 +85,17 @@
11
    test_mix(1, _M(MONO), 2, _M(FL)|_M(FR), 0,
12
            MATRIX(1.0, 1.0));
13
    test_mix(1, _M(MONO), 3, _M(FL)|_M(FR)|_M(LFE), 0,
14
+           MATRIX(1.0, 1.0, 0.0));
15
+   test_mix(1, _M(MONO), 3, _M(FL)|_M(FR)|_M(LFE), CHANNELMIX_OPTION_UPMIX,
16
            MATRIX(1.0, 1.0, 1.0));
17
    test_mix(1, _M(MONO), 4, _M(FL)|_M(FR)|_M(LFE)|_M(FC), 0,
18
+           MATRIX(1.0, 1.0, 0.0, 0.0));
19
+   test_mix(1, _M(MONO), 4, _M(FL)|_M(FR)|_M(LFE)|_M(FC), CHANNELMIX_OPTION_UPMIX,
20
            MATRIX(1.0, 1.0, 1.0, 1.0));
21
    test_mix(1, _M(MONO), 4, _M(FL)|_M(FR)|_M(RL)|_M(RR), 0,
22
-           MATRIX(1.0, 1.0, 1.0, 1.0));
23
+           MATRIX(1.0, 1.0, 0.0, 0.0));
24
+   test_mix(1, _M(MONO), 4, _M(FL)|_M(FR)|_M(RL)|_M(RR), CHANNELMIX_OPTION_UPMIX,
25
+           MATRIX(1.0, 1.0, 0.0, 0.0));
26
    test_mix(1, _M(MONO), 12, 0, 0,
27
            MATRIX(1.0, 1.0, 1.0, 1.0, 1.0, 1.0,
28
                   1.0, 1.0, 1.0, 1.0, 1.0, 1.0));
29
@@ -119,16 +127,49 @@
30
    test_mix(1, _M(FC), 1, _M(FC), 0,
31
            MATRIX(1.0));
32
    test_mix(2, _M(FL)|_M(FR), 1, _M(MONO), 0,
33
-           MATRIX(0.707107, 0.707107));
34
+           MATRIX(0.5, 0.5));
35
    test_mix(12, 0, 1, _M(MONO), 0,
36
            MATRIX(0.083333, 0.083333, 0.083333, 0.083333, 0.083333, 0.083333,
37
                   0.083333, 0.083333, 0.083333, 0.083333, 0.083333, 0.0833333));
38
 }
39
 
40
+static void test_2_N(void)
41
+{
42
+   test_mix(2, _M(FL)|_M(FR), 1, _M(MONO), 0, MATRIX(0.5, 0.5));
43
+   test_mix(2, _M(FL)|_M(FR), 1, 0, 0, MATRIX(0.5, 0.5));
44
+   test_mix(2, _M(FL)|_M(FR), 2, 0, 0, MATRIX(1.0, 0.0, 0.0, 1.0));
45
+   test_mix(2, _M(FL)|_M(FR), 2, _M(MONO), 0, MATRIX(1.0, 0.0, 0.0, 1.0));
46
+   test_mix(2, _M(FL)|_M(FR), 2, _M(FL)|_M(FR), 0, MATRIX(1.0, 0.0, 0.0, 1.0));
47
+   test_mix(2, _M(FL)|_M(FR), 4, _M(FL)|_M(FR)|_M(LFE)|_M(FC), 0,
48
+           MATRIX(1.0, 0.0,
49
+                  0.0, 1.0,
50
+                  0.0, 0.0,
51
+                  0.0, 0.0));
52
+   test_mix(2, _M(FL)|_M(FR), 4, _M(FL)|_M(FR)|_M(LFE)|_M(FC), CHANNELMIX_OPTION_UPMIX,
53
+           MATRIX(1.0, 0.0,
54
+                  0.0, 1.0,
55
+                  0.707107, 0.707107,
56
+                  0.5, 0.5));
57
+   test_mix(2, _M(FL)|_M(FR), 6, _M(FL)|_M(FR)|_M(LFE)|_M(FC)|_M(SL)|_M(SR), 0,
58
+           MATRIX(1.0, 0.0,
59
+                  0.0, 1.0,
60
+                  0.0, 0.0,
61
+                  0.0, 0.0,
62
+                  0.0, 0.0,
63
+                  0.0, 0.0));
64
+   test_mix(2, _M(FL)|_M(FR), 6, _M(FL)|_M(FR)|_M(LFE)|_M(FC)|_M(SL)|_M(SR), CHANNELMIX_OPTION_UPMIX,
65
+           MATRIX(1.0, 0.0,
66
+                  0.0, 1.0,
67
+                  0.707107, 0.707107,
68
+                  0.5, 0.5,
69
+                  0.0, 0.0,
70
+                  0.0, 0.0));
71
+}
72
+
73
 static void test_3p1_N(void)
74
 {
75
    test_mix(4, _M(FL)|_M(FR)|_M(LFE)|_M(FC), 1, _M(MONO), 0,
76
-           MATRIX(0.707107, 0.707107, 1.0, 0.0));
77
+           MATRIX(0.333333, 0.333333, 0.333333, 0.0));
78
    test_mix(4, _M(FL)|_M(FR)|_M(LFE)|_M(FC), 2, _M(FL)|_M(FR), 0,
79
            MATRIX(1.0, 0.0, 0.707107, 0.0,
80
                   0.0, 1.0, 0.707107, 0.0 ));
81
@@ -151,9 +192,9 @@
82
 static void test_4_N(void)
83
 {
84
    test_mix(4, _M(FL)|_M(FR)|_M(RL)|_M(RR), 1, _M(MONO), 0,
85
-           MATRIX(0.707107, 0.707107, 0.5, 0.5));
86
+           MATRIX(0.25, 0.25, 0.25, 0.25));
87
    test_mix(4, _M(FL)|_M(FR)|_M(SL)|_M(SR), 1, _M(MONO), 0,
88
-           MATRIX(0.707107, 0.707107, 0.5, 0.5));
89
+           MATRIX(0.25, 0.25, 0.25, 0.25));
90
    test_mix(4, _M(FL)|_M(FR)|_M(RL)|_M(RR), 2, _M(FL)|_M(FR), 0,
91
            MATRIX(1.0, 0.0, 0.707107, 0.0,
92
                   0.0, 1.0, 0.0, 0.707107));
93
@@ -178,13 +219,13 @@
94
            MATRIX(1.0, 0.0, 0.707107, 0.0,
95
                   0.0, 1.0, 0.0, 0.707107,
96
                   0.707107, 0.707107, 0.0, 0.0,
97
-                  0.0, 0.0, 0.0, 0.0));
98
+                  0.5, 0.5, 0.0, 0.0));
99
 }
100
 
101
 static void test_5p1_N(void)
102
 {
103
    test_mix(6, _M(FL)|_M(FR)|_M(LFE)|_M(FC)|_M(SL)|_M(SR), 1, _M(MONO), 0,
104
-           MATRIX(0.707107, 0.707107, 1.0, 0.0, 0.5, 0.5));
105
+           MATRIX(0.20, 0.20, 0.20, 0.0, 0.20, 0.20));
106
    test_mix(6, _M(FL)|_M(FR)|_M(LFE)|_M(FC)|_M(SL)|_M(SR), 2, _M(FL)|_M(FR), 0,
107
            MATRIX(1.0, 0.0, 0.707107, 0.0, 0.707107, 0.0,
108
                   0.0, 1.0, 0.707107, 0.0, 0.0, 0.707107));
109
@@ -223,7 +264,7 @@
110
 static void test_6p1_N(void)
111
 {
112
    test_mix(7, _M(FL)|_M(FR)|_M(LFE)|_M(FC)|_M(RC)|_M(SL)|_M(SR), 1, _M(MONO), 0,
113
-           MATRIX(0.707107, 0.707107, 1.0, 0.0, 0.5, 0.5, 0.5));
114
+           MATRIX(0.166667, 0.166667, 0.166667, 0.0, 0.166667, 0.166667, 0.166667));
115
    test_mix(7, _M(FL)|_M(FR)|_M(LFE)|_M(FC)|_M(SL)|_M(SR)|_M(RC),
116
         6, _M(FL)|_M(FR)|_M(LFE)|_M(FC)|_M(SL)|_M(SR), 0,
117
            MATRIX(1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
118
@@ -263,7 +304,7 @@
119
 static void test_7p1_N(void)
120
 {
121
    test_mix(8, _M(FL)|_M(FR)|_M(LFE)|_M(FC)|_M(SL)|_M(SR)|_M(RL)|_M(RR), 1, _M(MONO), 0,
122
-           MATRIX(0.707107, 0.707107, 1.0, 0.0, 0.5, 0.5, 0.5, 0.5));
123
+           MATRIX(0.142857, 0.142857, 0.142857, 0.0, 0.142857, 0.142857, 0.142857, 0.142857));
124
    test_mix(8, _M(FL)|_M(FR)|_M(LFE)|_M(FC)|_M(SL)|_M(SR)|_M(RL)|_M(RR), 2, _M(FL)|_M(FR), 0,
125
            MATRIX(1.0, 0.0, 0.707107, 0.0, 0.707107, 0.0, 0.707107, 0.0,
126
                   0.0, 1.0, 0.707107, 0.0, 0.0, 0.707107, 0.0, 0.707107));
127
@@ -362,6 +403,7 @@
128
    test_1_N_MONO();
129
    test_1_N_FC();
130
    test_N_1();
131
+   test_2_N();
132
    test_3p1_N();
133
    test_4_N();
134
    test_5p1_N();
135
pipewire-0.3.65.tar.gz/spa/plugins/bluez5/bluez5-dbus.c -> pipewire-0.3.66.tar.gz/spa/plugins/bluez5/bluez5-dbus.c Changed
568
 
1
@@ -134,8 +134,6 @@
2
 
3
    /* A reference audio info for A2DP codec configuration. */
4
    struct media_codec_audio_info default_audio_info;
5
-
6
-   bool le_audio_supported;
7
 };
8
 
9
 /* Stream endpoints owned by BlueZ for each device */
10
@@ -1008,6 +1006,52 @@
11
    return 0;
12
 }
13
 
14
+static int adapter_media_update_props(struct spa_bt_adapter *adapter,
15
+               DBusMessageIter *props_iter,
16
+               DBusMessageIter *invalidated_iter)
17
+{
18
+   /* Handle org.bluez.Media1 interface properties of .Adapter1 objects */
19
+   struct spa_bt_monitor *monitor = adapter->monitor;
20
+
21
+   while (dbus_message_iter_get_arg_type(props_iter) != DBUS_TYPE_INVALID) {
22
+       DBusMessageIter it2;
23
+       const char *key;
24
+
25
+       dbus_message_iter_recurse(props_iter, &it0);
26
+       dbus_message_iter_get_basic(&it0, &key);
27
+       dbus_message_iter_next(&it0);
28
+       dbus_message_iter_recurse(&it0, &it1);
29
+
30
+       if (spa_streq(key, "SupportedUUIDs")) {
31
+           DBusMessageIter iter;
32
+
33
+           if (!check_iter_signature(&it1, "as"))
34
+               goto next;
35
+
36
+           dbus_message_iter_recurse(&it1, &iter);
37
+
38
+           while (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_INVALID) {
39
+               const char *uuid;
40
+
41
+               dbus_message_iter_get_basic(&iter, &uuid);
42
+
43
+               if (spa_streq(uuid, SPA_BT_UUID_BAP_SINK)) {
44
+                   adapter->le_audio_supported = true;
45
+                   spa_log_info(monitor->log, "Adapter %s: LE Audio supported",
46
+                           adapter->path);
47
+               }
48
+               dbus_message_iter_next(&iter);
49
+           }
50
+       }
51
+       else
52
+           spa_log_debug(monitor->log, "media: unhandled key %s", key);
53
+
54
+next:
55
+       dbus_message_iter_next(props_iter);
56
+   }
57
+   return 0;
58
+}
59
+
60
 static void adapter_update_devices(struct spa_bt_adapter *adapter)
61
 {
62
    struct spa_bt_monitor *monitor = adapter->monitor;
63
@@ -1875,6 +1919,8 @@
64
 {
65
    struct spa_bt_monitor *monitor = device->monitor;
66
    struct spa_bt_remote_endpoint *ep;
67
+   enum spa_bt_profile codec_profile;
68
+   struct spa_bt_transport *t;
69
    const struct { enum spa_bluetooth_audio_codec codec; uint32_t mask; } quirks = {
70
        { SPA_BLUETOOTH_AUDIO_CODEC_SBC_XQ, SPA_BT_FEATURE_SBC_XQ },
71
        { SPA_BLUETOOTH_AUDIO_CODEC_FASTSTREAM, SPA_BT_FEATURE_FASTSTREAM },
72
@@ -1887,10 +1933,13 @@
73
    if (!is_media_codec_enabled(device->monitor, codec))
74
        return false;
75
 
76
-   if (!device->adapter->application_registered) {
77
+   if (!device->adapter->a2dp_application_registered && !codec->bap) {
78
        /* Codec switching not supported: only plain SBC allowed */
79
-       return (codec->codec_id == A2DP_CODEC_SBC && spa_streq(codec->name, "sbc"));
80
+       return (codec->codec_id == A2DP_CODEC_SBC && spa_streq(codec->name, "sbc") &&
81
+               device->adapter->legacy_endpoints_registered);
82
    }
83
+   if (!device->adapter->bap_application_registered && codec->bap)
84
+       return false;
85
 
86
    /* Check codec quirks */
87
    for (i = 0; i < SPA_N_ELEMENTS(quirks); ++i) {
88
@@ -1906,16 +1955,15 @@
89
            return false;
90
    }
91
 
92
+   if (codec->bap)
93
+       codec_profile = sink ? SPA_BT_PROFILE_BAP_SINK : SPA_BT_PROFILE_BAP_SOURCE;
94
+   else
95
+       codec_profile = sink ? SPA_BT_PROFILE_A2DP_SINK : SPA_BT_PROFILE_A2DP_SOURCE;
96
+
97
    spa_list_for_each(ep, &device->remote_endpoint_list, device_link) {
98
        const enum spa_bt_profile profile = spa_bt_profile_from_uuid(ep->uuid);
99
-       enum spa_bt_profile expected;
100
 
101
-       if (codec->bap)
102
-           expected = sink ? SPA_BT_PROFILE_BAP_SINK : SPA_BT_PROFILE_BAP_SOURCE;
103
-       else
104
-           expected = sink ? SPA_BT_PROFILE_A2DP_SINK : SPA_BT_PROFILE_A2DP_SOURCE;
105
-
106
-       if (profile != expected)
107
+       if (profile != codec_profile)
108
            continue;
109
 
110
        if (media_codec_check_caps(codec, ep->codec, ep->capabilities, ep->capabilities_len,
111
@@ -1923,6 +1971,21 @@
112
            return true;
113
    }
114
 
115
+   /* Codecs on configured transports are always supported.
116
+    *
117
+    * Remote BAP endpoints correspond to capabilities of the remote
118
+    * BAP Server, not to remote BAP Client, and need not be the same.
119
+    * BAP Clients may not have any remote endpoints. In this case we
120
+    * can only know that the currently configured codec is supported.
121
+    */
122
+   spa_list_for_each(t, &device->transport_list, device_link) {
123
+       if (t->profile != codec_profile)
124
+           continue;
125
+
126
+       if (codec == t->media_codec)
127
+           return true;
128
+   }
129
+
130
    return false;
131
 }
132
 
133
@@ -3355,7 +3418,8 @@
134
    const struct media_codec *preferred_codec = NULL;
135
    size_t i, j, num_codecs, num_eps;
136
 
137
-   if (!device->adapter->application_registered) {
138
+   if (!device->adapter->a2dp_application_registered &&
139
+           !device->adapter->bap_application_registered) {
140
        /* Codec switching not supported */
141
        return -ENOTSUP;
142
    }
143
@@ -3690,9 +3754,10 @@
144
    return res;
145
 }
146
 
147
-static void bluez_register_endpoint_reply(DBusPendingCall *pending, void *user_data)
148
+static void bluez_register_endpoint_legacy_reply(DBusPendingCall *pending, void *user_data)
149
 {
150
-   struct spa_bt_monitor *monitor = user_data;
151
+   struct spa_bt_adapter *adapter = user_data;
152
+   struct spa_bt_monitor *monitor = adapter->monitor;
153
    DBusMessage *r;
154
 
155
    r = dbus_pending_call_steal_reply(pending);
156
@@ -3711,6 +3776,8 @@
157
        goto finish;
158
    }
159
 
160
+   adapter->legacy_endpoints_registered = true;
161
+
162
 finish:
163
    dbus_message_unref(r);
164
 }
165
@@ -3739,10 +3806,12 @@
166
    dbus_message_iter_close_container(dict, &dict_entry_it);
167
 }
168
 
169
-static int bluez_register_endpoint(struct spa_bt_monitor *monitor,
170
-                  const char *path, enum spa_bt_media_direction direction,
171
+static int bluez_register_endpoint_legacy(struct spa_bt_adapter *adapter,
172
+                  enum spa_bt_media_direction direction,
173
                   const char *uuid, const struct media_codec *codec)
174
 {
175
+   struct spa_bt_monitor *monitor = adapter->monitor;
176
+   const char *path = adapter->path;
177
    char  *object_path = NULL;
178
    DBusMessage *m;
179
    DBusMessageIter object_it, dict_it;
180
@@ -3783,7 +3852,7 @@
181
    dbus_message_iter_close_container(&object_it, &dict_it);
182
 
183
    dbus_connection_send_with_reply(monitor->conn, m, &call, -1);
184
-   dbus_pending_call_set_notify(call, bluez_register_endpoint_reply, monitor, NULL);
185
+   dbus_pending_call_set_notify(call, bluez_register_endpoint_legacy_reply, adapter, NULL);
186
    dbus_message_unref(m);
187
 
188
    free(object_path);
189
@@ -3795,14 +3864,15 @@
190
    return ret;
191
 }
192
 
193
-static int adapter_register_endpoints(struct spa_bt_adapter *a)
194
+static int adapter_register_endpoints_legacy(struct spa_bt_adapter *a)
195
 {
196
    struct spa_bt_monitor *monitor = a->monitor;
197
    const struct media_codec * const * const media_codecs = monitor->media_codecs;
198
    int i;
199
    int err = 0;
200
+   bool registered = false;
201
 
202
-   if (a->endpoints_registered)
203
+   if (a->legacy_endpoints_registered)
204
        return err;
205
 
206
    /* The legacy bluez5 api doesn't support codec switching
207
@@ -3813,9 +3883,7 @@
208
     * */
209
    spa_log_warn(monitor->log,
210
             "Using legacy bluez5 API for A2DP - only SBC will be supported. "
211
-            "No LE Audio. Please upgrade bluez5.");
212
-
213
-   monitor->le_audio_supported = false;
214
+            "Please upgrade bluez5.");
215
 
216
    for (i = 0; media_codecsi; i++) {
217
        const struct media_codec *codec = media_codecsi;
218
@@ -3824,26 +3892,24 @@
219
            continue;
220
 
221
        if (endpoint_should_be_registered(monitor, codec, SPA_BT_MEDIA_SOURCE)) {
222
-           if ((err = bluez_register_endpoint(monitor, a->path,
223
-                                   SPA_BT_MEDIA_SOURCE,
224
+           if ((err = bluez_register_endpoint_legacy(a, SPA_BT_MEDIA_SOURCE,
225
                                    SPA_BT_UUID_A2DP_SOURCE,
226
                                    codec)))
227
                goto out;
228
        }
229
 
230
        if (endpoint_should_be_registered(monitor, codec, SPA_BT_MEDIA_SINK)) {
231
-           if ((err = bluez_register_endpoint(monitor, a->path,
232
-                                   SPA_BT_MEDIA_SINK,
233
+           if ((err = bluez_register_endpoint_legacy(a, SPA_BT_MEDIA_SINK,
234
                                    SPA_BT_UUID_A2DP_SINK,
235
                                    codec)))
236
                goto out;
237
        }
238
 
239
-       a->endpoints_registered = true;
240
+       registered = true;
241
        break;
242
    }
243
 
244
-   if (!a->endpoints_registered) {
245
+   if (!registered) {
246
        /* Should never happen as SBC support is always enabled */
247
        spa_log_error(monitor->log, "Broken PipeWire build - unable to locate SBC codec");
248
        err = -ENOSYS;
249
@@ -3887,7 +3953,7 @@
250
    dbus_message_iter_close_container(iter, &object);
251
 }
252
 
253
-static DBusHandlerResult object_manager_handler(DBusConnection *c, DBusMessage *m, void *user_data)
254
+static DBusHandlerResult object_manager_handler(DBusConnection *c, DBusMessage *m, void *user_data, bool is_bap)
255
 {
256
    struct spa_bt_monitor *monitor = user_data;
257
    const struct media_codec * const * const media_codecs = monitor->media_codecs;
258
@@ -3930,20 +3996,11 @@
259
            int caps_size, ret;
260
            uint16_t codec_id = codec->codec_id;
261
 
262
-           if (!is_media_codec_enabled(monitor, codec))
263
+           if (codec->bap != is_bap)
264
                continue;
265
 
266
-           if (codec->bap && !monitor->le_audio_supported) {
267
-               /* The legacy bluez5 api doesn't support LE Audio
268
-                * It doesn't make sense to register unsupported codecs as it prevents
269
-                * registration of A2DP codecs
270
-                * let's incentivize users to upgrade their bluez5 daemon
271
-                * if they want proper media codec support
272
-                * */
273
-               spa_log_warn(monitor->log, "Trying to use legacy bluez5 API for LE Audio - only A2DP will be supported. "
274
-                                       "Please upgrade bluez5.");
275
+           if (!is_media_codec_enabled(monitor, codec))
276
                continue;
277
-           }
278
 
279
            if (endpoint_should_be_registered(monitor, codec, SPA_BT_MEDIA_SINK)) {
280
                caps_size = codec->fill_caps(codec, MEDIA_CODEC_FLAG_SINK, caps);
281
@@ -3987,7 +4044,17 @@
282
    return res;
283
 }
284
 
285
-static void bluez_register_application_reply(DBusPendingCall *pending, void *user_data)
286
+static DBusHandlerResult object_manager_handler_a2dp(DBusConnection *c, DBusMessage *m, void *user_data)
287
+{
288
+   return object_manager_handler(c, m, user_data, false);
289
+}
290
+
291
+static DBusHandlerResult object_manager_handler_bap(DBusConnection *c, DBusMessage *m, void *user_data)
292
+{
293
+   return object_manager_handler(c, m, user_data, true);
294
+}
295
+
296
+static void bluez_register_application_a2dp_reply(DBusPendingCall *pending, void *user_data)
297
 {
298
    struct spa_bt_adapter *adapter = user_data;
299
    struct spa_bt_monitor *monitor = adapter->monitor;
300
@@ -4012,13 +4079,37 @@
301
    }
302
 
303
    fallback = false;
304
-   adapter->application_registered = true;
305
+   adapter->a2dp_application_registered = true;
306
 
307
 finish:
308
    dbus_message_unref(r);
309
 
310
    if (fallback)
311
-       adapter_register_endpoints(adapter);
312
+       adapter_register_endpoints_legacy(adapter);
313
+}
314
+
315
+static void bluez_register_application_bap_reply(DBusPendingCall *pending, void *user_data)
316
+{
317
+   struct spa_bt_adapter *adapter = user_data;
318
+   struct spa_bt_monitor *monitor = adapter->monitor;
319
+   DBusMessage *r;
320
+
321
+   r = dbus_pending_call_steal_reply(pending);
322
+   dbus_pending_call_unref(pending);
323
+
324
+   if (r == NULL)
325
+       return;
326
+
327
+   if (dbus_message_get_type(r) == DBUS_MESSAGE_TYPE_ERROR) {
328
+       spa_log_error(monitor->log, "RegisterApplication() failed: %s",
329
+               dbus_message_get_error_name(r));
330
+       goto finish;
331
+   }
332
+
333
+   adapter->bap_application_registered = true;
334
+
335
+finish:
336
+   dbus_message_unref(r);
337
 }
338
 
339
 static int register_media_endpoint(struct spa_bt_monitor *monitor,
340
@@ -4037,7 +4128,7 @@
341
    if (ret < 0)
342
        return ret;
343
 
344
-   spa_log_info(monitor->log, "registering endpoint: %s", object_path);
345
+   spa_log_info(monitor->log, "Registering DBus media endpoint: %s", object_path);
346
 
347
    if (!dbus_connection_register_object_path(monitor->conn,
348
                          object_path,
349
@@ -4053,15 +4144,27 @@
350
 static int register_media_application(struct spa_bt_monitor * monitor)
351
 {
352
    const struct media_codec * const * const media_codecs = monitor->media_codecs;
353
-   const DBusObjectPathVTable vtable_object_manager = {
354
-       .message_function = object_manager_handler,
355
+   const DBusObjectPathVTable vtable_object_manager_a2dp = {
356
+       .message_function = object_manager_handler_a2dp,
357
    };
358
+   const DBusObjectPathVTable vtable_object_manager_bap = {
359
+       .message_function = object_manager_handler_bap,
360
+   };
361
+
362
+   spa_log_info(monitor->log, "Registering DBus media object manager: %s",
363
+           A2DP_OBJECT_MANAGER_PATH);
364
 
365
-   spa_log_info(monitor->log, "Registering media application object: " MEDIA_OBJECT_MANAGER_PATH);
366
+   if (!dbus_connection_register_object_path(monitor->conn,
367
+                                             A2DP_OBJECT_MANAGER_PATH,
368
+                                             &vtable_object_manager_a2dp, monitor))
369
+       return -EIO;
370
+
371
+   spa_log_info(monitor->log, "Registering DBus media object manager: %s",
372
+           BAP_OBJECT_MANAGER_PATH);
373
 
374
    if (!dbus_connection_register_object_path(monitor->conn,
375
-                                             MEDIA_OBJECT_MANAGER_PATH,
376
-                                             &vtable_object_manager, monitor))
377
+                                             BAP_OBJECT_MANAGER_PATH,
378
+                                             &vtable_object_manager_bap, monitor))
379
        return -EIO;
380
 
381
    for (int i = 0; media_codecsi; i++) {
382
@@ -4105,20 +4208,31 @@
383
        unregister_media_endpoint(monitor, codec, SPA_BT_MEDIA_SINK);
384
    }
385
 
386
-   dbus_connection_unregister_object_path(monitor->conn, MEDIA_OBJECT_MANAGER_PATH);
387
+   dbus_connection_unregister_object_path(monitor->conn, BAP_OBJECT_MANAGER_PATH);
388
+   dbus_connection_unregister_object_path(monitor->conn, A2DP_OBJECT_MANAGER_PATH);
389
 }
390
 
391
-static int adapter_register_application(struct spa_bt_adapter *a) {
392
-   const char *object_manager_path = MEDIA_OBJECT_MANAGER_PATH;
393
+static int adapter_register_application(struct spa_bt_adapter *a, bool bap)
394
+{
395
+   const char *object_manager_path = bap ? BAP_OBJECT_MANAGER_PATH : A2DP_OBJECT_MANAGER_PATH;
396
    struct spa_bt_monitor *monitor = a->monitor;
397
    DBusMessage *m;
398
    DBusMessageIter i, d;
399
    DBusPendingCall *call;
400
 
401
-   if (a->application_registered)
402
+   if (bap && a->bap_application_registered)
403
+       return 0;
404
+   if (!bap && a->a2dp_application_registered)
405
        return 0;
406
 
407
-   spa_log_debug(monitor->log, "Registering bluez5 media application on adapter %s", a->path);
408
+   if (bap && !a->le_audio_supported) {
409
+       spa_log_info(monitor->log, "Adapter %s indicates LE Audio unsupported: not registering application",
410
+               a->path);
411
+       return -ENOTSUP;
412
+   }
413
+
414
+   spa_log_debug(monitor->log, "Registering bluez5 %s media application on adapter %s",
415
+           (bap ? "LE Audio" : "A2DP"), a->path);
416
 
417
    m = dbus_message_new_method_call(BLUEZ_SERVICE,
418
                                     a->path,
419
@@ -4133,7 +4247,9 @@
420
    dbus_message_iter_close_container(&i, &d);
421
 
422
    dbus_connection_send_with_reply(monitor->conn, m, &call, -1);
423
-   dbus_pending_call_set_notify(call, bluez_register_application_reply, a, NULL);
424
+   dbus_pending_call_set_notify(call,
425
+           bap ? bluez_register_application_bap_reply : bluez_register_application_a2dp_reply,
426
+           a, NULL);
427
    dbus_message_unref(m);
428
 
429
    return 0;
430
@@ -4205,48 +4321,6 @@
431
                backend ? backend->name : "none");
432
 }
433
 
434
-static int media_update_props(struct spa_bt_monitor *monitor,
435
-               DBusMessageIter *props_iter,
436
-               DBusMessageIter *invalidated_iter)
437
-{
438
-   while (dbus_message_iter_get_arg_type(props_iter) != DBUS_TYPE_INVALID) {
439
-       DBusMessageIter it2;
440
-       const char *key;
441
-
442
-       dbus_message_iter_recurse(props_iter, &it0);
443
-       dbus_message_iter_get_basic(&it0, &key);
444
-       dbus_message_iter_next(&it0);
445
-       dbus_message_iter_recurse(&it0, &it1);
446
-
447
-       if (spa_streq(key, "SupportedUUIDs")) {
448
-           DBusMessageIter iter;
449
-
450
-           if (!check_iter_signature(&it1, "as"))
451
-               goto next;
452
-
453
-           dbus_message_iter_recurse(&it1, &iter);
454
-
455
-           while (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_INVALID) {
456
-               const char *uuid;
457
-
458
-               dbus_message_iter_get_basic(&iter, &uuid);
459
-
460
-               if (spa_streq(uuid, SPA_BT_UUID_BAP_SINK)) {
461
-                   monitor->le_audio_supported = true;
462
-                   spa_log_info(monitor->log, "LE Audio supported");
463
-               }
464
-               dbus_message_iter_next(&iter);
465
-           }
466
-       }
467
-       else
468
-           spa_log_debug(monitor->log, "media: unhandled key %s", key);
469
-
470
-next:
471
-       dbus_message_iter_next(props_iter);
472
-   }
473
-   return 0;
474
-}
475
-
476
 static void interface_added(struct spa_bt_monitor *monitor,
477
                DBusConnection *conn,
478
                const char *object_path,
479
@@ -4255,7 +4329,8 @@
480
 {
481
    spa_log_debug(monitor->log, "Found object %s, interface %s", object_path, interface_name);
482
 
483
-   if (spa_streq(interface_name, BLUEZ_ADAPTER_INTERFACE)) {
484
+   if (spa_streq(interface_name, BLUEZ_ADAPTER_INTERFACE) ||
485
+           spa_streq(interface_name, BLUEZ_MEDIA_INTERFACE)) {
486
        struct spa_bt_adapter *a;
487
 
488
        a = adapter_find(monitor, object_path);
489
@@ -4266,10 +4341,21 @@
490
                return;
491
            }
492
        }
493
-       adapter_update_props(a, props_iter, NULL);
494
-       adapter_register_application(a);
495
-       adapter_register_player(a);
496
-       adapter_update_devices(a);
497
+
498
+       if (spa_streq(interface_name, BLUEZ_ADAPTER_INTERFACE)) {
499
+           adapter_update_props(a, props_iter, NULL);
500
+           a->has_adapter1_interface = true;
501
+       } else {
502
+           adapter_media_update_props(a, props_iter, NULL);
503
+           a->has_media1_interface = true;
504
+       }
505
+
506
+       if (a->has_adapter1_interface && a->has_media1_interface) {
507
+           adapter_register_application(a, false);
508
+           adapter_register_application(a, true);
509
+           adapter_register_player(a);
510
+           adapter_update_devices(a);
511
+       }
512
    }
513
    else if (spa_streq(interface_name, BLUEZ_PROFILE_MANAGER_INTERFACE)) {
514
        if (monitor->backendsBACKEND_NATIVE)
515
@@ -4320,9 +4406,6 @@
516
        if (d)
517
            spa_bt_device_emit_profiles_changed(d, d->profiles, d->connected_profiles);
518
    }
519
-   else if (spa_streq(interface_name, BLUEZ_MEDIA_INTERFACE)) {
520
-       media_update_props(monitor, props_iter, NULL);
521
-   }
522
 }
523
 
524
 static void interfaces_added(struct spa_bt_monitor *monitor, DBusMessageIter *arg_iter)
525
@@ -4371,7 +4454,8 @@
526
            d = spa_bt_device_find(monitor, object_path);
527
            if (d != NULL)
528
                device_free(d);
529
-       } else if (spa_streq(interface_name, BLUEZ_ADAPTER_INTERFACE)) {
530
+       } else if (spa_streq(interface_name, BLUEZ_ADAPTER_INTERFACE) ||
531
+               spa_streq(interface_name, BLUEZ_MEDIA_INTERFACE)) {
532
            struct spa_bt_adapter *a;
533
            a = adapter_find(monitor, object_path);
534
            if (a != NULL)
535
@@ -4575,7 +4659,8 @@
536
        dbus_message_iter_next(&it0);
537
        dbus_message_iter_recurse(&it0, &it1);
538
 
539
-       if (spa_streq(iface, BLUEZ_ADAPTER_INTERFACE)) {
540
+       if (spa_streq(iface, BLUEZ_ADAPTER_INTERFACE) ||
541
+               spa_streq(iface, BLUEZ_MEDIA_INTERFACE)) {
542
            struct spa_bt_adapter *a;
543
 
544
            a = adapter_find(monitor, path);
545
@@ -4586,7 +4671,10 @@
546
            }
547
            spa_log_debug(monitor->log, "Properties changed in adapter %s", path);
548
 
549
-           adapter_update_props(a, &it1, NULL);
550
+           if (spa_streq(iface, BLUEZ_ADAPTER_INTERFACE))
551
+               adapter_update_props(a, &it1, NULL);
552
+           else
553
+               adapter_media_update_props(a, &it1, NULL);
554
        }
555
        else if (spa_streq(iface, BLUEZ_DEVICE_INTERFACE)) {
556
            struct spa_bt_device *d;
557
@@ -4691,6 +4779,10 @@
558
    dbus_bus_add_match(this->conn,
559
            "type='signal',sender='" BLUEZ_SERVICE "',"
560
            "interface='org.freedesktop.DBus.Properties',member='PropertiesChanged',"
561
+           "arg0='" BLUEZ_MEDIA_INTERFACE "'", &err);
562
+   dbus_bus_add_match(this->conn,
563
+           "type='signal',sender='" BLUEZ_SERVICE "',"
564
+           "interface='org.freedesktop.DBus.Properties',member='PropertiesChanged',"
565
            "arg0='" BLUEZ_DEVICE_INTERFACE "'", &err);
566
    dbus_bus_add_match(this->conn,
567
            "type='signal',sender='" BLUEZ_SERVICE "',"
568
pipewire-0.3.65.tar.gz/spa/plugins/bluez5/bluez5-device.c -> pipewire-0.3.66.tar.gz/spa/plugins/bluez5/bluez5-device.c Changed
136
 
1
@@ -204,6 +204,33 @@
2
    return media_codec;
3
 }
4
 
5
+static bool is_bap_client(struct impl *this)
6
+{
7
+   struct spa_bt_device *device = this->bt_dev;
8
+   struct spa_bt_transport *t;
9
+
10
+   spa_list_for_each(t, &device->transport_list, device_link) {
11
+       if (t->bap_initiator)
12
+           return true;
13
+   }
14
+
15
+   return false;
16
+}
17
+
18
+static bool can_bap_codec_switch(struct impl *this)
19
+{
20
+   if (!is_bap_client(this))
21
+       return false;
22
+
23
+   /* XXX: codec switching for source/duplex is not currently
24
+    * XXX: implemented properly. TODO: fix this
25
+    */
26
+   if (this->bt_dev->connected_profiles & SPA_BT_PROFILE_BAP_SOURCE)
27
+       return false;
28
+
29
+   return true;
30
+}
31
+
32
 static unsigned int get_hfp_codec(enum spa_bluetooth_audio_codec id)
33
 {
34
    switch (id) {
35
@@ -873,9 +900,15 @@
36
     * A2DP/BAP: ensure there's a transport with the selected codec (0 means any).
37
     * Don't try to switch codecs when the device is in the A2DP source role, since
38
     * devices do not appear to like that.
39
+    *
40
+    * For BAP, only BAP client can configure the codec.
41
+    *
42
+    * XXX: codec switching also currently does not work in the duplex or
43
+    * XXX: source-only case, as it will only switch the sink, and we only
44
+    * XXX: list the sink codecs here. TODO: fix this
45
     */
46
-   if ((profile == DEVICE_PROFILE_A2DP || profile == DEVICE_PROFILE_BAP)
47
-       && !(this->bt_dev->connected_profiles & SPA_BT_PROFILE_A2DP_SOURCE)) {
48
+   if ((profile == DEVICE_PROFILE_A2DP || (profile == DEVICE_PROFILE_BAP && can_bap_codec_switch(this)))
49
+           && !(this->bt_dev->connected_profiles & SPA_BT_PROFILE_A2DP_SOURCE)) {
50
        int ret;
51
        const struct media_codec *codecs64;
52
 
53
@@ -1332,11 +1365,6 @@
54
        if (profile == 0)
55
            return NULL;
56
 
57
-       if (!codec) {
58
-           errno = EINVAL;
59
-           return NULL;
60
-       }
61
-
62
        if (profile & (SPA_BT_PROFILE_BAP_SINK))
63
            n_sink++;
64
        if (profile & (SPA_BT_PROFILE_BAP_SOURCE))
65
@@ -1344,28 +1372,51 @@
66
 
67
        name = spa_bt_profile_name(profile);
68
 
69
-       media_codec = get_supported_media_codec(this, codec, &idx);
70
-       if (media_codec == NULL) {
71
-           errno = EINVAL;
72
+       /* If we can't codec switch, emit codecless profile */
73
+       if (current && !can_bap_codec_switch(this)) {
74
+           codec = 0;
75
+           index = get_index_from_profile(this, profile_index, codec);
76
+       } else if ((codec != 0) != can_bap_codec_switch(this)) {
77
+           errno = -EINVAL;
78
            return NULL;
79
        }
80
-       name_and_codec = spa_aprintf("%s-%s", name, media_codec->name);
81
-       name = name_and_codec;
82
-       switch (profile) {
83
-       case SPA_BT_PROFILE_BAP_SINK:
84
-           desc_and_codec = spa_aprintf(_("High Fidelity Playback (BAP Sink, codec %s)"),
85
-                            media_codec->description);
86
-           break;
87
-       case SPA_BT_PROFILE_BAP_SOURCE:
88
-           desc_and_codec = spa_aprintf(_("High Fidelity Input (BAP Source, codec %s)"),
89
-                            media_codec->description);
90
-           break;
91
-       default:
92
-           desc_and_codec = spa_aprintf(_("High Fidelity Duplex (BAP Source/Sink, codec %s)"),
93
-                            media_codec->description);
94
+
95
+       if (codec) {
96
+           media_codec = get_supported_media_codec(this, codec, &idx);
97
+           if (media_codec == NULL) {
98
+               errno = EINVAL;
99
+               return NULL;
100
+           }
101
+           name_and_codec = spa_aprintf("%s-%s", name, media_codec->name);
102
+           name = name_and_codec;
103
+           switch (profile) {
104
+           case SPA_BT_PROFILE_BAP_SINK:
105
+               desc_and_codec = spa_aprintf(_("High Fidelity Playback (BAP Sink, codec %s)"),
106
+                       media_codec->description);
107
+               break;
108
+           case SPA_BT_PROFILE_BAP_SOURCE:
109
+               desc_and_codec = spa_aprintf(_("High Fidelity Input (BAP Source, codec %s)"),
110
+                       media_codec->description);
111
+               break;
112
+           default:
113
+               desc_and_codec = spa_aprintf(_("High Fidelity Duplex (BAP Source/Sink, codec %s)"),
114
+                       media_codec->description);
115
+           }
116
+           desc = desc_and_codec;
117
+           priority = 128 + this->supported_codec_count - idx;  /* order as in codec list */
118
+       } else {
119
+           switch (profile) {
120
+           case SPA_BT_PROFILE_BAP_SINK:
121
+               desc = _("High Fidelity Playback (BAP Sink)");
122
+               break;
123
+           case SPA_BT_PROFILE_BAP_SOURCE:
124
+               desc = _("High Fidelity Input (BAP Source)");
125
+               break;
126
+           default:
127
+               desc = _("High Fidelity Duplex (BAP Source/Sink)");
128
+           }
129
+           priority = 128;
130
        }
131
-       desc = desc_and_codec;
132
-       priority = 128 + this->supported_codec_count - idx;  /* order as in codec list */
133
        break;
134
    }
135
    case DEVICE_PROFILE_HSP_HFP:
136
pipewire-0.3.65.tar.gz/spa/plugins/bluez5/defs.h -> pipewire-0.3.66.tar.gz/spa/plugins/bluez5/defs.h Changed
38
 
1
@@ -161,12 +161,13 @@
2
 #define HFP_AUDIO_CODEC_CVSD    0x01
3
 #define HFP_AUDIO_CODEC_MSBC    0x02
4
 
5
-#define MEDIA_OBJECT_MANAGER_PATH "/MediaEndpoint"
6
-#define A2DP_SINK_ENDPOINT MEDIA_OBJECT_MANAGER_PATH "/A2DPSink"
7
-#define A2DP_SOURCE_ENDPOINT   MEDIA_OBJECT_MANAGER_PATH "/A2DPSource"
8
+#define A2DP_OBJECT_MANAGER_PATH "/MediaEndpoint"
9
+#define A2DP_SINK_ENDPOINT A2DP_OBJECT_MANAGER_PATH "/A2DPSink"
10
+#define A2DP_SOURCE_ENDPOINT   A2DP_OBJECT_MANAGER_PATH "/A2DPSource"
11
 
12
-#define BAP_SINK_ENDPOINT  MEDIA_OBJECT_MANAGER_PATH "/BAPSink"
13
-#define BAP_SOURCE_ENDPOINT    MEDIA_OBJECT_MANAGER_PATH "/BAPSource"
14
+#define BAP_OBJECT_MANAGER_PATH "/MediaEndpointLE"
15
+#define BAP_SINK_ENDPOINT  BAP_OBJECT_MANAGER_PATH "/BAPSink"
16
+#define BAP_SOURCE_ENDPOINT    BAP_OBJECT_MANAGER_PATH "/BAPSource"
17
 
18
 #define SPA_BT_UNKNOWN_DELAY           0
19
 
20
@@ -358,11 +359,15 @@
21
    int powered;
22
    unsigned int has_msbc:1;
23
    unsigned int msbc_probed:1;
24
-   unsigned int endpoints_registered:1;
25
-   unsigned int application_registered:1;
26
+   unsigned int legacy_endpoints_registered:1;
27
+   unsigned int a2dp_application_registered:1;
28
+   unsigned int bap_application_registered:1;
29
    unsigned int player_registered:1;
30
    unsigned int has_battery_provider:1;
31
    unsigned int battery_provider_unavailable:1;
32
+   unsigned int le_audio_supported:1;
33
+   unsigned int has_adapter1_interface:1;
34
+   unsigned int has_media1_interface:1;
35
 };
36
 
37
 enum spa_bt_form_factor {
38
pipewire-0.3.65.tar.gz/spa/plugins/bluez5/meson.build -> pipewire-0.3.66.tar.gz/spa/plugins/bluez5/meson.build Changed
14
 
1
@@ -1,12 +1,5 @@
2
 gnome = import('gnome')
3
 
4
-bluez5_deps =  mathlib, dbus_dep, glib2_dep, sbc_dep, bluez_dep, gio_dep, gio_unix_dep 
5
-foreach dep: bluez5_deps
6
-  if not dep.found()
7
-    subdir_done()
8
-  endif
9
-endforeach
10
-
11
 cdata.set('HAVE_BLUEZ_5_BACKEND_NATIVE',
12
           get_option('bluez5-backend-hsp-native').allowed() or
13
           get_option('bluez5-backend-hfp-native').allowed())
14
pipewire-0.3.65.tar.gz/spa/plugins/libcamera/libcamera-utils.cpp -> pipewire-0.3.66.tar.gz/spa/plugins/libcamera/libcamera-utils.cpp Changed
86
 
1
@@ -35,6 +35,7 @@
2
 #include <limits.h>
3
 
4
 #include <linux/media.h>
5
+#include <libcamera/control_ids.h>
6
 
7
 int spa_libcamera_open(struct impl *impl)
8
 {
9
@@ -458,6 +459,38 @@
10
 
11
 }
12
 
13
+static struct {
14
+   uint32_t id;
15
+   uint32_t spa_id;
16
+} control_map = {
17
+   { libcamera::controls::BRIGHTNESS, SPA_PROP_brightness },
18
+   { libcamera::controls::CONTRAST, SPA_PROP_contrast },
19
+   { libcamera::controls::SATURATION, SPA_PROP_saturation },
20
+   { libcamera::controls::EXPOSURE_TIME, SPA_PROP_exposure },
21
+   { libcamera::controls::ANALOGUE_GAIN, SPA_PROP_gain },
22
+   { libcamera::controls::SHARPNESS, SPA_PROP_sharpness },
23
+};
24
+
25
+static uint32_t control_to_prop_id(struct impl *impl, uint32_t control_id)
26
+{
27
+   SPA_FOR_EACH_ELEMENT_VAR(control_map, c) {
28
+       if (c->id == control_id)
29
+           return c->spa_id;
30
+   }
31
+   return SPA_PROP_START_CUSTOM + control_id;
32
+}
33
+
34
+static uint32_t prop_id_to_control(struct impl *impl, uint32_t prop_id)
35
+{
36
+   SPA_FOR_EACH_ELEMENT_VAR(control_map, c) {
37
+       if (c->spa_id == prop_id)
38
+           return c->id;
39
+   }
40
+   if (prop_id >= SPA_PROP_START_CUSTOM)
41
+       return prop_id - SPA_PROP_START_CUSTOM;
42
+   return SPA_ID_INVALID;
43
+}
44
+
45
 static int
46
 spa_libcamera_enum_controls(struct impl *impl, struct port *port, int seq,
47
               uint32_t start, uint32_t num,
48
@@ -469,7 +502,7 @@
49
    struct spa_pod_frame f2;
50
    struct spa_result_node_params result;
51
    struct spa_pod *ctrl;
52
-   uint32_t count = 0, skip;
53
+   uint32_t count = 0, skip, id;
54
    int res;
55
    const ControlId *ctrl_id;
56
    ControlInfo ctrl_info;
57
@@ -492,10 +525,12 @@
58
    ctrl_id = it->first;
59
    ctrl_info = it->second;
60
 
61
+   id = control_to_prop_id(impl, ctrl_id->id());
62
+
63
    spa_pod_builder_init(&b, buffer, sizeof(buffer));
64
    spa_pod_builder_push_object(&b, &f0, SPA_TYPE_OBJECT_PropInfo, SPA_PARAM_PropInfo);
65
    spa_pod_builder_add(&b,
66
-           SPA_PROP_INFO_id,   SPA_POD_Id(ctrl_id->id()),
67
+           SPA_PROP_INFO_id,   SPA_POD_Id(id),
68
            SPA_PROP_INFO_description, SPA_POD_String(ctrl_id->name().c_str()),
69
            0);
70
 
71
@@ -601,8 +636,13 @@
72
    const ControlId *ctrl_id;
73
    int res;
74
    struct val d;
75
+   uint32_t control_id;
76
+
77
+   control_id = prop_id_to_control(impl, prop->key);
78
+   if (control_id == SPA_ID_INVALID)
79
+       return -ENOENT;
80
 
81
-   auto v = info.idmap().find(prop->key);
82
+   auto v = info.idmap().find(control_id);
83
    if (v == info.idmap().end())
84
        return -ENOENT;
85
 
86
pipewire-0.3.65.tar.gz/spa/plugins/libcamera/meson.build -> pipewire-0.3.66.tar.gz/spa/plugins/libcamera/meson.build Changed
20
 
1
@@ -5,13 +5,8 @@
2
   'libcamera-source.cpp'
3
 
4
 
5
-libdrm_dep = dependency('libdrm', version : '>= 2.4.98',
6
-                        required : get_option('libcamera'))
7
-summary({'libdrm': libdrm_dep.found()}, bool_yn: true, section: 'Backend')
8
-if libdrm_dep.found()
9
-  libcameralib = shared_library('spa-libcamera',
10
-    libcamera_sources,
11
-    dependencies :  spa_dep, libudev_dep, libcamera_dep, pthread_lib, libdrm_dep  ,
12
-    install : true,
13
-    install_dir : spa_plugindir / 'libcamera')
14
-endif
15
+libcameralib = shared_library('spa-libcamera',
16
+  libcamera_sources,
17
+  dependencies :  spa_dep, libudev_dep, libcamera_dep, pthread_lib  ,
18
+  install : true,
19
+  install_dir : spa_plugindir / 'libcamera')
20
pipewire-0.3.65.tar.gz/spa/plugins/support/journal.c -> pipewire-0.3.66.tar.gz/spa/plugins/support/journal.c Changed
20
 
1
@@ -22,6 +22,8 @@
2
  * DEALINGS IN THE SOFTWARE.
3
  */
4
 
5
+#include "config.h"
6
+
7
 #include <stddef.h>
8
 #include <unistd.h>
9
 #include <string.h>
10
@@ -114,6 +116,9 @@
11
    sd_journal_send_with_location(file_buffer, line_buffer, func,
12
                      "MESSAGE=%s", message_buffer,
13
                      "PRIORITY=%i", priority,
14
+#ifdef HAVE_GETTID
15
+                     "TID=%jd", (intmax_t) gettid(),
16
+#endif
17
                      NULL);
18
 }
19
 
20
pipewire-0.3.65.tar.gz/spa/plugins/support/meson.build -> pipewire-0.3.66.tar.gz/spa/plugins/support/meson.build Changed
18
 
1
@@ -18,7 +18,7 @@
2
 spa_support_lib = shared_library('spa-support',
3
   spa_support_sources,
4
   c_args :  simd_cargs ,
5
-  dependencies :  spa_dep, pthread_lib, epoll_shim_dep ,
6
+  dependencies :  spa_dep, pthread_lib, epoll_shim_dep, mathlib ,
7
   install : true,
8
   install_dir : spa_plugindir / 'support')
9
 spa_support_dep = declare_dependency(link_with: spa_support_lib)
10
@@ -61,6 +61,7 @@
11
 
12
   spa_journal_lib = shared_library('spa-journal',
13
     spa_journal_sources,
14
+    include_directories :  configinc ,
15
     dependencies :  spa_dep, systemd_dep ,
16
     install : true,
17
     install_dir : spa_plugindir / 'support')
18
pipewire-0.3.65.tar.gz/spa/plugins/support/node-driver.c -> pipewire-0.3.66.tar.gz/spa/plugins/support/node-driver.c Changed
364
 
1
@@ -27,6 +27,7 @@
2
 #include <unistd.h>
3
 #include <string.h>
4
 #include <stdio.h>
5
+#include <fcntl.h>
6
 
7
 #include <spa/support/plugin.h>
8
 #include <spa/support/log.h>
9
@@ -34,6 +35,7 @@
10
 #include <spa/utils/names.h>
11
 #include <spa/utils/result.h>
12
 #include <spa/utils/string.h>
13
+#include <spa/utils/dll.h>
14
 #include <spa/node/node.h>
15
 #include <spa/node/keys.h>
16
 #include <spa/node/io.h>
17
@@ -43,11 +45,20 @@
18
 #define NAME "driver"
19
 
20
 #define DEFAULT_FREEWHEEL  false
21
-#define DEFAULT_CLOCK_NAME "clock.system.monotonic"
22
+#define DEFAULT_CLOCK_PREFIX   "clock.system"
23
+#define DEFAULT_CLOCK_ID   CLOCK_MONOTONIC
24
+
25
+#define CLOCKFD 3
26
+#define FD_TO_CLOCKID(fd)  ((~(clockid_t) (fd) << 3) | CLOCKFD)
27
+#define CLOCKID_TO_FD(clk) ((unsigned int) ~((clk) >> 3))
28
+
29
+#define BW_PERIOD  (3 * SPA_NSEC_PER_SEC)
30
+#define MAX_ERROR_MS   1
31
 
32
 struct props {
33
    bool freewheel;
34
    char clock_name64;
35
+   clockid_t clock_id;
36
 };
37
 
38
 struct impl {
39
@@ -72,19 +83,62 @@
40
 
41
    struct spa_source timer_source;
42
    struct itimerspec timerspec;
43
+   int clock_fd;
44
 
45
    bool started;
46
    bool following;
47
+   bool tracking;
48
+   clockid_t timer_clockid;
49
    uint64_t next_time;
50
+   uint64_t last_time;
51
+   uint64_t base_time;
52
+   struct spa_dll dll;
53
+   double max_error;
54
 };
55
 
56
 static void reset_props(struct props *props)
57
 {
58
    props->freewheel = DEFAULT_FREEWHEEL;
59
-   spa_scnprintf(props->clock_name, sizeof(props->clock_name),
60
-           "%s", DEFAULT_CLOCK_NAME);
61
+   spa_zero(props->clock_name);
62
+   props->clock_id = CLOCK_MONOTONIC;
63
 }
64
 
65
+static const struct clock_info {
66
+   const char *name;
67
+   clockid_t id;
68
+} clock_info = {
69
+   { "realtime", CLOCK_REALTIME },
70
+   { "tai", CLOCK_TAI },
71
+   { "monotonic", CLOCK_MONOTONIC },
72
+   { "monotonic-raw", CLOCK_MONOTONIC_RAW },
73
+   { "boottime", CLOCK_BOOTTIME },
74
+};
75
+
76
+static bool clock_for_timerfd(clockid_t id)
77
+{
78
+   return id == CLOCK_REALTIME ||
79
+       id == CLOCK_MONOTONIC ||
80
+       id == CLOCK_BOOTTIME;
81
+}
82
+
83
+static clockid_t clock_name_to_id(const char *name)
84
+{
85
+   SPA_FOR_EACH_ELEMENT_VAR(clock_info, i) {
86
+       if (spa_streq(i->name, name))
87
+           return i->id;
88
+   }
89
+   return -1;
90
+}
91
+static const char *clock_id_to_name(clockid_t id)
92
+{
93
+   SPA_FOR_EACH_ELEMENT_VAR(clock_info, i) {
94
+       if (i->id == id)
95
+           return i->name;
96
+   }
97
+   return "custom";
98
+}
99
+
100
+
101
 static void set_timeout(struct impl *this, uint64_t next_time)
102
 {
103
    spa_log_trace(this->log, "set timeout %"PRIu64, next_time);
104
@@ -94,14 +148,22 @@
105
            this->timer_source.fd, SPA_FD_TIMER_ABSTIME, &this->timerspec, NULL);
106
 }
107
 
108
+static inline uint64_t gettime_nsec(struct impl *this, clockid_t clock_id)
109
+{
110
+   struct timespec now = { 0 };
111
+   uint64_t nsec;
112
+   if (spa_system_clock_gettime(this->data_system, clock_id, &now) < 0)
113
+       return 0;
114
+   nsec = SPA_TIMESPEC_TO_NSEC(&now);
115
+   spa_log_trace(this->log, "%p now:%"PRIu64, this, nsec);
116
+   return nsec;
117
+}
118
+
119
 static int set_timers(struct impl *this)
120
 {
121
-   struct timespec now;
122
-   int res;
123
+   this->next_time = gettime_nsec(this, this->timer_clockid);
124
 
125
-   if ((res = spa_system_clock_gettime(this->data_system, CLOCK_MONOTONIC, &now)) < 0)
126
-       return res;
127
-   this->next_time = SPA_TIMESPEC_TO_NSEC(&now);
128
+   spa_log_debug(this->log, "%p now:%"PRIu64, this, this->next_time);
129
 
130
    if (this->following) {
131
        set_timeout(this, 0);
132
@@ -176,15 +238,23 @@
133
    return 0;
134
 }
135
 
136
+static inline uint64_t scale_u64(uint64_t val, uint32_t num, uint32_t denom)
137
+{
138
+#if 0
139
+   return ((__uint128_t)val * num) / denom;
140
+#else
141
+   return (double)val / denom * num;
142
+#endif
143
+}
144
+
145
 static void on_timeout(struct spa_source *source)
146
 {
147
    struct impl *this = source->data;
148
-   uint64_t expirations, nsec, duration;
149
+   uint64_t expirations, nsec, duration, current_time, current_position, position;
150
    uint32_t rate;
151
+   double corr = 1.0, err = 0.0;
152
    int res;
153
 
154
-   spa_log_trace(this->log, "timeout");
155
-
156
    if ((res = spa_system_timerfd_read(this->data_system,
157
                this->timer_source.fd, &expirations)) < 0) {
158
        if (res != EAGAIN)
159
@@ -192,9 +262,6 @@
160
                    this, spa_strerror(res));
161
        return;
162
    }
163
-
164
-   nsec = this->next_time;
165
-
166
    if (SPA_LIKELY(this->position)) {
167
        duration = this->position->clock.duration;
168
        rate = this->position->clock.rate.denom;
169
@@ -202,15 +269,61 @@
170
        duration = 1024;
171
        rate = 48000;
172
    }
173
+   nsec = this->next_time;
174
+
175
+   if (this->tracking)
176
+       /* we are actually following another clock */
177
+       current_time = gettime_nsec(this, this->props.clock_id);
178
+   else
179
+       current_time = nsec;
180
+
181
+   current_position = scale_u64(current_time, rate, SPA_NSEC_PER_SEC);
182
+
183
+   if (SPA_LIKELY(this->clock))
184
+       position = this->clock->position;
185
+   else
186
+       position = current_position;
187
+
188
+   if (this->last_time == 0) {
189
+       spa_dll_set_bw(&this->dll, SPA_DLL_BW_MIN, duration, rate);
190
+       this->max_error = rate * MAX_ERROR_MS / 1000;
191
+       position = current_position;
192
+   }
193
 
194
-   this->next_time = nsec + duration * SPA_NSEC_PER_SEC / rate;
195
+   /* check the elapsed time of the other clock against
196
+    * the graph clock elapsed time, feed this error into the
197
+    * dll and adjust the timeout of our MONOTONIC clock. */
198
+   err = (double)position - (double)current_position;
199
+   if (err > this->max_error)
200
+       err = this->max_error;
201
+   else if (err < -this->max_error)
202
+       err = -this->max_error;
203
+
204
+   position += duration;
205
+   this->last_time = current_time;
206
+
207
+   if (this->tracking) {
208
+       corr = spa_dll_update(&this->dll, err);
209
+       this->next_time = nsec + duration / corr * 1e9 / rate;
210
+   } else {
211
+       corr = 1.0;
212
+       this->next_time = scale_u64(position, SPA_NSEC_PER_SEC, rate);
213
+   }
214
+
215
+   if (SPA_UNLIKELY((this->next_time - this->base_time) > BW_PERIOD)) {
216
+       this->base_time = this->next_time;
217
+       spa_log_debug(this->log, "%p: rate:%f "
218
+           "bw:%f dur:%"PRIu64" max:%f drift:%f",
219
+               this, corr, this->dll.bw, duration,
220
+               this->max_error, err);
221
+   }
222
 
223
    if (SPA_LIKELY(this->clock)) {
224
        this->clock->nsec = nsec;
225
-       this->clock->position += duration;
226
+       this->clock->position = position;
227
        this->clock->duration = duration;
228
        this->clock->delay = 0;
229
-       this->clock->rate_diff = 1.0;
230
+       this->clock->rate_diff = corr;
231
        this->clock->next_nsec = this->next_time;
232
    }
233
 
234
@@ -228,6 +341,7 @@
235
    this->following = is_following(this);
236
    set_timers(this);
237
    this->started = true;
238
+   this->last_time = 0;
239
    return 0;
240
 }
241
 
242
@@ -262,17 +376,19 @@
243
    return 0;
244
 }
245
 
246
-static const struct spa_dict_item node_info_items = {
247
-   { SPA_KEY_NODE_DRIVER, "true" },
248
-};
249
-
250
 static void emit_node_info(struct impl *this, bool full)
251
 {
252
    uint64_t old = full ? this->info.change_mask : 0;
253
    if (full)
254
        this->info.change_mask = this->info_all;
255
    if (this->info.change_mask) {
256
-       this->info.props = &SPA_DICT_INIT_ARRAY(node_info_items);
257
+       struct spa_dict_item items3;
258
+
259
+       items0 = SPA_DICT_ITEM_INIT(SPA_KEY_NODE_DRIVER, "true");
260
+       items1 = SPA_DICT_ITEM_INIT("clock.id", clock_id_to_name(this->props.clock_id));
261
+       items2 = SPA_DICT_ITEM_INIT("clock.name", this->props.clock_name);
262
+
263
+       this->info.props = &SPA_DICT_INIT(items, 3);
264
        spa_node_emit_info(&this->hooks, &this->info);
265
        this->info.change_mask = old;
266
    }
267
@@ -314,14 +430,12 @@
268
 static int impl_node_process(void *object)
269
 {
270
    struct impl *this = object;
271
-   struct timespec now;
272
 
273
    spa_return_val_if_fail(this != NULL, -EINVAL);
274
    spa_log_trace(this->log, "process %d", this->props.freewheel);
275
 
276
    if (this->props.freewheel) {
277
-       clock_gettime(CLOCK_MONOTONIC, &now);
278
-       this->next_time = SPA_TIMESPEC_TO_NSEC(&now);
279
+       this->next_time = gettime_nsec(this, this->timer_clockid);
280
        set_timeout(this, this->next_time);
281
    }
282
    return SPA_STATUS_HAVE_DATA | SPA_STATUS_NEED_DATA;
283
@@ -371,6 +485,9 @@
284
    spa_loop_invoke(this->data_loop, do_remove_timer, 0, NULL, 0, true, this);
285
    spa_system_close(this->data_system, this->timer_source.fd);
286
 
287
+   if (this->clock_fd != -1)
288
+       close(this->clock_fd);
289
+
290
    return 0;
291
 }
292
 
293
@@ -402,6 +519,8 @@
294
    this->log = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_Log);
295
    this->data_loop = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_DataLoop);
296
    this->data_system = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_DataSystem);
297
+   this->clock_fd = -1;
298
+   spa_dll_init(&this->dll);
299
 
300
    if (this->data_loop == NULL) {
301
        spa_log_error(this->log, "a data_loop is needed");
302
@@ -430,17 +549,6 @@
303
    this->info.params = this->params;
304
    this->info.n_params = 0;
305
 
306
-   this->timer_source.func = on_timeout;
307
-   this->timer_source.data = this;
308
-   this->timer_source.fd = spa_system_timerfd_create(this->data_system, CLOCK_MONOTONIC,
309
-                             SPA_FD_CLOEXEC | SPA_FD_NONBLOCK);
310
-   this->timer_source.mask = SPA_IO_IN;
311
-   this->timer_source.rmask = 0;
312
-   this->timerspec.it_value.tv_sec = 0;
313
-   this->timerspec.it_value.tv_nsec = 0;
314
-   this->timerspec.it_interval.tv_sec = 0;
315
-   this->timerspec.it_interval.tv_nsec = 0;
316
-
317
    reset_props(&this->props);
318
 
319
    for (i = 0; info && i < info->n_items; i++) {
320
@@ -451,8 +559,43 @@
321
        } else if (spa_streq(k, "clock.name")) {
322
            spa_scnprintf(this->props.clock_name,
323
                sizeof(this->props.clock_name), "%s", s);
324
+       } else if (spa_streq(k, "clock.id")) {
325
+           this->props.clock_id = clock_name_to_id(s);
326
+           if (this->props.clock_id == -1) {
327
+               spa_log_warn(this->log, "unknown clock id '%s'", s);
328
+               this->props.clock_id = DEFAULT_CLOCK_ID;
329
+           }
330
+       } else if (spa_streq(k, "clock.device")) {
331
+           this->clock_fd = open(s, O_RDWR);
332
+           if (this->clock_fd == -1) {
333
+               spa_log_warn(this->log, "failed to open clock device '%s'", s);
334
+           } else {
335
+               this->props.clock_id = FD_TO_CLOCKID(this->clock_fd);
336
+           }
337
        }
338
    }
339
+   if (this->props.clock_name0 == '\0') {
340
+       spa_scnprintf(this->props.clock_name, sizeof(this->props.clock_name),
341
+               "%s.%s", DEFAULT_CLOCK_PREFIX,
342
+               clock_id_to_name(this->props.clock_id));
343
+   }
344
+
345
+   this->tracking = !clock_for_timerfd(this->props.clock_id);
346
+   this->timer_clockid = this->tracking ? CLOCK_MONOTONIC : this->props.clock_id;
347
+   this->max_error = 128;
348
+
349
+   this->timer_source.func = on_timeout;
350
+   this->timer_source.data = this;
351
+   this->timer_source.fd = spa_system_timerfd_create(this->data_system,
352
+           this->timer_clockid, SPA_FD_CLOEXEC | SPA_FD_NONBLOCK);
353
+
354
+   this->timer_source.mask = SPA_IO_IN;
355
+   this->timer_source.rmask = 0;
356
+   this->timerspec.it_value.tv_sec = 0;
357
+   this->timerspec.it_value.tv_nsec = 0;
358
+   this->timerspec.it_interval.tv_sec = 0;
359
+   this->timerspec.it_interval.tv_nsec = 0;
360
+
361
    spa_loop_add_source(this->data_loop, &this->timer_source);
362
 
363
    return 0;
364
pipewire-0.3.65.tar.gz/src/daemon/client-rt.conf.in -> pipewire-0.3.66.tar.gz/src/daemon/client-rt.conf.in Changed
13
 
1
@@ -31,8 +31,9 @@
2
 
3
 context.modules = 
4
     #{ name = <module-name>
5
-    #     args  = { <key> = <value> ... } 
6
-    #     flags =   ifexists   nofail  
7
+    #    ( args  = { <key> = <value> ... } )
8
+    #    ( flags =  ( ifexists ) ( nofail )  )
9
+    #    ( condition =  { <key> = <value> ... } ...  )
10
     #}
11
     #
12
     # Loads a module with the given parameters.
13
pipewire-0.3.65.tar.gz/src/daemon/client.conf.in -> pipewire-0.3.66.tar.gz/src/daemon/client.conf.in Changed
13
 
1
@@ -31,8 +31,9 @@
2
 
3
 context.modules = 
4
     #{ name = <module-name>
5
-    #     args  = { <key> = <value> ... } 
6
-    #     flags =   ifexists   nofail  
7
+    #    ( args  = { <key> = <value> ... } )
8
+    #    ( flags =  ( ifexists ) ( nofail )  )
9
+    #    ( condition =  { <key> = <value> ... } ...  )
10
     #}
11
     #
12
     # Loads a module with the given parameters.
13
pipewire-0.3.65.tar.gz/src/daemon/filter-chain.conf.in -> pipewire-0.3.66.tar.gz/src/daemon/filter-chain.conf.in Changed
13
 
1
@@ -30,8 +30,9 @@
2
 
3
 context.modules = 
4
     #{ name = <module-name>
5
-    #     args  = { <key> = <value> ... } 
6
-    #     flags =   ifexists   nofail  
7
+    #    ( args  = { <key> = <value> ... } )
8
+    #    ( flags =  ( ifexists ) ( nofail )  )
9
+    #    ( condition =  { <key> = <value> ... } ...  )
10
     #}
11
     #
12
     # Loads a module with the given parameters.
13
pipewire-0.3.66.tar.gz/src/daemon/filter-chain/spatializer-7.1.conf Added
159
 
1
@@ -0,0 +1,157 @@
2
+# Headphone surround sink
3
+#
4
+# Copy this file into a conf.d/ directory such as
5
+# ~/.config/pipewire/filter-chain.conf.d/
6
+#
7
+context.modules = 
8
+    { name = libpipewire-module-filter-chain
9
+        args = {
10
+            node.description = "Spatial Sink"
11
+            media.name       = "Spatial Sink"
12
+            filter.graph = {
13
+                nodes = 
14
+                    {
15
+                        type = sofa
16
+                        label = spatializer
17
+                        name = spFL
18
+                        config = {
19
+                            filename = "~/.config/hrtf-sofa/hrtf b_nh724.sofa"
20
+                        }
21
+                        control = {
22
+                            "Azimuth"    = 30.0
23
+                            "Elevation"  = 0.0
24
+                            "Radius"     = 3.0
25
+                        }
26
+                    }
27
+                    {
28
+                        type = sofa
29
+                        label = spatializer
30
+                        name = spFR
31
+                        config = {
32
+                            filename = "~/.config/hrtf-sofa/hrtf b_nh724.sofa"
33
+                        }
34
+                        control = {
35
+                            "Azimuth"    = 330.0
36
+                            "Elevation"  = 0.0
37
+                            "Radius"     = 3.0
38
+                        }
39
+                    }
40
+                    {
41
+                        type = sofa
42
+                        label = spatializer
43
+                        name = spFC
44
+                        config = {
45
+                            filename = "~/.config/hrtf-sofa/hrtf b_nh724.sofa"
46
+                        }
47
+                        control = {
48
+                            "Azimuth"    = 0.0
49
+                            "Elevation"  = 0.0
50
+                            "Radius"     = 3.0
51
+                        }
52
+                    }
53
+                    {
54
+                        type = sofa
55
+                        label = spatializer
56
+                        name = spRL
57
+                        config = {
58
+                            filename = "~/.config/hrtf-sofa/hrtf b_nh724.sofa"
59
+                        }
60
+                        control = {
61
+                            "Azimuth"    = 150.0
62
+                            "Elevation"  = 0.0
63
+                            "Radius"     = 3.0
64
+                        }
65
+                    }
66
+                    {
67
+                        type = sofa
68
+                        label = spatializer
69
+                        name = spRR
70
+                        config = {
71
+                            filename = "~/.config/hrtf-sofa/hrtf b_nh724.sofa"
72
+                        }
73
+                        control = {
74
+                            "Azimuth"    = 210.0
75
+                            "Elevation"  = 0.0
76
+                            "Radius"     = 3.0
77
+                        }
78
+                    }
79
+                    {
80
+                        type = sofa
81
+                        label = spatializer
82
+                        name = spSL
83
+                        config = {
84
+                            filename = "~/.config/hrtf-sofa/hrtf b_nh724.sofa"
85
+                        }
86
+                        control = {
87
+                            "Azimuth"    = 90.0
88
+                            "Elevation"  = 0.0
89
+                            "Radius"     = 3.0
90
+                        }
91
+                    }
92
+                    {
93
+                        type = sofa
94
+                        label = spatializer
95
+                        name = spSR
96
+                        config = {
97
+                            filename = "~/.config/hrtf-sofa/hrtf b_nh724.sofa"
98
+                        }
99
+                        control = {
100
+                            "Azimuth"    = 270.0
101
+                            "Elevation"  = 0.0
102
+                            "Radius"     = 3.0
103
+                        }
104
+                    }
105
+                    {
106
+                        type = sofa
107
+                        label = spatializer
108
+                        name = spLFE
109
+                        config = {
110
+                            filename = "~/.config/hrtf-sofa/hrtf b_nh724.sofa"
111
+                        }
112
+                        control = {
113
+                            "Azimuth"    = 0.0
114
+                            "Elevation"  = -60.0
115
+                            "Radius"     = 3.0
116
+                        }
117
+                    }
118
+
119
+                    { type = builtin label = mixer name = mixL }
120
+                    { type = builtin label = mixer name = mixR }
121
+                
122
+                links = 
123
+                    # output
124
+                    { output = "spFL:Out L"  input="mixL:In 1" }
125
+                    { output = "spFL:Out R"  input="mixR:In 1" }
126
+                    { output = "spFR:Out L"  input="mixL:In 2" }
127
+                    { output = "spFR:Out R"  input="mixR:In 2" }
128
+                    { output = "spFC:Out L"  input="mixL:In 3" }
129
+                    { output = "spFC:Out R"  input="mixR:In 3" }
130
+                    { output = "spRL:Out L"  input="mixL:In 4" }
131
+                    { output = "spRL:Out R"  input="mixR:In 4" }
132
+                    { output = "spRR:Out L"  input="mixL:In 5" }
133
+                    { output = "spRR:Out R"  input="mixR:In 5" }
134
+                    { output = "spSL:Out L"  input="mixL:In 6" }
135
+                    { output = "spSL:Out R"  input="mixR:In 6" }
136
+                    { output = "spSR:Out L"  input="mixL:In 7" }
137
+                    { output = "spSR:Out R"  input="mixR:In 7" }
138
+                    { output = "spLFE:Out L" input="mixL:In 8" }
139
+                    { output = "spLFE:Out R" input="mixR:In 8" }
140
+                
141
+                inputs  =  "spFL:In" "spFR:In" "spFC:In" "spLFE:In" "spRL:In" "spRR:In", "spSL:In", "spSR:In" 
142
+                outputs =  "mixL:Out" "mixR:Out" 
143
+            }
144
+            capture.props = {
145
+                node.name      = "effect_input.spatializer"
146
+                media.class    = Audio/Sink
147
+                audio.channels = 8
148
+                audio.position =  FL FR FC LFE RL RR SL SR 
149
+            }
150
+            playback.props = {
151
+                node.name      = "effect_output.spatializer"
152
+                node.passive   = true
153
+                audio.channels = 2
154
+                audio.position =  FL FR 
155
+            }
156
+        }
157
+    }
158
+
159
pipewire-0.3.66.tar.gz/src/daemon/filter-chain/spatializer-single.conf Added
47
 
1
@@ -0,0 +1,45 @@
2
+# A virtual sound source sink
3
+# Useful for testing spatial effects by moving it around with controls
4
+#
5
+# Copy this file into a conf.d/ directory such as
6
+# ~/.config/pipewire/filter-chain.conf.d/
7
+#
8
+context.modules = 
9
+    { name = libpipewire-module-filter-chain
10
+        args = {
11
+            node.description = "3D Sink"
12
+            media.name       = "3D Sink"
13
+            filter.graph = {
14
+                nodes = 
15
+                    {
16
+                        type = sofa
17
+                        label = spatializer
18
+                        name = sp
19
+                        config = {
20
+                            filename = "~/.config/hrtf-sofa/hrtf b_nh724.sofa"
21
+                        }
22
+                        control = {
23
+                            "Azimuth"    = 220.0
24
+                            "Elevation"  = 0.0
25
+                            "Radius"     = 3.0
26
+                        }
27
+                    }
28
+                
29
+                inputs  =  "sp:In" 
30
+                outputs =  "sp:Out L" "sp:Out R" 
31
+            }
32
+            capture.props = {
33
+                node.name      = "effect_input.3d"
34
+                media.class    = Audio/Sink
35
+                audio.channels = 1
36
+                audio.position =  FC 
37
+            }
38
+            playback.props = {
39
+                node.name      = "effect_output.3d"
40
+                node.passive   = true
41
+                audio.channels = 2
42
+                audio.position =  FL FR 
43
+            }
44
+        }
45
+    }
46
+
47
pipewire-0.3.65.tar.gz/src/daemon/jack.conf.in -> pipewire-0.3.66.tar.gz/src/daemon/jack.conf.in Changed
13
 
1
@@ -30,8 +30,9 @@
2
 
3
 context.modules = 
4
     #{ name = <module-name>
5
-    #     args  = { <key> = <value> ... } 
6
-    #     flags =   ifexists   nofail  
7
+    #    ( args  = { <key> = <value> ... } )
8
+    #    ( flags =  ( ifexists ) ( nofail )  )
9
+    #    ( condition =  { <key> = <value> ... } ...  )
10
     #}
11
     #
12
     # Loads a module with the given parameters.
13
pipewire-0.3.65.tar.gz/src/daemon/meson.build -> pipewire-0.3.66.tar.gz/src/daemon/meson.build Changed
24
 
1
@@ -72,6 +72,7 @@
2
  'minimal.conf',
3
  'pipewire-pulse.conf',
4
  'pipewire-avb.conf',
5
+ 'pipewire-aes67.conf',
6
 
7
 
8
 foreach c : conf_files
9
@@ -109,6 +110,14 @@
10
   dependencies :  spa_dep, pipewire_dep, ,
11
 )
12
 
13
+executable('pipewire-aes67',
14
+  pipewire_daemon_sources,
15
+  install: true,
16
+  c_args : pipewire_c_args,
17
+  include_directories :  configinc ,
18
+  dependencies :  spa_dep, pipewire_dep, ,
19
+)
20
+
21
 ln = find_program('ln')
22
 
23
 custom_target('pipewire-uninstalled',
24
pipewire-0.3.65.tar.gz/src/daemon/minimal.conf.in -> pipewire-0.3.66.tar.gz/src/daemon/minimal.conf.in Changed
37
 
1
@@ -60,8 +60,9 @@
2
 
3
 context.modules = 
4
     #{ name = <module-name>
5
-    #     args  = { <key> = <value> ... } 
6
-    #     flags =   ifexists   nofail  
7
+    #    ( args  = { <key> = <value> ... } )
8
+    #    ( flags =  ( ifexists ) ( nofail )  )
9
+    #    ( condition =  { <key> = <value> ... } ...  )
10
     #}
11
     #
12
     # Loads a module with the given parameters.
13
@@ -135,8 +136,9 @@
14
 
15
 context.objects = 
16
     #{ factory = <factory-name>
17
-    #     args  = { <key> = <value> ... } 
18
-    #     flags =   nofail  
19
+    #    ( args  = { <key> = <value> ... } )
20
+    #    ( flags =  ( nofail )  )
21
+    #    ( condition =  { <key> = <value> ... } ...  )
22
     #}
23
     #
24
     # Creates an object from a PipeWire factory with the given parameters.
25
@@ -339,7 +341,10 @@
26
 
27
 
28
 context.exec = 
29
-    #{ path = <program-name>  args = "<arguments>"  }
30
+    #{   path = <program-name>
31
+    #    ( args = "<arguments>" )
32
+    #    ( condition =  { <key> = <value> ... } ...  )
33
+    #}
34
     #
35
     # Execute the given program with arguments.
36
     #
37
pipewire-0.3.66.tar.gz/src/daemon/pipewire-aes67.conf.in Added
54
 
1
@@ -0,0 +1,52 @@
2
+# AES67 config file for PipeWire version @VERSION@ #
3
+#
4
+# Copy and edit this file in @PIPEWIRE_CONFIG_DIR@ for system-wide changes
5
+# or in ~/.config/pipewire for local changes.
6
+#
7
+# It is also possible to place a file with an updated section in
8
+# @PIPEWIRE_CONFIG_DIR@/pipewire-aes67.conf.d/ for system-wide changes or in
9
+# ~/.config/pipewire/pipewire-aes67.conf.d/ for local changes.
10
+#
11
+
12
+context.properties = {
13
+    ## Configure properties in the system.
14
+    #mem.warn-mlock  = false
15
+    #mem.allow-mlock = true
16
+    #mem.mlock-all   = false
17
+    #log.level       = 2
18
+
19
+    #default.clock.quantum-limit = 8192
20
+}
21
+
22
+#context.spa-libs = {
23
+#    audio.convert.* = audioconvert/libspa-audioconvert
24
+#    support.*       = support/libspa-support
25
+#}
26
+
27
+context.modules = 
28
+    { name = libpipewire-module-rt
29
+        args = {
30
+            nice.level   = -11
31
+            #rt.prio      = 88
32
+            #rt.time.soft = -1
33
+            #rt.time.hard = -1
34
+        }
35
+        flags =  ifexists nofail 
36
+    }
37
+    { name = libpipewire-module-protocol-native }
38
+    { name = libpipewire-module-client-node }
39
+    { name = libpipewire-module-adapter }
40
+    { name = libpipewire-module-rtp-source
41
+        args = {
42
+            sap.ip = 239.255.255.255
43
+            sap.port = 9875
44
+            sess.latency.msec = 10
45
+            local.ifname = eth0
46
+            stream.props = {
47
+               media.class = "Audio/Source"
48
+               node.virtual = false
49
+               device.api = aes67
50
+            }
51
+        }
52
+    }
53
+
54
pipewire-0.3.65.tar.gz/src/daemon/pipewire-pulse.conf.in -> pipewire-0.3.66.tar.gz/src/daemon/pipewire-pulse.conf.in Changed
15
 
1
@@ -57,11 +57,11 @@
2
 # Extra commands can be executed here.
3
 #   load-module : loads a module with args and flags
4
 #      args = "<module-name> <module-args>"
5
-#      flags =  "no-fail" 
6
+#      ( flags =  nofail  )
7
 pulse.cmd = 
8
     { cmd = "load-module" args = "module-always-sink" flags =   }
9
     #{ cmd = "load-module" args = "module-switch-on-connect" }
10
-    #{ cmd = "load-module" args = "module-gsettings" flags =  "nofail"  }
11
+    #{ cmd = "load-module" args = "module-gsettings" flags =  nofail  }
12
 
13
 
14
 stream.properties = {
15
pipewire-0.3.65.tar.gz/src/daemon/pipewire.conf.in -> pipewire-0.3.66.tar.gz/src/daemon/pipewire.conf.in Changed
109
 
1
@@ -29,7 +29,7 @@
2
     #default.clock.rate          = 48000
3
     #default.clock.allowed-rates =  48000 
4
     #default.clock.quantum       = 1024
5
-    default.clock.min-quantum   = 16
6
+    #default.clock.min-quantum   = 32
7
     #default.clock.max-quantum   = 2048
8
     #default.clock.quantum-limit = 8192
9
     #default.video.width         = 640
10
@@ -44,6 +44,9 @@
11
     vm.overrides = {
12
         default.clock.min-quantum = 1024
13
     }
14
+
15
+    # keys checked below to disable module loading
16
+    module.x11.bell = true
17
 }
18
 
19
 context.spa-libs = {
20
@@ -68,13 +71,16 @@
21
 
22
 context.modules = 
23
     #{ name = <module-name>
24
-    #     args  = { <key> = <value> ... } 
25
-    #     flags =   ifexists   nofail  
26
+    #    ( args  = { <key> = <value> ... } )
27
+    #    ( flags =  ( ifexists ) ( nofail )  )
28
+    #    ( condition =  { <key> = <value> ... } ...  )
29
     #}
30
     #
31
     # Loads a module with the given parameters.
32
     # If ifexists is given, the module is ignored when it is not found.
33
     # If nofail is given, module initialization failures are ignored.
34
+    # If condition is given, the module is loaded only when the context
35
+    # properties all match the match rules.
36
     #
37
 
38
     # Uses realtime scheduling to boost the audio thread priorities. This uses
39
@@ -167,17 +173,21 @@
40
             #x11.xauthority = null
41
         }
42
         flags =  ifexists nofail 
43
+        condition =  { module.x11.bell = true } 
44
     }
45
 
46
 
47
 context.objects = 
48
     #{ factory = <factory-name>
49
-    #     args  = { <key> = <value> ... } 
50
-    #     flags =   nofail  
51
+    #    ( args  = { <key> = <value> ... } )
52
+    #    ( flags =  ( nofail )  )
53
+    #    ( condition =  { <key> = <value> ... } ...  )
54
     #}
55
     #
56
     # Creates an object from a PipeWire factory with the given parameters.
57
     # If nofail is given, errors are ignored (and no object is created).
58
+    # If condition is given, the object is created only when the context properties
59
+    # all match the match rules.
60
     #
61
     #{ factory = spa-node-factory   args = { factory.name = videotestsrc node.name = videotestsrc Spa:Pod:Object:Param:Props:patternType = 1 } }
62
     #{ factory = spa-device-factory args = { factory.name = api.jack.device foo=bar } flags =  nofail  }
63
@@ -194,6 +204,8 @@
64
             node.name       = Dummy-Driver
65
             node.group      = pipewire.dummy
66
             priority.driver = 20000
67
+            #clock.id       = monotonic # realtime | tai | monotonic-raw | boottime
68
+            #clock.name     = "clock.system.monotonic"
69
         }
70
     }
71
     { factory = spa-node-factory
72
@@ -205,6 +217,20 @@
73
             node.freewheel  = true
74
         }
75
     }
76
+    # An example clock reading from /dev/ptp0. Another option is to sync the
77
+    # ptp clock to CLOCK_TAI and then set clock.id = tai.
78
+    #{ factory = spa-node-factory
79
+    #    args = {
80
+    #        factory.name    = support.node.driver
81
+    #        node.name       = PTP0-Driver
82
+    #        node.group      = pipewire.ptp0
83
+    #        priority.driver = 30000
84
+    #        clock.name      = "clock.system.ptp0"
85
+    #        #clock.id       = tai
86
+    #        clock.device    = "/dev/ptp0"
87
+    #    }
88
+    #}
89
+
90
     # This creates a new Source node. It will have input ports
91
     # that you can link, to provide audio for this source.
92
     #{ factory = adapter
93
@@ -240,9 +266,14 @@
94
 
95
 
96
 context.exec = 
97
-    #{ path = <program-name>  args = "<arguments>"  }
98
+    #{   path = <program-name>
99
+    #    ( args = "<arguments>" )
100
+    #    ( condition =  { <key> = <value> ... } ...  )
101
+    #}
102
     #
103
     # Execute the given program with arguments.
104
+    # If condition is given, the program is executed only when the context
105
+    # properties all match the match rules.
106
     #
107
     # You can optionally start the session manager here,
108
     # but it is better to start it as a systemd service.
109
pipewire-0.3.65.tar.gz/src/gst/gstpipewirepool.c -> pipewire-0.3.66.tar.gz/src/gst/gstpipewirepool.c Changed
69
 
1
@@ -109,6 +109,16 @@
2
       gst_buffer_insert_memory (buf, i, gmem);
3
   }
4
 
5
+  if (pool->add_metavideo) {
6
+    gst_buffer_add_video_meta_full (buf, GST_VIDEO_FRAME_FLAG_NONE,
7
+        GST_VIDEO_INFO_FORMAT (&pool->video_info),
8
+        GST_VIDEO_INFO_WIDTH (&pool->video_info),
9
+        GST_VIDEO_INFO_HEIGHT (&pool->video_info),
10
+        GST_VIDEO_INFO_N_PLANES (&pool->video_info),
11
+        pool->video_info.offset,
12
+        pool->video_info.stride);
13
+  }
14
+
15
   data->pool = gst_object_ref (pool);
16
   data->owner = NULL;
17
   data->header = spa_buffer_find_meta_data (b->buffer, SPA_META_Header, sizeof(*data->header));
18
@@ -208,6 +218,41 @@
19
   }
20
 }
21
 
22
+static const gchar **
23
+get_options (GstBufferPool * pool)
24
+{
25
+  static const gchar *options = { GST_BUFFER_POOL_OPTION_VIDEO_META, NULL };
26
+  return options;
27
+}
28
+
29
+static gboolean
30
+set_config (GstBufferPool * pool, GstStructure * config)
31
+{
32
+  GstPipeWirePool *p = GST_PIPEWIRE_POOL (pool);
33
+  GstCaps *caps;
34
+  guint size, min_buffers, max_buffers;
35
+  gboolean has_video;
36
+
37
+  if (!gst_buffer_pool_config_get_params (config, &caps, &size, &min_buffers, &max_buffers)) {
38
+    GST_WARNING_OBJECT (pool, "invalid config");
39
+    return FALSE;
40
+  }
41
+
42
+  if (caps == NULL) {
43
+    GST_WARNING_OBJECT (pool, "no caps in config");
44
+    return FALSE;
45
+  }
46
+
47
+  has_video = gst_video_info_from_caps (&p->video_info, caps);
48
+
49
+  p->add_metavideo = has_video && gst_buffer_pool_config_has_option (config,
50
+      GST_BUFFER_POOL_OPTION_VIDEO_META);
51
+
52
+  gst_buffer_pool_config_set_params (config, caps, p->video_info.size, min_buffers, max_buffers);
53
+
54
+  return GST_BUFFER_POOL_CLASS (gst_pipewire_pool_parent_class)->set_config (pool, config);
55
+}
56
+
57
 static void
58
 flush_start (GstBufferPool * pool)
59
 {
60
@@ -252,6 +297,8 @@
61
 
62
   gobject_class->finalize = gst_pipewire_pool_finalize;
63
 
64
+  bufferpool_class->get_options = get_options;
65
+  bufferpool_class->set_config = set_config;
66
   bufferpool_class->start = do_start;
67
   bufferpool_class->flush_start = flush_start;
68
   bufferpool_class->acquire_buffer = acquire_buffer;
69
pipewire-0.3.65.tar.gz/src/gst/gstpipewirepool.h -> pipewire-0.3.66.tar.gz/src/gst/gstpipewirepool.h Changed
20
 
1
@@ -27,6 +27,8 @@
2
 
3
 #include <gst/gst.h>
4
 
5
+#include <gst/video/video.h>
6
+
7
 #include <pipewire/pipewire.h>
8
 
9
 G_BEGIN_DECLS
10
@@ -66,6 +68,9 @@
11
   struct pw_stream *stream;
12
   struct pw_type *t;
13
 
14
+  gboolean add_metavideo;
15
+  GstVideoInfo video_info;
16
+
17
   GstAllocator *fd_allocator;
18
   GstAllocator *dmabuf_allocator;
19
 
20
pipewire-0.3.65.tar.gz/src/gst/gstpipewiresink.c -> pipewire-0.3.66.tar.gz/src/gst/gstpipewiresink.c Changed
32
 
1
@@ -142,6 +142,7 @@
2
   GstPipeWireSink *pwsink = GST_PIPEWIRE_SINK (bsink);
3
 
4
   gst_query_add_allocation_pool (query, GST_BUFFER_POOL_CAST (pwsink->pool), 0, 0, 0);
5
+  gst_query_add_allocation_meta (query, GST_VIDEO_META_API_TYPE, NULL);
6
   return TRUE;
7
 }
8
 
9
@@ -504,6 +505,22 @@
10
     d->chunk->stride = 0;
11
   }
12
 
13
+  GstVideoMeta *meta = gst_buffer_get_video_meta (buffer);
14
+  if (meta) {
15
+    if (meta->n_planes == b->n_datas) {
16
+      gsize video_size = 0;
17
+      for (i = 0; i < meta->n_planes; i++) {
18
+        struct spa_data *d = &b->datasi;
19
+        d->chunk->offset += meta->offseti - video_size;
20
+        d->chunk->stride = meta->stridei;
21
+
22
+        video_size += d->chunk->size;
23
+      }
24
+    } else {
25
+      GST_ERROR ("plane num not matching, meta:%u buffer:%u", meta->n_planes, b->n_datas);
26
+    }
27
+  }
28
+
29
   if ((res = pw_stream_queue_buffer (pwsink->stream, data->b)) < 0) {
30
     g_warning ("can't send buffer %s", spa_strerror(res));
31
   }
32
pipewire-0.3.65.tar.gz/src/gst/gstpipewiresrc.c -> pipewire-0.3.66.tar.gz/src/gst/gstpipewiresrc.c Changed
48
 
1
@@ -615,6 +615,27 @@
2
     }
3
   }
4
 
5
+  if (pwsrc->is_video) {
6
+    gsize video_size = 0;
7
+    GstVideoInfo *info = &pwsrc->video_info;
8
+    GstVideoMeta *meta = gst_buffer_add_video_meta_full (buf, GST_VIDEO_FRAME_FLAG_NONE,
9
+                             GST_VIDEO_INFO_FORMAT (info),
10
+                             GST_VIDEO_INFO_WIDTH (info),
11
+                             GST_VIDEO_INFO_HEIGHT (info),
12
+                             GST_VIDEO_INFO_N_PLANES (info),
13
+                             info->offset,
14
+                             info->stride);
15
+
16
+    meta->n_planes = MIN(meta->n_planes, b->buffer->n_datas);
17
+    for (i = 0; i < meta->n_planes; i++) {
18
+      struct spa_data *d = &b->buffer->datasi;
19
+      meta->offseti = video_size;
20
+      meta->stridei = d->chunk->stride;
21
+
22
+      video_size += d->chunk->size;
23
+    }
24
+  }
25
+
26
   for (i = 0; i < b->buffer->n_datas; i++) {
27
     struct spa_data *d = &b->buffer->datasi;
28
     GstMemory *pmem = gst_buffer_peek_memory (data->buf, i);
29
@@ -658,6 +679,7 @@
30
     case PW_STREAM_STATE_STREAMING:
31
       break;
32
     case PW_STREAM_STATE_ERROR:
33
+      pw_stream_set_error (pwsrc->stream, -EPIPE, "%s", error);
34
       GST_ELEMENT_ERROR (pwsrc, RESOURCE, FAILED,
35
           ("stream error: %s", error), (NULL));
36
       break;
37
@@ -958,6 +980,10 @@
38
           gst_caps_unref(pwsrc->caps);
39
   pwsrc->caps = gst_caps_from_format (param);
40
 
41
+  pwsrc->is_video = pwsrc->caps != NULL
42
+                      ? gst_video_info_from_caps (&pwsrc->video_info, pwsrc->caps)
43
+                      : FALSE;
44
+
45
   pwsrc->negotiated = pwsrc->caps != NULL;
46
 
47
   if (pwsrc->negotiated) {
48
pipewire-0.3.65.tar.gz/src/gst/gstpipewiresrc.h -> pipewire-0.3.66.tar.gz/src/gst/gstpipewiresrc.h Changed
20
 
1
@@ -28,6 +28,8 @@
2
 #include <gst/gst.h>
3
 #include <gst/base/gstpushsrc.h>
4
 
5
+#include <gst/video/video.h>
6
+
7
 #include <pipewire/pipewire.h>
8
 #include <gst/gstpipewirepool.h>
9
 #include <gst/gstpipewirecore.h>
10
@@ -71,6 +73,9 @@
11
 
12
   GstCaps *caps;
13
 
14
+  gboolean is_video;
15
+  GstVideoInfo video_info;
16
+
17
   gboolean negotiated;
18
   gboolean flushing;
19
   gboolean started;
20
pipewire-0.3.65.tar.gz/src/modules/meson.build -> pipewire-0.3.66.tar.gz/src/modules/meson.build Changed
282
 
1
@@ -1,3 +1,4 @@
2
+subdir('module-rt')
3
 subdir('spa')
4
 
5
 # The list of "main" source files for modules, the ones that have the
6
@@ -108,10 +109,11 @@
7
   'module-filter-chain/biquad.c',
8
   'module-filter-chain/ladspa_plugin.c',
9
   'module-filter-chain/builtin_plugin.c',
10
+  'module-filter-chain/sofa_plugin.c',
11
   'module-filter-chain/convolver.c'
12
 
13
 filter_chain_dependencies = 
14
-  mathlib, dl_lib, pipewire_dep, sndfile_dep, audioconvert_dep
15
+  mathlib, dl_lib, pipewire_dep, sndfile_dep, audioconvert_dep, libmysofa_dep
16
 
17
 
18
 if lilv_lib.found()
19
@@ -139,7 +141,7 @@
20
 pipewire_module_combine_stream = shared_library('pipewire-module-combine-stream',
21
    'module-combine-stream.c' ,
22
   include_directories : configinc,
23
-  install : false,
24
+  install : true,
25
   install_dir : modules_install_dir,
26
   install_rpath: modules_install_dir,
27
   dependencies : spa_dep, dl_lib, pipewire_dep,
28
@@ -174,27 +176,25 @@
29
 
30
 build_module_rtkit = dbus_dep.found() and (get_option('legacy-rtkit') == true)
31
 if build_module_rtkit
32
-# TODO: This serves as a temporary alias to prevent breaking existing setups
33
-#       while `module-rtkit` is being migrated to `module-rt`
34
-pipewire_module_rtkit = shared_library('pipewire-module-rtkit',  'module-rt.c' ,
35
-  include_directories : configinc,
36
-  install : true,
37
-  install_dir : modules_install_dir,
38
-  install_rpath: modules_install_dir,
39
-  dependencies : dbus_dep, mathlib, dl_lib, pipewire_dep,
40
-)
41
+  pipewire_module_rtkit = shared_library('pipewire-module-rtkit',  'module-rt.c' ,
42
+    include_directories : configinc,
43
+    install : true,
44
+    install_dir : modules_install_dir,
45
+    install_rpath: modules_install_dir,
46
+    dependencies : dbus_dep, mathlib, dl_lib, pipewire_dep,
47
+  )
48
 endif
49
 summary({'rt': '@0@ RTKit'.format(build_module_rtkit ? 'with' : 'without')}, section: 'Optional Modules')
50
 
51
 build_module_portal = dbus_dep.found()
52
 if build_module_portal
53
-pipewire_module_portal = shared_library('pipewire-module-portal',  'module-portal.c' ,
54
-  include_directories : configinc,
55
-  install : true,
56
-  install_dir : modules_install_dir,
57
-  install_rpath: modules_install_dir,
58
-  dependencies : dbus_dep, mathlib, dl_lib, pipewire_dep,
59
-)
60
+  pipewire_module_portal = shared_library('pipewire-module-portal',  'module-portal.c' ,
61
+    include_directories : configinc,
62
+    install : true,
63
+    install_dir : modules_install_dir,
64
+    install_rpath: modules_install_dir,
65
+    dependencies : dbus_dep, mathlib, dl_lib, pipewire_dep,
66
+  )
67
 endif
68
 summary({'portal': build_module_portal}, bool_yn: true, section: 'Optional Modules')
69
 
70
@@ -332,16 +332,16 @@
71
 )
72
 
73
 build_module_pulse_tunnel = pulseaudio_dep.found()
74
-if build_module_pulse_tunnel
75
-  pipewire_module_pulse_tunnel = shared_library('pipewire-module-pulse-tunnel',
76
-   'module-pulse-tunnel.c',
77
-    'module-protocol-pulse/format.c' ,
78
-  include_directories : configinc,
79
-  install : true,
80
-  install_dir : modules_install_dir,
81
-  install_rpath: modules_install_dir,
82
-  dependencies : mathlib, dl_lib, pipewire_dep, pulseaudio_dep,
83
-)
84
+  if build_module_pulse_tunnel
85
+    pipewire_module_pulse_tunnel = shared_library('pipewire-module-pulse-tunnel',
86
+     'module-pulse-tunnel.c',
87
+      'module-protocol-pulse/format.c' ,
88
+    include_directories : configinc,
89
+    install : true,
90
+    install_dir : modules_install_dir,
91
+    install_rpath: modules_install_dir,
92
+    dependencies : mathlib, dl_lib, pipewire_dep, pulseaudio_dep,
93
+  )
94
 endif
95
 summary({'pulse-tunnel': build_module_pulse_tunnel}, bool_yn: true, section: 'Optional Modules')
96
 
97
@@ -474,44 +474,44 @@
98
 
99
 build_module_zeroconf_discover = avahi_dep.found()
100
 if build_module_zeroconf_discover
101
-pipewire_module_zeroconf_discover = shared_library('pipewire-module-zeroconf-discover',
102
-   'module-zeroconf-discover.c',
103
-    'module-protocol-pulse/format.c',
104
-    'module-zeroconf-discover/avahi-poll.c' ,
105
-  include_directories : configinc,
106
-  install : true,
107
-  install_dir : modules_install_dir,
108
-  install_rpath: modules_install_dir,
109
-  dependencies : mathlib, dl_lib, rt_lib, pipewire_dep, avahi_dep,
110
-)
111
+  pipewire_module_zeroconf_discover = shared_library('pipewire-module-zeroconf-discover',
112
+     'module-zeroconf-discover.c',
113
+      'module-protocol-pulse/format.c',
114
+      'module-zeroconf-discover/avahi-poll.c' ,
115
+    include_directories : configinc,
116
+    install : true,
117
+    install_dir : modules_install_dir,
118
+    install_rpath: modules_install_dir,
119
+    dependencies : mathlib, dl_lib, rt_lib, pipewire_dep, avahi_dep,
120
+  )
121
 endif
122
 summary({'zeroconf-discover': build_module_zeroconf_discover}, bool_yn: true, section: 'Optional Modules')
123
 
124
 build_module_raop_discover = avahi_dep.found()
125
 if build_module_raop_discover
126
-pipewire_module_raop_discover = shared_library('pipewire-module-raop-discover',
127
-   'module-raop-discover.c',
128
-    'module-zeroconf-discover/avahi-poll.c' ,
129
-  include_directories : configinc,
130
-  install : true,
131
-  install_dir : modules_install_dir,
132
-  install_rpath: modules_install_dir,
133
-  dependencies : mathlib, dl_lib, rt_lib, pipewire_dep, avahi_dep,
134
-)
135
+  pipewire_module_raop_discover = shared_library('pipewire-module-raop-discover',
136
+     'module-raop-discover.c',
137
+      'module-zeroconf-discover/avahi-poll.c' ,
138
+    include_directories : configinc,
139
+    install : true,
140
+    install_dir : modules_install_dir,
141
+    install_rpath: modules_install_dir,
142
+    dependencies : mathlib, dl_lib, rt_lib, pipewire_dep, avahi_dep,
143
+  )
144
 endif
145
 summary({'raop-discover (needs Avahi)': build_module_raop_discover}, bool_yn: true, section: 'Optional Modules')
146
 
147
 build_module_raop = openssl_lib.found()
148
 if build_module_raop
149
-pipewire_module_raop_sink = shared_library('pipewire-module-raop-sink',
150
-   'module-raop-sink.c',
151
-    'module-raop/rtsp-client.c' ,
152
-  include_directories : configinc,
153
-  install : true,
154
-  install_dir : modules_install_dir,
155
-  install_rpath: modules_install_dir,
156
-  dependencies : mathlib, dl_lib, rt_lib, pipewire_dep, openssl_lib,
157
-)
158
+  pipewire_module_raop_sink = shared_library('pipewire-module-raop-sink',
159
+     'module-raop-sink.c',
160
+      'module-raop/rtsp-client.c' ,
161
+    include_directories : configinc,
162
+    install : true,
163
+    install_dir : modules_install_dir,
164
+    install_rpath: modules_install_dir,
165
+    dependencies : mathlib, dl_lib, rt_lib, pipewire_dep, openssl_lib,
166
+  )
167
 endif
168
 summary({'raop-sink (requires OpenSSL)': build_module_raop}, bool_yn: true, section: 'Optional Modules')
169
 
170
@@ -538,37 +538,37 @@
171
 
172
 build_module_roc = roc_dep.found()
173
 if build_module_roc
174
-pipewire_module_roc_sink = shared_library('pipewire-module-roc-sink',
175
-   'module-roc-sink.c' ,
176
-  include_directories : configinc,
177
-  install : true,
178
-  install_dir : modules_install_dir,
179
-  install_rpath: modules_install_dir,
180
-  dependencies : mathlib, dl_lib, rt_lib, pipewire_dep, roc_dep,
181
-)
182
+  pipewire_module_roc_sink = shared_library('pipewire-module-roc-sink',
183
+     'module-roc-sink.c' ,
184
+    include_directories : configinc,
185
+    install : true,
186
+    install_dir : modules_install_dir,
187
+    install_rpath: modules_install_dir,
188
+    dependencies : mathlib, dl_lib, rt_lib, pipewire_dep, roc_dep,
189
+  )
190
 
191
-pipewire_module_roc_source = shared_library('pipewire-module-roc-source',
192
-   'module-roc-source.c' ,
193
-  include_directories : configinc,
194
-  install : true,
195
-  install_dir : modules_install_dir,
196
-  install_rpath: modules_install_dir,
197
-  dependencies : mathlib, dl_lib, rt_lib, pipewire_dep, roc_dep,
198
-)
199
+  pipewire_module_roc_source = shared_library('pipewire-module-roc-source',
200
+     'module-roc-source.c' ,
201
+    include_directories : configinc,
202
+    install : true,
203
+    install_dir : modules_install_dir,
204
+    install_rpath: modules_install_dir,
205
+    dependencies : mathlib, dl_lib, rt_lib, pipewire_dep, roc_dep,
206
+  )
207
 endif
208
 summary({'roc-sink': build_module_roc}, bool_yn: true, section: 'Optional Modules')
209
 summary({'roc-source': build_module_roc}, bool_yn: true, section: 'Optional Modules')
210
 
211
 build_module_x11_bell = x11_dep.found() and canberra_dep.found()
212
 if build_module_x11_bell
213
-pipewire_module_x11_bell = shared_library('pipewire-module-x11-bell',
214
-   'module-x11-bell.c' ,
215
-  include_directories : configinc,
216
-  install : true,
217
-  install_dir : modules_install_dir,
218
-  install_rpath: modules_install_dir,
219
-  dependencies : mathlib, dl_lib, rt_lib, pipewire_dep, x11_dep, xfixes_dep, canberra_dep,
220
-)
221
+  pipewire_module_x11_bell = shared_library('pipewire-module-x11-bell',
222
+     'module-x11-bell.c' ,
223
+    include_directories : configinc,
224
+    install : true,
225
+    install_dir : modules_install_dir,
226
+    install_rpath: modules_install_dir,
227
+    dependencies : mathlib, dl_lib, rt_lib, pipewire_dep, x11_dep, xfixes_dep, canberra_dep,
228
+  )
229
 endif
230
 summary({'x11-bell': build_module_x11_bell}, bool_yn: true, section: 'Optional Modules')
231
 
232
@@ -583,27 +583,27 @@
233
 
234
 build_module_avb = get_option('avb').require(host_machine.system() == 'linux', error_message: 'AVB support is only available on Linux').allowed()
235
 if build_module_avb
236
-pipewire_module_avb = shared_library('pipewire-module-avb',
237
-   'module-avb.c',
238
-    'module-avb/avb.c',
239
-    'module-avb/adp.c',
240
-    'module-avb/acmp.c',
241
-    'module-avb/aecp.c',
242
-    'module-avb/aecp-aem.c',
243
-    'module-avb/avdecc.c',
244
-    'module-avb/maap.c',
245
-    'module-avb/mmrp.c',
246
-    'module-avb/mrp.c',
247
-    'module-avb/msrp.c',
248
-    'module-avb/mvrp.c',
249
-    'module-avb/srp.c',
250
-    'module-avb/stream.c'
251
-    ,
252
-  include_directories : configinc,
253
-  install : true,
254
-  install_dir : modules_install_dir,
255
-  install_rpath: modules_install_dir,
256
-  dependencies : mathlib, dl_lib, rt_lib, pipewire_dep,
257
-)
258
+  pipewire_module_avb = shared_library('pipewire-module-avb',
259
+     'module-avb.c',
260
+      'module-avb/avb.c',
261
+      'module-avb/adp.c',
262
+      'module-avb/acmp.c',
263
+      'module-avb/aecp.c',
264
+      'module-avb/aecp-aem.c',
265
+      'module-avb/avdecc.c',
266
+      'module-avb/maap.c',
267
+      'module-avb/mmrp.c',
268
+      'module-avb/mrp.c',
269
+      'module-avb/msrp.c',
270
+      'module-avb/mvrp.c',
271
+      'module-avb/srp.c',
272
+      'module-avb/stream.c'
273
+      ,
274
+    include_directories : configinc,
275
+    install : true,
276
+    install_dir : modules_install_dir,
277
+    install_rpath: modules_install_dir,
278
+    dependencies : mathlib, dl_lib, rt_lib, pipewire_dep,
279
+  )
280
 endif
281
 summary({'avb': build_module_avb}, bool_yn: true, section: 'Optional Modules')
282
pipewire-0.3.65.tar.gz/src/modules/module-combine-stream.c -> pipewire-0.3.66.tar.gz/src/modules/module-combine-stream.c Changed
9
 
1
@@ -847,6 +847,7 @@
2
 }
3
 
4
 static const struct pw_proxy_events core_proxy_events = {
5
+   PW_VERSION_PROXY_EVENTS,
6
    .removed = core_removed,
7
 };
8
 
9
pipewire-0.3.65.tar.gz/src/modules/module-filter-chain.c -> pipewire-0.3.66.tar.gz/src/modules/module-filter-chain.c Changed
130
 
1
@@ -273,6 +273,12 @@
2
  * - `max-delay` the maximum delay in seconds. The "Delay (s)" parameter will
3
  *              be clamped to this value.
4
  *
5
+ * ### Invert
6
+ *
7
+ * The invert plugin can be used to invert the phase of the signal.
8
+ *
9
+ * It has an input port "In" and an output port "Out".
10
+ *
11
  * ## General options
12
  *
13
  * Options with well-known behavior. Most options can be added to the global
14
@@ -445,7 +451,7 @@
15
 struct plugin {
16
    struct spa_list link;
17
    int ref;
18
-   char type64;
19
+   char type256;
20
    char pathPATH_MAX;
21
 
22
    struct fc_plugin *plugin;
23
@@ -506,6 +512,7 @@
24
    unsigned int n_deps;
25
    unsigned int visited:1;
26
    unsigned int disabled:1;
27
+   unsigned int control_changed:1;
28
 };
29
 
30
 struct link {
31
@@ -878,7 +885,8 @@
32
    old = port->control_data;
33
    port->control_data = value ? *value : desc->default_controlport->idx;
34
    pw_log_info("control %d ('%s') from %f to %f", port->idx, name, old, port->control_data);
35
-   return old == port->control_data ? 0 : 1;
36
+   node->control_changed = old != port->control_data;
37
+   return node->control_changed ? 1 : 0;
38
 }
39
 
40
 static int parse_params(struct graph *graph, const struct spa_pod *pod)
41
@@ -938,6 +946,24 @@
42
            d->activate(*hndl->hndl);
43
    }
44
 }
45
+
46
+static void node_control_changed(struct node *node)
47
+{
48
+   const struct fc_descriptor *d = node->desc->desc;
49
+   uint32_t i;
50
+
51
+   if (!node->control_changed)
52
+       return;
53
+
54
+   for (i = 0; i < node->n_hndl; i++) {
55
+       if (node->hndli == NULL)
56
+           continue;
57
+       if (d->control_changed)
58
+           d->control_changed(node->hndli);
59
+   }
60
+   node->control_changed = false;
61
+}
62
+
63
 static void param_props_changed(struct impl *impl, const struct spa_pod *param)
64
 {
65
    struct spa_pod_object *obj = (struct spa_pod_object *) param;
66
@@ -953,6 +979,10 @@
67
        uint8_t buffer1024;
68
        struct spa_pod_dynamic_builder b;
69
        const struct spa_pod *params1;
70
+       struct node *node;
71
+
72
+       spa_list_for_each(node, &graph->node_list, link)
73
+           node_control_changed(node);
74
 
75
        spa_pod_dynamic_builder_init(&b, buffer, sizeof(buffer), 4096);
76
        params0 = get_props_param(graph, &b.b);
77
@@ -1205,6 +1235,9 @@
78
    if (spa_streq(type, "builtin")) {
79
        pl = load_builtin_plugin(support, n_support, &impl->dsp, path, NULL);
80
    }
81
+   else if (spa_streq(type, "sofa")) {
82
+       pl = load_sofa_plugin(support, n_support, &impl->dsp, path, NULL);
83
+   }
84
    else if (spa_streq(type, "ladspa")) {
85
        pl = load_ladspa_plugin(support, n_support, &impl->dsp, path, NULL);
86
    }
87
@@ -1696,6 +1729,7 @@
88
    free(node->output_port);
89
    free(node->control_port);
90
    free(node->notify_port);
91
+   free(node->config);
92
    free(node);
93
 }
94
 
95
@@ -1767,6 +1801,8 @@
96
            }
97
            if (d->activate)
98
                d->activate(node->hndli);
99
+           if (node->control_changed && d->control_changed)
100
+               d->control_changed(node->hndli);
101
        }
102
    }
103
    return 0;
104
@@ -1902,6 +1938,8 @@
105
                    pw_log_error("input port %s not found", v);
106
                    goto error;
107
                } else {
108
+                   bool disabled = false;
109
+
110
                    desc = port->node->desc;
111
                    d = desc->desc;
112
                    if (i == 0 && port->external != SPA_ID_INVALID) {
113
@@ -1936,12 +1974,14 @@
114
                                gp->hndl = &peer->node->hndli;
115
                                gp->port = peer->p;
116
                                gp->next = true;
117
+                               disabled = true;
118
                            }
119
                            if (gp != NULL)
120
                                gp->next = false;
121
                        }
122
-                       port->node->disabled = true;
123
-                   } else {
124
+                       port->node->disabled = disabled;
125
+                   }
126
+                   if (!disabled) {
127
                        pw_log_info("input port %s%d:%s",
128
                            port->node->name, i, d->portsport->p.name);
129
                        port->external = graph->n_input;
130
pipewire-0.3.65.tar.gz/src/modules/module-filter-chain/builtin_plugin.c -> pipewire-0.3.66.tar.gz/src/modules/module-filter-chain/builtin_plugin.c Changed
49
 
1
@@ -978,6 +978,38 @@
2
    .cleanup = delay_cleanup,
3
 };
4
 
5
+static void invert_run(void * Instance, unsigned long SampleCount)
6
+{
7
+   struct builtin *impl = Instance;
8
+   float *in = impl->port1, *out = impl->port0;
9
+   unsigned long n;
10
+   for (n = 0; n < SampleCount; n++)
11
+       outn = -inn;
12
+}
13
+
14
+static struct fc_port invert_ports = {
15
+   { .index = 0,
16
+     .name = "Out",
17
+     .flags = FC_PORT_OUTPUT | FC_PORT_AUDIO,
18
+   },
19
+   { .index = 1,
20
+     .name = "In",
21
+     .flags = FC_PORT_INPUT | FC_PORT_AUDIO,
22
+   },
23
+};
24
+
25
+static const struct fc_descriptor invert_desc = {
26
+   .name = "invert",
27
+
28
+   .n_ports = 2,
29
+   .ports = invert_ports,
30
+
31
+   .instantiate = builtin_instantiate,
32
+   .connect_port = builtin_connect_port,
33
+   .run = invert_run,
34
+   .cleanup = builtin_cleanup,
35
+};
36
+
37
 static const struct fc_descriptor * builtin_descriptor(unsigned long Index)
38
 {
39
    switch(Index) {
40
@@ -1005,6 +1037,8 @@
41
        return &convolve_desc;
42
    case 11:
43
        return &delay_desc;
44
+   case 12:
45
+       return &invert_desc;
46
    }
47
    return NULL;
48
 }
49
pipewire-0.3.65.tar.gz/src/modules/module-filter-chain/plugin.h -> pipewire-0.3.66.tar.gz/src/modules/module-filter-chain/plugin.h Changed
17
 
1
@@ -83,6 +83,7 @@
2
    void (*cleanup) (void *instance);
3
 
4
    void (*connect_port) (void *instance, unsigned long port, float *data);
5
+   void (*control_changed) (void *instance);
6
 
7
    void (*activate) (void *instance);
8
    void (*deactivate) (void *instance);
9
@@ -108,5 +109,7 @@
10
        struct dsp_ops *dsp, const char *path, const char *config);
11
 struct fc_plugin *load_builtin_plugin(const struct spa_support *support, uint32_t n_support,
12
        struct dsp_ops *dsp, const char *path, const char *config);
13
+struct fc_plugin *load_sofa_plugin(const struct spa_support *support, uint32_t n_support,
14
+       struct dsp_ops *dsp, const char *path, const char *config);
15
 
16
 #endif /* PLUGIN_H */
17
pipewire-0.3.66.tar.gz/src/modules/module-filter-chain/sofa_plugin.c Added
377
 
1
@@ -0,0 +1,375 @@
2
+#include "config.h"
3
+
4
+#include <spa/utils/json.h>
5
+#include <spa/support/loop.h>
6
+
7
+#include <pipewire/log.h>
8
+#include "plugin.h"
9
+#include "convolver.h"
10
+#include "dsp-ops.h"
11
+#include "pffft.h"
12
+
13
+#ifdef HAVE_LIBMYSOFA
14
+#include <mysofa.h>
15
+
16
+#define MAX_SAMPLES    8192u
17
+#endif
18
+
19
+static struct dsp_ops *dsp_ops;
20
+static struct spa_loop *data_loop;
21
+static struct spa_loop *main_loop;
22
+
23
+struct spatializer_impl {
24
+   unsigned long rate;
25
+   float *port6;
26
+   int n_samples, blocksize, tailsize;
27
+   float *tmp2;
28
+
29
+#ifdef HAVE_LIBMYSOFA
30
+   struct MYSOFA_EASY *sofa;
31
+#endif
32
+   unsigned int interpolate:1;
33
+   struct convolver *l_conv3;
34
+   struct convolver *r_conv3;
35
+};
36
+
37
+static void * spatializer_instantiate(const struct fc_descriptor * Descriptor,
38
+       unsigned long SampleRate, int index, const char *config)
39
+{
40
+#ifdef HAVE_LIBMYSOFA
41
+   struct spatializer_impl *impl;
42
+   struct spa_json it2;
43
+   const char *val;
44
+   char key256;
45
+   char filenamePATH_MAX = "";
46
+
47
+   errno = EINVAL;
48
+   if (config == NULL)
49
+       return NULL;
50
+
51
+   spa_json_init(&it0, config, strlen(config));
52
+   if (spa_json_enter_object(&it0, &it1) <= 0)
53
+       return NULL;
54
+
55
+   impl = calloc(1, sizeof(*impl));
56
+   if (impl == NULL) {
57
+       errno = ENOMEM;
58
+       return NULL;
59
+   }
60
+
61
+   while (spa_json_get_string(&it1, key, sizeof(key)) > 0) {
62
+       if (spa_streq(key, "blocksize")) {
63
+           if (spa_json_get_int(&it1, &impl->blocksize) <= 0) {
64
+               pw_log_error("spatializer:blocksize requires a number");
65
+               errno = EINVAL;
66
+               goto error;
67
+           }
68
+       }
69
+       else if (spa_streq(key, "tailsize")) {
70
+           if (spa_json_get_int(&it1, &impl->tailsize) <= 0) {
71
+               pw_log_error("spatializer:tailsize requires a number");
72
+               errno = EINVAL;
73
+               goto error;
74
+           }
75
+       }
76
+       else if (spa_streq(key, "filename")) {
77
+           if (spa_json_get_string(&it1, filename, sizeof(filename)) <= 0) {
78
+               pw_log_error("spatializer:filename requires a string");
79
+               errno = EINVAL;
80
+               goto error;
81
+           }
82
+       }
83
+       else if (spa_json_next(&it1, &val) < 0)
84
+           break;
85
+   }
86
+   if (!filename0) {
87
+       pw_log_error("spatializer:filename was not given");
88
+       errno = EINVAL;
89
+       goto error;
90
+   }
91
+
92
+   int ret = MYSOFA_OK;
93
+
94
+   impl->sofa = mysofa_open_cached(filename, SampleRate, &impl->n_samples, &ret);
95
+
96
+   if (ret != MYSOFA_OK) {
97
+       pw_log_error("Unable to load HRTF from %s: %d", filename, ret);
98
+       errno = ENOENT;
99
+       goto error;
100
+   }
101
+
102
+   if (impl->blocksize <= 0)
103
+       impl->blocksize = SPA_CLAMP(impl->n_samples, 64, 256);
104
+   if (impl->tailsize <= 0)
105
+       impl->tailsize = SPA_CLAMP(4096, impl->blocksize, 32768);
106
+
107
+   pw_log_info("using n_samples:%u %d:%d blocksize sofa:%s", impl->n_samples,
108
+       impl->blocksize, impl->tailsize, filename);
109
+
110
+   impl->tmp0 = calloc(MAX_SAMPLES, sizeof(float));
111
+   impl->tmp1 = calloc(MAX_SAMPLES, sizeof(float));
112
+   impl->rate = SampleRate;
113
+   return impl;
114
+error:
115
+   if (impl->sofa)
116
+       mysofa_close_cached(impl->sofa);
117
+   free(impl);
118
+   return NULL;
119
+#else
120
+   pw_log_error("libmysofa is required for spatializer, but disabled at compile time");
121
+   errno = EINVAL;
122
+   return NULL;
123
+#endif
124
+}
125
+
126
+#ifdef HAVE_LIBMYSOFA
127
+static int
128
+do_switch(struct spa_loop *loop, bool async, uint32_t seq, const void *data,
129
+       size_t size, void *user_data)
130
+{
131
+   struct spatializer_impl *impl = user_data;
132
+
133
+   if (impl->l_conv0 == NULL) {
134
+       SPA_SWAP(impl->l_conv0, impl->l_conv2);
135
+       SPA_SWAP(impl->r_conv0, impl->r_conv2);
136
+   } else {
137
+       SPA_SWAP(impl->l_conv1, impl->l_conv2);
138
+       SPA_SWAP(impl->r_conv1, impl->r_conv2);
139
+   }
140
+   impl->interpolate = impl->l_conv0 && impl->l_conv1;
141
+
142
+   return 0;
143
+}
144
+
145
+static void spatializer_reload(void * Instance)
146
+{
147
+   struct spatializer_impl *impl = Instance;
148
+   float *left_ir = calloc(impl->n_samples, sizeof(float));
149
+   float *right_ir = calloc(impl->n_samples, sizeof(float));
150
+   float left_delay;
151
+   float right_delay;
152
+   float coords3;
153
+
154
+   for (uint8_t i = 0; i < 3; i++)
155
+       coordsi = impl->port3 + i0;
156
+
157
+   mysofa_s2c(coords);
158
+   mysofa_getfilter_float(
159
+       impl->sofa,
160
+       coords0,
161
+       coords1,
162
+       coords2,
163
+       left_ir,
164
+       right_ir,
165
+       &left_delay,
166
+       &right_delay
167
+   );
168
+
169
+   // TODO: make use of delay
170
+   if ((left_delay || right_delay) && (!isnan(left_delay) || !isnan(right_delay))) {
171
+       pw_log_warn("delay dropped l: %f, r: %f", left_delay, right_delay);
172
+   }
173
+
174
+   if (impl->l_conv2)
175
+       convolver_free(impl->l_conv2);
176
+   if (impl->r_conv2)
177
+       convolver_free(impl->r_conv2);
178
+
179
+   impl->l_conv2 = convolver_new(dsp_ops, impl->blocksize, impl->tailsize,
180
+           left_ir, impl->n_samples);
181
+   impl->r_conv2 = convolver_new(dsp_ops, impl->blocksize, impl->tailsize,
182
+           right_ir, impl->n_samples);
183
+
184
+   free(left_ir);
185
+   free(right_ir);
186
+
187
+   if (impl->l_conv2 == NULL || impl->r_conv2 == NULL) {
188
+       pw_log_error("reloading left or right convolver failed");
189
+       return;
190
+   }
191
+   spa_loop_invoke(data_loop, do_switch, 1, NULL, 0, true, impl);
192
+}
193
+
194
+struct free_data {
195
+   void *item2;
196
+};
197
+
198
+static int
199
+do_free(struct spa_loop *loop, bool async, uint32_t seq, const void *data,
200
+       size_t size, void *user_data)
201
+{
202
+   const struct free_data *fd = data;
203
+   if (fd->item0)
204
+       convolver_free(fd->item0);
205
+   if (fd->item1)
206
+       convolver_free(fd->item1);
207
+   return 0;
208
+}
209
+#endif
210
+
211
+static void spatializer_run(void * Instance, unsigned long SampleCount)
212
+{
213
+#ifdef HAVE_LIBMYSOFA
214
+   struct spatializer_impl *impl = Instance;
215
+
216
+   if (impl->interpolate) {
217
+       uint32_t len = SPA_MIN(SampleCount, MAX_SAMPLES);
218
+       struct free_data free_data;
219
+       float *l = impl->tmp0, *r = impl->tmp1;
220
+
221
+       convolver_run(impl->l_conv0, impl->port2, impl->port0, len);
222
+       convolver_run(impl->l_conv1, impl->port2, l, len);
223
+       convolver_run(impl->r_conv0, impl->port2, impl->port1, len);
224
+       convolver_run(impl->r_conv1, impl->port2, r, len);
225
+
226
+       for (uint32_t i = 0; i < SampleCount; i++) {
227
+           float t = (float)i / SampleCount;
228
+           impl->port0i = impl->port0i * (1.0f - t) + li * t;
229
+           impl->port1i = impl->port1i * (1.0f - t) + ri * t;
230
+       }
231
+       free_data.item0 = impl->l_conv0;
232
+       free_data.item1 = impl->r_conv0;
233
+       impl->l_conv0 = impl->l_conv1;
234
+       impl->r_conv0 = impl->r_conv1;
235
+       impl->l_conv1 = impl->r_conv1 = NULL;
236
+       impl->interpolate = false;
237
+
238
+       spa_loop_invoke(main_loop, do_free, 1, &free_data, sizeof(free_data), false, impl);
239
+   } else if (impl->l_conv0 && impl->r_conv0) {
240
+       convolver_run(impl->l_conv0, impl->port2, impl->port0, SampleCount);
241
+       convolver_run(impl->r_conv0, impl->port2, impl->port1, SampleCount);
242
+   }
243
+#endif
244
+}
245
+
246
+static void spatializer_connect_port(void * Instance, unsigned long Port,
247
+                        float * DataLocation)
248
+{
249
+   struct spatializer_impl *impl = Instance;
250
+   if (Port > 5)
251
+       return;
252
+   impl->portPort = DataLocation;
253
+}
254
+
255
+static void spatializer_cleanup(void * Instance)
256
+{
257
+   struct spatializer_impl *impl = Instance;
258
+
259
+   for (uint8_t i = 0; i < 3; i++) {
260
+       if (impl->l_convi)
261
+           convolver_free(impl->l_convi);
262
+       if (impl->r_convi)
263
+           convolver_free(impl->r_convi);
264
+   }
265
+#ifdef HAVE_LIBMYSOFA
266
+   if (impl->sofa)
267
+       mysofa_close_cached(impl->sofa);
268
+#endif
269
+   free(impl->tmp0);
270
+   free(impl->tmp1);
271
+
272
+   free(impl);
273
+}
274
+
275
+static void spatializer_control_changed(void * Instance)
276
+{
277
+#ifdef HAVE_LIBMYSOFA
278
+   pw_log_info("control changed");
279
+   spatializer_reload(Instance);
280
+#endif
281
+}
282
+
283
+static void spatializer_deactivate(void * Instance)
284
+{
285
+   struct spatializer_impl *impl = Instance;
286
+   if (impl->l_conv0)
287
+       convolver_reset(impl->l_conv0);
288
+   if (impl->r_conv0)
289
+       convolver_reset(impl->r_conv0);
290
+   impl->interpolate = false;
291
+}
292
+
293
+static struct fc_port spatializer_ports = {
294
+   { .index = 0,
295
+     .name = "Out L",
296
+     .flags = FC_PORT_OUTPUT | FC_PORT_AUDIO,
297
+   },
298
+   { .index = 1,
299
+     .name = "Out R",
300
+     .flags = FC_PORT_OUTPUT | FC_PORT_AUDIO,
301
+   },
302
+   { .index = 2,
303
+     .name = "In",
304
+     .flags = FC_PORT_INPUT | FC_PORT_AUDIO,
305
+   },
306
+
307
+   { .index = 3,
308
+     .name = "Azimuth",
309
+     .flags = FC_PORT_INPUT | FC_PORT_CONTROL,
310
+     .def = 0.0f, .min = 0.0f, .max = 360.0f
311
+   },
312
+   { .index = 4,
313
+     .name = "Elevation",
314
+     .flags = FC_PORT_INPUT | FC_PORT_CONTROL,
315
+     .def = 0.0f, .min = -90.0f, .max = 90.0f
316
+   },
317
+   { .index = 5,
318
+     .name = "Radius",
319
+     .flags = FC_PORT_INPUT | FC_PORT_CONTROL,
320
+     .def = 1.0f, .min = 0.0f, .max = 100.0f
321
+   },
322
+};
323
+
324
+static const struct fc_descriptor spatializer_desc = {
325
+   .name = "spatializer",
326
+
327
+   .n_ports = 6,
328
+   .ports = spatializer_ports,
329
+
330
+   .instantiate = spatializer_instantiate,
331
+   .connect_port = spatializer_connect_port,
332
+   .control_changed = spatializer_control_changed,
333
+   .deactivate = spatializer_deactivate,
334
+   .run = spatializer_run,
335
+   .cleanup = spatializer_cleanup,
336
+};
337
+
338
+static const struct fc_descriptor * sofa_descriptor(unsigned long Index)
339
+{
340
+   switch(Index) {
341
+   case 0:
342
+       return &spatializer_desc;
343
+   }
344
+   return NULL;
345
+}
346
+
347
+
348
+static const struct fc_descriptor *sofa_make_desc(struct fc_plugin *plugin, const char *name)
349
+{
350
+   unsigned long i;
351
+   for (i = 0; ;i++) {
352
+       const struct fc_descriptor *d = sofa_descriptor(i);
353
+       if (d == NULL)
354
+           break;
355
+       if (spa_streq(d->name, name))
356
+           return d;
357
+   }
358
+   return NULL;
359
+}
360
+
361
+static struct fc_plugin builtin_plugin = {
362
+   .make_desc = sofa_make_desc
363
+};
364
+
365
+struct fc_plugin *load_sofa_plugin(const struct spa_support *support, uint32_t n_support,
366
+       struct dsp_ops *dsp, const char *plugin, const char *config)
367
+{
368
+   dsp_ops = dsp;
369
+   pffft_select_cpu(dsp->cpu_flags);
370
+
371
+   data_loop = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_DataLoop);
372
+   main_loop = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_Loop);
373
+
374
+   return &builtin_plugin;
375
+}
376
+
377
pipewire-0.3.65.tar.gz/src/modules/module-loopback.c -> pipewire-0.3.66.tar.gz/src/modules/module-loopback.c Changed
34
 
1
@@ -111,6 +111,32 @@
2
  * 
3
  *\endcode
4
  *
5
+ * ## Example configuration of a virtual source
6
+ *
7
+ * This Virtual source routes the front-left channel of a multi-channel input to a mono channel.
8
+ * This is useful for splitting up multi-channel inputs from USB audio interfaces that are not yet fully supported by alsa.
9
+ *
10
+ *\code{.unparsed}
11
+ * context.modules = 
12
+ * {   name = libpipewire-module-loopback
13
+ *     args = {
14
+ *       node.description = "Scarlett Focusrite Line 1"
15
+ *       capture.props = {
16
+ *           audio.position =  FL 
17
+ *           stream.dont-remix = true
18
+ *           node.target = "alsa_input.usb-Focusrite_Scarlett_Solo_USB_Y7ZD17C24495BC-00.analog-stereo"
19
+ *           node.passive = true
20
+ *       }
21
+ *       playback.props = {
22
+ *           node.name = "SF_mono_in_1"
23
+ *           media.class = "Audio/Source"
24
+ *           audio.position =  MONO 
25
+ *       }
26
+ *     }
27
+ * }
28
+ * 
29
+ *\endcode
30
+ *
31
  * ## See also
32
  *
33
  * `pw-loopback` : a tool that loads the loopback module with given parameters.
34
pipewire-0.3.65.tar.gz/src/modules/module-protocol-pulse/cmd.c -> pipewire-0.3.66.tar.gz/src/modules/module-protocol-pulse/cmd.c Changed
13
 
1
@@ -76,7 +76,10 @@
2
 
3
 /*
4
  * pulse.cmd = 
5
- *   { cmd = <command>  args = "<arguments>"  }
6
+ *   {   cmd = <command>
7
+ *       ( args = "<arguments>" )
8
+ *       ( flags =  ( nofail )  )
9
+ *   }
10
  *   ...
11
  * 
12
  */
13
pipewire-0.3.66.tar.gz/src/modules/module-rt Added
2
 
1
+(directory)
2
pipewire-0.3.66.tar.gz/src/modules/module-rt/20-pw-defaults.conf.in Added
22
 
1
@@ -0,0 +1,20 @@
2
+# This file was installed by PipeWire project for buffer locking to always work
3
+
4
+# Required to memlock audio buffers for all client types
5
+#
6
+# This will match all PAM users i.e. those going through the login procedure but
7
+# it should not get applied to system daemons, since they are run bypassing PAM.
8
+#
9
+# While at first glance this might appear very relevant, in fact abusing this
10
+# can at most allow for either more rapid OOM or enhance malicious system memory
11
+# thrashing while evading systemd-oomd limits that are based on the requirement
12
+# that swap utilization must be high before issues arise. As such it's perfectly
13
+# reasonable to just set a limit where each client can lock a few megabytes with
14
+# nearly no impact on regular systems. Meanwhile malicious attackers can OOM
15
+# just as they could. And instead tooling for OOM and resource abuse should be
16
+# improved, if such denial of service attacks are a serious consideration at all.
17
+#
18
+# Starting with Linux 5.16 or systemd v253 the default is 8192 which is plenty
19
+# good enough and this file should not be installed on such systems.
20
+#
21
+*         - memlock @PAM_MEMLOCK@
22
pipewire-0.3.66.tar.gz/src/modules/module-rt/25-pw-rlimits.conf.in Added
10
 
1
@@ -0,0 +1,8 @@
2
+# This file was installed by PipeWire project for its libpipewire-module-rt.so
3
+
4
+# It's believed to be acceptable to have match rules that will never be true
5
+# i.e. a group that does not exist.
6
+#
7
+@MATCH@   - rtprio  @RTPRIO@
8
+@MATCH@   - nice    @NICE@
9
+@MATCH@   - memlock @MEMLOCK@
10
pipewire-0.3.66.tar.gz/src/modules/module-rt/meson.build Added
25
 
1
@@ -0,0 +1,23 @@
2
+rlimits_install = get_option('rlimits-install')
3
+rlimits_data = configuration_data()
4
+rlimits_data.set('MATCH', get_option('rlimits-match'))
5
+rlimits_data.set('RTPRIO', get_option('rlimits-rtprio'))
6
+rlimits_data.set('NICE', get_option('rlimits-nice'))
7
+rlimits_data.set('MEMLOCK', get_option('rlimits-memlock'))
8
+configure_file(input: '25-pw-rlimits.conf.in',
9
+  output: '25-pw-rlimits.conf',
10
+  install: rlimits_install,
11
+  install_dir: get_option('sysconfdir') / 'security' / 'limits.d',
12
+  configuration: rlimits_data)
13
+summary({'RLIMITs': '@0@ limits.d file affecting matching PAM users'.format(rlimits_install ? 'with' : 'without')})
14
+
15
+# The pam-defaults-install related code can be removed once all Linux <5.16 kernels are EOL (projected Dec, 2026)
16
+pam_defaults_install = get_option('pam-defaults-install')
17
+pam_defaults_data = configuration_data()
18
+pam_defaults_data.set('PAM_MEMLOCK', get_option('pam-memlock-default'))
19
+configure_file(input: '20-pw-defaults.conf.in',
20
+  output: '20-pw-defaults.conf',
21
+  install: pam_defaults_install,
22
+  install_dir: get_option('sysconfdir') / 'security' / 'limits.d',
23
+  configuration: pam_defaults_data)
24
+summary({'PAM defaults': '@0@ limits.d file affecting all PAM users (not needed with modern systemd or kernel)'.format(pam_defaults_install ? 'with' : 'without')})
25
pipewire-0.3.65.tar.gz/src/modules/module-rtp-sink.c -> pipewire-0.3.66.tar.gz/src/modules/module-rtp-sink.c Changed
351
 
1
@@ -70,6 +70,8 @@
2
  * - `sess.min-ptime = <int>`: minimum packet time in milliseconds, default 2
3
  * - `sess.max-ptime = <int>`: maximum packet time in milliseconds, default 20
4
  * - `sess.name = <str>`: a session name
5
+ * - `sess.ts-offset = <int>`: an offset to apply to the timestamp, default -1 = random offset
6
+ * - `sess.ts-refclk = <string>`: the name of a reference clock
7
  * - `stream.props = {}`: properties to be passed to the stream
8
  *
9
  * ## General options
10
@@ -149,6 +151,7 @@
11
 
12
 #define DEFAULT_MIN_PTIME  2
13
 #define DEFAULT_MAX_PTIME  20
14
+#define DEFAULT_TS_OFFSET  -1
15
 
16
 #define USAGE  "sap.ip=<SAP IP address to send announce, default:"DEFAULT_SAP_IP"> "       \
17
        "sap.port=<SAP port to send on, default:"SPA_STRINGIFY(DEFAULT_SAP_PORT)"> "    \
18
@@ -212,6 +215,8 @@
19
    struct pw_stream *stream;
20
    struct spa_hook stream_listener;
21
 
22
+   struct spa_io_position *io_position;
23
+
24
    unsigned int do_disconnect:1;
25
 
26
    char *ifname;
27
@@ -220,9 +225,9 @@
28
    int mtu;
29
    bool ttl;
30
    bool mcast_loop;
31
-   uint32_t min_ptime;
32
-   uint32_t max_ptime;
33
-   uint32_t pbytes;
34
+   float min_ptime;
35
+   float max_ptime;
36
+   uint32_t psamples;
37
 
38
    struct sockaddr_storage src_addr;
39
    socklen_t src_len;
40
@@ -240,17 +245,20 @@
41
 
42
    struct spa_audio_info_raw info;
43
    const struct format_info *format_info;
44
-   uint32_t frame_size;
45
+   uint32_t stride;
46
    int payload;
47
    uint16_t seq;
48
-   uint32_t timestamp;
49
    uint32_t ssrc;
50
+   uint32_t ts_offset;
51
+   char ts_refclk64;
52
 
53
    struct spa_ringbuffer ring;
54
    uint8_t bufferBUFFER_SIZE;
55
 
56
    int rtp_fd;
57
    int sap_fd;
58
+
59
+   unsigned sync:1;
60
 };
61
 
62
 
63
@@ -274,20 +282,21 @@
64
 static void flush_packets(struct impl *impl)
65
 {
66
    int32_t avail;
67
-   uint32_t index;
68
+   uint32_t stride, timestamp;
69
    struct iovec iov3;
70
    struct msghdr msg;
71
    ssize_t n;
72
    struct rtp_header header;
73
    int32_t tosend;
74
 
75
-   avail = spa_ringbuffer_get_read_index(&impl->ring, &index);
76
-
77
-   tosend = impl->pbytes;
78
+   avail = spa_ringbuffer_get_read_index(&impl->ring, &timestamp);
79
+   tosend = impl->psamples;
80
 
81
    if (avail < tosend)
82
        return;
83
 
84
+   stride = impl->stride;
85
+
86
    spa_zero(header);
87
    header.v = 2;
88
    header.pt = impl->payload;
89
@@ -306,13 +315,14 @@
90
 
91
    while (avail >= tosend) {
92
        header.sequence_number = htons(impl->seq);
93
-       header.timestamp = htonl(impl->timestamp);
94
+       header.timestamp = htonl(impl->ts_offset + timestamp);
95
 
96
        set_iovec(&impl->ring,
97
            impl->buffer, BUFFER_SIZE,
98
-           index & BUFFER_MASK,
99
-           &iov1, tosend);
100
+           (timestamp * stride) & BUFFER_MASK,
101
+           &iov1, tosend * stride);
102
 
103
+       pw_log_trace("sending %d timestamp:%d", tosend, timestamp);
104
        n = sendmsg(impl->rtp_fd, &msg, MSG_NOSIGNAL);
105
        if (n < 0) {
106
            switch (errno) {
107
@@ -321,18 +331,18 @@
108
                pw_log_debug("remote end not listening");
109
                break;
110
            default:
111
-               pw_log_warn("sendmsg() failed: %m");
112
+               pw_log_warn("sendmsg() failed, seq:%u dropped: %m",
113
+                       impl->seq);
114
                break;
115
            }
116
        }
117
 
118
        impl->seq++;
119
-       impl->timestamp += tosend / impl->frame_size;
120
 
121
-       index += tosend;
122
+       timestamp += tosend;
123
        avail -= tosend;
124
    }
125
-   spa_ringbuffer_read_update(&impl->ring, index);
126
+   spa_ringbuffer_read_update(&impl->ring, timestamp);
127
 }
128
 
129
 static void stream_process(void *data)
130
@@ -340,8 +350,8 @@
131
    struct impl *impl = data;
132
    struct pw_buffer *buf;
133
    struct spa_data *d;
134
-   uint32_t index;
135
-        int32_t filled, wanted;
136
+   uint32_t offs, size, timestamp, expected_timestamp, stride;
137
+   int32_t filled, wanted;
138
 
139
    if ((buf = pw_stream_dequeue_buffer(impl->stream)) == NULL) {
140
        pw_log_debug("Out of stream buffers: %m");
141
@@ -349,27 +359,56 @@
142
    }
143
    d = buf->buffer->datas;
144
 
145
-   wanted = d0.chunk->size;
146
+   offs = SPA_MIN(d0.chunk->offset, d0.maxsize);
147
+   size = SPA_MIN(d0.chunk->size, d0.maxsize - offs);
148
+   stride = impl->stride;
149
+   wanted = size / stride;
150
+
151
+   filled = spa_ringbuffer_get_write_index(&impl->ring, &expected_timestamp);
152
+   if (SPA_LIKELY(impl->io_position))
153
+       timestamp = impl->io_position->clock.position;
154
+   else
155
+       timestamp = expected_timestamp;
156
+
157
+   if (impl->sync) {
158
+       if (expected_timestamp != timestamp) {
159
+           pw_log_warn("expected %u != timestamp %u", expected_timestamp, timestamp);
160
+           impl->sync = false;
161
+       } else if (filled + wanted > (int32_t)(BUFFER_SIZE / stride)) {
162
+           pw_log_warn("overrun %u + %u > %u", filled, wanted, BUFFER_SIZE / stride);
163
+           impl->sync = false;
164
+       }
165
+   }
166
+   if (!impl->sync) {
167
+       pw_log_info("sync to timestamp %u", timestamp);
168
+       impl->ring.readindex = impl->ring.writeindex = timestamp;
169
+       memset(impl->buffer, 0, BUFFER_SIZE);
170
+       impl->sync = true;
171
+   }
172
 
173
-   filled = spa_ringbuffer_get_write_index(&impl->ring, &index);
174
+   spa_ringbuffer_write_data(&impl->ring,
175
+           impl->buffer,
176
+           BUFFER_SIZE,
177
+           (timestamp * stride) & BUFFER_MASK,
178
+           SPA_PTROFF(d0.data, offs, void), wanted * stride);
179
+   timestamp += wanted;
180
+   spa_ringbuffer_write_update(&impl->ring, timestamp);
181
 
182
-   if (filled + wanted > (int32_t)BUFFER_SIZE) {
183
-       pw_log_warn("overrun %u + %u > %u", filled, wanted, BUFFER_SIZE);
184
-   } else {
185
-       spa_ringbuffer_write_data(&impl->ring,
186
-               impl->buffer,
187
-               BUFFER_SIZE,
188
-                                index & BUFFER_MASK,
189
-                                d0.data, wanted);
190
-
191
-                index += wanted;
192
-                spa_ringbuffer_write_update(&impl->ring, index);
193
-        }
194
    pw_stream_queue_buffer(impl->stream, buf);
195
 
196
    flush_packets(impl);
197
 }
198
 
199
+static void stream_io_changed(void *data, uint32_t id, void *area, uint32_t size)
200
+{
201
+   struct impl *impl = data;
202
+   switch (id) {
203
+   case SPA_IO_Position:
204
+       impl->io_position = area;
205
+       break;
206
+   }
207
+}
208
+
209
 static void on_stream_state_changed(void *d, enum pw_stream_state old,
210
        enum pw_stream_state state, const char *error)
211
 {
212
@@ -383,6 +422,9 @@
213
    case PW_STREAM_STATE_ERROR:
214
        pw_log_error("stream error: %s", error);
215
        break;
216
+   case PW_STREAM_STATE_PAUSED:
217
+       impl->sync = false;
218
+       break;
219
    default:
220
        break;
221
    }
222
@@ -391,6 +433,7 @@
223
 static const struct pw_stream_events in_stream_events = {
224
    PW_VERSION_STREAM_EVENTS,
225
    .destroy = stream_destroy,
226
+   .io_changed = stream_io_changed,
227
    .state_changed = on_stream_state_changed,
228
    .process = stream_process
229
 };
230
@@ -463,6 +506,7 @@
231
    if (setsockopt(fd, SOL_SOCKET, SO_PRIORITY, &val, sizeof(val)) < 0)
232
        pw_log_warn("setsockopt(SO_PRIORITY) failed: %m");
233
 #endif
234
+   /* FIXME AES67 wants IPTOS_DSCP_AF41 */
235
    val = IPTOS_LOWDELAY;
236
    if (setsockopt(fd, IPPROTO_IP, IP_TOS, &val, sizeof(val)) < 0)
237
        pw_log_warn("setsockopt(IP_TOS) failed: %m");
238
@@ -489,8 +533,7 @@
239
 
240
    if (pw_properties_get(props, PW_KEY_NODE_LATENCY) == NULL) {
241
        pw_properties_setf(props, PW_KEY_NODE_LATENCY,
242
-               "%d/%d", impl->pbytes / impl->frame_size,
243
-               impl->info.rate);
244
+               "%d/%d", impl->psamples, impl->info.rate);
245
    }
246
    pw_properties_setf(props, PW_KEY_NODE_RATE, "1/%d", impl->info.rate);
247
 
248
@@ -548,6 +591,7 @@
249
    struct sap_header header;
250
    struct iovec iov4;
251
    struct msghdr msg;
252
+   struct spa_strbuf buf;
253
 
254
    spa_zero(header);
255
    header.v = 1;
256
@@ -580,7 +624,8 @@
257
    if (is_multicast((struct sockaddr*)&impl->dst_addr, impl->dst_len))
258
        snprintf(dst_ttl, sizeof(dst_ttl), "/%d", impl->ttl);
259
 
260
-   snprintf(buffer, sizeof(buffer),
261
+   spa_strbuf_init(&buf, buffer, sizeof(buffer));
262
+   spa_strbuf_append(&buf,
263
            "v=0\n"
264
            "o=%s %u 0 IN %s %s\n"
265
            "s=%s\n"
266
@@ -600,7 +645,17 @@
267
            impl->port, impl->payload,
268
            impl->payload, impl->format_info->mime,
269
            impl->info.rate, impl->info.channels,
270
-           (impl->pbytes / impl->frame_size) * 1000 / impl->info.rate);
271
+           impl->psamples * 1000 / impl->info.rate);
272
+
273
+   if (impl->ts_refclk0 != '\0') {
274
+       spa_strbuf_append(&buf,
275
+               "a=ts-refclk:%s\n"
276
+               "a=mediaclk:direct=%u\n",
277
+               impl->ts_refclk,
278
+               impl->ts_offset);
279
+   } else {
280
+       spa_strbuf_append(&buf, "a=mediaclk:sender\n");
281
+   }
282
 
283
    iov3.iov_base = buffer;
284
    iov3.iov_len = strlen(buffer);
285
@@ -793,7 +848,8 @@
286
    struct impl *impl;
287
    struct pw_properties *props = NULL, *stream_props = NULL;
288
    uint32_t id = pw_global_get_id(pw_impl_module_get_global(module));
289
-   uint32_t pid = getpid(), port, min_bytes, max_bytes;
290
+   uint32_t pid = getpid(), port, min_samples, max_samples;
291
+   int64_t ts_offset;
292
    char addr64;
293
    const char *str;
294
    int res = 0;
295
@@ -867,13 +923,12 @@
296
        res = -EINVAL;
297
        goto out;
298
    }
299
-   impl->frame_size = impl->format_info->size * impl->info.channels;
300
+   impl->stride = impl->format_info->size * impl->info.channels;
301
    impl->msg_id_hash = rand();
302
    impl->ntp = (uint32_t) time(NULL) + 2208988800U;
303
 
304
    impl->payload = 127;
305
    impl->seq = rand();
306
-   impl->timestamp = rand();
307
    impl->ssrc = rand();
308
 
309
    str = pw_properties_get(props, "local.ifname");
310
@@ -907,14 +962,25 @@
311
    impl->ttl = pw_properties_get_uint32(props, "net.ttl", DEFAULT_TTL);
312
    impl->mcast_loop = pw_properties_get_bool(props, "net.loop", DEFAULT_LOOP);
313
 
314
-   impl->min_ptime = pw_properties_get_uint32(props, "sess.min-ptime", DEFAULT_MIN_PTIME);
315
-   impl->max_ptime = pw_properties_get_uint32(props, "sess.max-ptime", DEFAULT_MAX_PTIME);
316
+   ts_offset = pw_properties_get_int64(props, "sess.ts-offset", DEFAULT_TS_OFFSET);
317
+   impl->ts_offset = ts_offset < 0 ? rand() : ts_offset;
318
+
319
+   str = pw_properties_get(props, "sess.ts-refclk");
320
+   if (str != NULL)
321
+       snprintf(impl->ts_refclk, sizeof(impl->ts_refclk), "%s", str);
322
+
323
+   str = pw_properties_get(props, "sess.min-ptime");
324
+   if (!spa_atof(str, &impl->min_ptime))
325
+       impl->min_ptime = DEFAULT_MIN_PTIME;
326
+   str = pw_properties_get(props, "sess.max-ptime");
327
+   if (!spa_atof(str, &impl->max_ptime))
328
+       impl->max_ptime = DEFAULT_MAX_PTIME;
329
 
330
-   min_bytes = (impl->min_ptime * impl->info.rate / 1000) * impl->frame_size;
331
-   max_bytes = (impl->max_ptime * impl->info.rate / 1000) * impl->frame_size;
332
+   min_samples = impl->min_ptime * impl->info.rate / 1000;
333
+   max_samples = impl->max_ptime * impl->info.rate / 1000;
334
 
335
-   impl->pbytes = SPA_ROUND_DOWN(impl->mtu, impl->frame_size);
336
-   impl->pbytes = SPA_CLAMP(impl->pbytes, min_bytes, max_bytes);
337
+   impl->psamples = impl->mtu / impl->stride;
338
+   impl->psamples = SPA_CLAMP(impl->psamples, min_samples, max_samples);
339
 
340
    if ((str = pw_properties_get(props, "sess.name")) == NULL)
341
        pw_properties_setf(props, "sess.name", "PipeWire RTP Stream on %s",
342
@@ -931,7 +997,7 @@
343
    pw_properties_setf(stream_props, "rtp.mtu", "%u", impl->mtu);
344
    pw_properties_setf(stream_props, "rtp.ttl", "%u", impl->ttl);
345
    pw_properties_setf(stream_props, "rtp.ptime", "%u",
346
-           (impl->pbytes / impl->frame_size) * 1000 / impl->info.rate);
347
+           impl->psamples * 1000 / impl->info.rate);
348
 
349
    impl->core = pw_context_get_object(impl->module_context, PW_TYPE_INTERFACE_Core);
350
    if (impl->core == NULL) {
351
pipewire-0.3.65.tar.gz/src/modules/module-rtp-source.c -> pipewire-0.3.66.tar.gz/src/modules/module-rtp-source.c Changed
643
 
1
@@ -25,6 +25,7 @@
2
 #include "config.h"
3
 
4
 #include <limits.h>
5
+#include <string.h>
6
 #include <unistd.h>
7
 #include <sys/stat.h>
8
 #include <sys/socket.h>
9
@@ -84,6 +85,7 @@
10
  *         #sap.port = 9875
11
  *         #local.ifname = eth0
12
  *         sess.latency.msec = 100
13
+ *         #node.always-process = false # true to receive even when not running
14
  *         stream.props = {
15
  *            #media.class = "Audio/Source"
16
  *            #node.name = "rtp-source"
17
@@ -97,11 +99,14 @@
18
  *                         #rtp.payload = "127"
19
  *                         #rtp.fmt = "L16/48000/2"
20
  *                         #rtp.session = "PipeWire RTP Stream on fedora"
21
+ *                         #rtp.ts-offset = 0
22
+ *                         #rtp.ts-refclk = "private"
23
  *                     }
24
  *                 
25
  *                 actions = {
26
  *                     create-stream = {
27
  *                         #sess.latency.msec = 100
28
+ *                         #sess.ts-direct = false
29
  *                         #target.object = ""
30
  *                     }
31
  *                 }
32
@@ -169,6 +174,7 @@
33
 
34
    char *ifname;
35
    char *sap_ip;
36
+   bool always_process;
37
    int sap_port;
38
    int sess_latency_msec;
39
    uint32_t cleanup_interval;
40
@@ -202,6 +208,7 @@
41
 
42
    char origin128;
43
    char session256;
44
+   char channelmap512;
45
 
46
    struct sockaddr_storage sa;
47
    socklen_t salen;
48
@@ -212,6 +219,9 @@
49
    const struct format_info *format_info;
50
    struct spa_audio_info_raw info;
51
    uint32_t stride;
52
+
53
+   uint32_t ts_offset;
54
+   char refclk64;
55
 };
56
 
57
 struct session {
58
@@ -237,12 +247,13 @@
59
    uint8_t bufferBUFFER_SIZE;
60
 
61
    struct spa_io_rate_match *rate_match;
62
+   struct spa_io_position *position;
63
    struct spa_dll dll;
64
    uint32_t target_buffer;
65
-   uint32_t last_packet_size;
66
    float max_error;
67
-   unsigned buffering:1;
68
    unsigned first:1;
69
+   unsigned receiving:1;
70
+   unsigned direct_timestamp:1;
71
 };
72
 
73
 static void stream_destroy(void *d)
74
@@ -257,8 +268,8 @@
75
    struct session *sess = data;
76
    struct pw_buffer *buf;
77
    struct spa_data *d;
78
-   uint32_t index, target_buffer;
79
-   int32_t avail, wanted;
80
+   uint32_t wanted, timestamp, target_buffer, stride, maxsize;
81
+   int32_t avail;
82
 
83
    if ((buf = pw_stream_dequeue_buffer(sess->stream)) == NULL) {
84
        pw_log_debug("Out of stream buffers: %m");
85
@@ -266,38 +277,53 @@
86
    }
87
    d = buf->buffer->datas;
88
 
89
-   wanted = buf->requested ?
90
-       SPA_MIN(buf->requested * sess->info.stride, d0.maxsize)
91
-       : d0.maxsize;
92
+   stride = sess->info.stride;
93
 
94
-   avail = spa_ringbuffer_get_read_index(&sess->ring, &index);
95
+   maxsize = d0.maxsize / stride;
96
+   wanted = buf->requested ? SPA_MIN(buf->requested, maxsize) : maxsize;
97
 
98
-   target_buffer = sess->target_buffer + sess->last_packet_size / 2;
99
+   if (sess->position && sess->direct_timestamp) {
100
+       /* in direct mode, read directly from the timestamp index,
101
+        * because sender and receiver are in sync, this would keep
102
+        * target_buffer of bytes available. */
103
+       spa_ringbuffer_read_update(&sess->ring,
104
+               sess->position->clock.position);
105
+   }
106
+   avail = spa_ringbuffer_get_read_index(&sess->ring, &timestamp);
107
 
108
-   if (avail < wanted || sess->buffering) {
109
-       memset(d0.data, 0, wanted);
110
-       if (!sess->buffering && sess->have_sync) {
111
-           pw_log_debug("underrun %u/%u < %u, buffering...",
112
-                   avail, target_buffer, wanted);
113
-           sess->buffering = true;
114
+   target_buffer = sess->target_buffer;
115
+
116
+   if (avail < (int32_t)wanted) {
117
+       enum spa_log_level level;
118
+       memset(d0.data, 0, wanted * stride);
119
+       if (sess->have_sync) {
120
+           sess->have_sync = false;
121
+           level = SPA_LOG_LEVEL_WARN;
122
+       } else {
123
+           level = SPA_LOG_LEVEL_DEBUG;
124
        }
125
+       pw_log(level, "underrun %d/%u < %u",
126
+                   avail, target_buffer, wanted);
127
    } else {
128
        float error, corr;
129
-       if (avail > (int32_t)SPA_MIN(target_buffer * 8, BUFFER_SIZE)) {
130
+       if (avail > (int32_t)SPA_MIN(target_buffer * 8, BUFFER_SIZE / stride)) {
131
            pw_log_warn("overrun %u > %u", avail, target_buffer * 8);
132
-           index += avail - target_buffer;
133
+           timestamp += avail - target_buffer;
134
            avail = target_buffer;
135
-       } else {
136
-           if (sess->first) {
137
-               if ((uint32_t)avail > target_buffer) {
138
-                   uint32_t skip = avail - target_buffer;
139
-                   pw_log_debug("first: avail:%d skip:%u target:%u",
140
+       } else if (sess->first) {
141
+           if ((uint32_t)avail > target_buffer) {
142
+               uint32_t skip = avail - target_buffer;
143
+               pw_log_debug("first: avail:%d skip:%u target:%u",
144
                            avail, skip, target_buffer);
145
-                   index += skip;
146
-                   avail = target_buffer;
147
-               }
148
-               sess->first = false;
149
+               timestamp += skip;
150
+               avail = target_buffer;
151
            }
152
+           sess->first = false;
153
+       }
154
+       if (!sess->direct_timestamp) {
155
+           /* when not using direct timestamp and clocks are not
156
+            * in sync, try to adjust our playback rate to keep the
157
+            * requested target_buffer bytes in the ringbuffer */
158
            error = (float)target_buffer - (float)avail;
159
            error = SPA_CLAMP(error, -sess->max_error, sess->max_error);
160
 
161
@@ -307,46 +333,28 @@
162
                    target_buffer, error, corr);
163
 
164
            if (sess->rate_match) {
165
-               SPA_FLAG_SET(sess->rate_match->flags, SPA_IO_RATE_MATCH_FLAG_ACTIVE);
166
+               SPA_FLAG_SET(sess->rate_match->flags,
167
+                       SPA_IO_RATE_MATCH_FLAG_ACTIVE);
168
                sess->rate_match->rate = 1.0f / corr;
169
            }
170
        }
171
        spa_ringbuffer_read_data(&sess->ring,
172
                sess->buffer,
173
                BUFFER_SIZE,
174
-               index & BUFFER_MASK,
175
-               d0.data, wanted);
176
+               (timestamp * stride) & BUFFER_MASK,
177
+               d0.data, wanted * stride);
178
 
179
-       index += wanted;
180
-       spa_ringbuffer_read_update(&sess->ring, index);
181
+       timestamp += wanted;
182
+       spa_ringbuffer_read_update(&sess->ring, timestamp);
183
    }
184
-   d0.chunk->size = wanted;
185
-   d0.chunk->stride = sess->info.stride;
186
+   d0.chunk->size = wanted * stride;
187
+   d0.chunk->stride = stride;
188
    d0.chunk->offset = 0;
189
-   buf->size = wanted / sess->info.stride;
190
+   buf->size = wanted;
191
 
192
    pw_stream_queue_buffer(sess->stream, buf);
193
 }
194
 
195
-static void on_stream_state_changed(void *d, enum pw_stream_state old,
196
-       enum pw_stream_state state, const char *error)
197
-{
198
-   struct session *sess = d;
199
-   struct impl *impl = sess->impl;
200
-
201
-   switch (state) {
202
-   case PW_STREAM_STATE_UNCONNECTED:
203
-       pw_log_info("stream disconnected, unloading");
204
-       pw_impl_module_schedule_destroy(impl->module);
205
-       break;
206
-   case PW_STREAM_STATE_ERROR:
207
-       pw_log_error("stream error: %s", error);
208
-       break;
209
-   default:
210
-       break;
211
-   }
212
-}
213
-
214
 static void stream_io_changed(void *data, uint32_t id, void *area, uint32_t size)
215
 {
216
    struct session *sess = data;
217
@@ -354,16 +362,18 @@
218
    case SPA_IO_RateMatch:
219
        sess->rate_match = area;
220
        break;
221
+   case SPA_IO_Position:
222
+       sess->position = area;
223
+       break;
224
    }
225
 }
226
 
227
-static const struct pw_stream_events out_stream_events = {
228
-   PW_VERSION_STREAM_EVENTS,
229
-   .destroy = stream_destroy,
230
-   .state_changed = on_stream_state_changed,
231
-   .io_changed = stream_io_changed,
232
-   .process = stream_process
233
-};
234
+static void session_touch(struct session *sess)
235
+{
236
+   struct timespec ts;
237
+   clock_gettime(CLOCK_MONOTONIC, &ts);
238
+   sess->timestamp = SPA_TIMESPEC_TO_NSEC(&ts);
239
+}
240
 
241
 static void
242
 on_rtp_io(void *data, int fd, uint32_t mask)
243
@@ -374,7 +384,7 @@
244
    uint8_t buffer2048, *payload;
245
 
246
    if (mask & SPA_IO_IN) {
247
-       uint32_t index, expected_index, timestamp;
248
+       uint32_t stride, read, timestamp, expected_timestamp, samples;
249
        uint16_t seq;
250
        int32_t filled;
251
 
252
@@ -399,65 +409,54 @@
253
 
254
        seq = ntohs(hdr->sequence_number);
255
        if (sess->have_seq && sess->expected_seq != seq) {
256
-           pw_log_warn("unexpected seq (%d != %d)", seq, sess->expected_seq);
257
+           pw_log_info("unexpected seq (%d != %d)", seq, sess->expected_seq);
258
+           sess->have_sync = false;
259
        }
260
        sess->expected_seq = seq + 1;
261
        sess->have_seq = true;
262
 
263
-       len = SPA_ROUND_DOWN(len - hlen, sess->info.stride);
264
+       stride = sess->info.stride;
265
+       samples = (len - hlen) / stride;
266
        payload = &bufferhlen;
267
 
268
-       filled = spa_ringbuffer_get_write_index(&sess->ring, &index);
269
+       filled = spa_ringbuffer_get_write_index(&sess->ring, &expected_timestamp);
270
 
271
-       timestamp = ntohl(hdr->timestamp);
272
-       expected_index = timestamp * sess->info.stride;
273
+       read = ntohl(hdr->timestamp) - sess->info.ts_offset;
274
+       /* we always write to timestamp + delay */
275
+       timestamp = read + sess->target_buffer;
276
 
277
        if (!sess->have_sync) {
278
-           pw_log_trace("got rtp, no sync");
279
-           sess->ring.readindex = sess->ring.writeindex =
280
-               index = expected_index;
281
-           filled = 0;
282
-           sess->have_sync = true;
283
-           sess->buffering = true;
284
-           pw_log_debug("sync to timestamp %u", index);
285
+           pw_log_info("sync to timestamp %u", read);
286
+           /* we read from timestamp, keeping target_buffer of data
287
+            * in the ringbuffer. */
288
+           sess->ring.readindex = read;
289
+           sess->ring.writeindex = timestamp;
290
+           filled = sess->target_buffer;
291
 
292
            spa_dll_init(&sess->dll);
293
            spa_dll_set_bw(&sess->dll, SPA_DLL_BW_MIN, 128, sess->info.info.rate);
294
-
295
-       } else if (expected_index != index) {
296
-           pw_log_trace("got rtp, wrong timestamp");
297
+           memset(sess->buffer, 0, BUFFER_SIZE);
298
+           sess->have_sync = true;
299
+       } else if (expected_timestamp != timestamp) {
300
            pw_log_debug("unexpected timestamp (%u != %u)",
301
-                   index / sess->info.stride,
302
-                   expected_index / sess->info.stride);
303
-           index = expected_index;
304
-           filled = 0;
305
+                   timestamp, expected_timestamp);
306
        }
307
 
308
-       if (filled + len > BUFFER_SIZE) {
309
-           pw_log_debug("got rtp, capture overrun %u %zd", filled, len);
310
+       if (filled + samples > BUFFER_SIZE / stride) {
311
+           pw_log_debug("capture overrun %u + %u > %u", filled, samples,
312
+                   BUFFER_SIZE / stride);
313
            sess->have_sync = false;
314
        } else {
315
-           uint32_t target_buffer;
316
-
317
-           pw_log_trace("got rtp packet len:%zd", len);
318
+           pw_log_trace("got samples:%u", samples);
319
            spa_ringbuffer_write_data(&sess->ring,
320
                    sess->buffer,
321
                    BUFFER_SIZE,
322
-                   index & BUFFER_MASK,
323
-                   payload, len);
324
-           index += len;
325
-           filled += len;
326
-           spa_ringbuffer_write_update(&sess->ring, index);
327
-
328
-           sess->last_packet_size = len;
329
-           target_buffer = sess->target_buffer + len/2;
330
-
331
-           if (sess->buffering && (uint32_t)filled > target_buffer) {
332
-               sess->buffering = false;
333
-               pw_log_debug("buffering done %u > %u",
334
-                   filled, target_buffer);
335
-           }
336
+                   (timestamp * stride) & BUFFER_MASK,
337
+                   payload, (samples * stride));
338
+           timestamp += samples;
339
+           spa_ringbuffer_write_update(&sess->ring, timestamp);
340
        }
341
+       sess->receiving = true;
342
    }
343
    return;
344
 
345
@@ -556,16 +555,9 @@
346
    return res;
347
 }
348
 
349
-static uint32_t msec_to_bytes(struct sdp_info *info, uint32_t msec)
350
-{
351
-   return msec * info->stride * info->info.rate / 1000;
352
-}
353
-
354
-static void session_touch(struct session *sess)
355
+static uint32_t msec_to_samples(struct sdp_info *info, uint32_t msec)
356
 {
357
-   struct timespec ts;
358
-   clock_gettime(CLOCK_MONOTONIC, &ts);
359
-   sess->timestamp = SPA_TIMESPEC_TO_NSEC(&ts);
360
+   return msec * info->info.rate / 1000;
361
 }
362
 
363
 static void session_free(struct session *sess)
364
@@ -601,6 +593,78 @@
365
    return res;
366
 }
367
 
368
+static int session_start(struct impl *impl, struct session *session) {
369
+   int fd;
370
+   if (session->source)
371
+     return 0;
372
+
373
+   pw_log_info("starting RTP listener");
374
+
375
+   if ((fd = make_socket((const struct sockaddr *)&session->info.sa,
376
+                   session->info.salen, impl->ifname)) < 0) {
377
+       pw_log_error("failed to create socket: %m");
378
+       return fd;
379
+   }
380
+
381
+   session->source = pw_loop_add_io(impl->data_loop, fd,
382
+               SPA_IO_IN, true, on_rtp_io, session);
383
+   if (session->source == NULL) {
384
+       pw_log_error("can't create io source: %m");
385
+       close(fd);
386
+       return -errno;
387
+   }
388
+   return 0;
389
+}
390
+
391
+static void session_stop(struct impl *impl, struct session *session) {
392
+   if (!session->source)
393
+       return;
394
+
395
+   pw_log_info("stopping RTP listener");
396
+
397
+   pw_loop_destroy_source(
398
+       session->impl->data_loop,
399
+       session->source
400
+   );
401
+
402
+   session->source = NULL;
403
+}
404
+
405
+static void on_stream_state_changed(void *d, enum pw_stream_state old,
406
+       enum pw_stream_state state, const char *error)
407
+{
408
+   struct session *sess = d;
409
+   struct impl *impl = sess->impl;
410
+
411
+   switch (state) {
412
+       case PW_STREAM_STATE_UNCONNECTED:
413
+           pw_log_info("stream disconnected, unloading");
414
+           pw_impl_module_schedule_destroy(impl->module);
415
+           break;
416
+       case PW_STREAM_STATE_ERROR:
417
+           pw_log_error("stream error: %s", error);
418
+           break;
419
+       case PW_STREAM_STATE_STREAMING:
420
+           if ((errno = -session_start(impl, sess)) < 0)
421
+               pw_log_error("failed to start RTP stream: %m");
422
+           break;
423
+       case PW_STREAM_STATE_PAUSED:
424
+           if (!impl->always_process)
425
+               session_stop(impl, sess);
426
+         break;
427
+       default:
428
+           break;
429
+   }
430
+}
431
+
432
+static const struct pw_stream_events out_stream_events = {
433
+   PW_VERSION_STREAM_EVENTS,
434
+   .destroy = stream_destroy,
435
+   .state_changed = on_stream_state_changed,
436
+   .io_changed = stream_io_changed,
437
+   .process = stream_process
438
+};
439
+
440
 static int session_new(struct impl *impl, struct sdp_info *info)
441
 {
442
    struct session *session;
443
@@ -609,7 +673,7 @@
444
    uint32_t n_params;
445
    uint8_t buffer1024;
446
    struct pw_properties *props;
447
-   int res, fd, sess_latency_msec;
448
+   int res, sess_latency_msec;
449
    const char *str;
450
 
451
    if (impl->n_sessions >= MAX_SESSIONS) {
452
@@ -643,6 +707,8 @@
453
    } else {
454
        pw_properties_set(props, PW_KEY_MEDIA_NAME, "RTP Stream");
455
    }
456
+   pw_properties_setf(props, "rtp.ts-offset", "%u", info->ts_offset);
457
+   pw_properties_set(props, "rtp.ts-refclk", info->refclk);
458
 
459
    if ((str = pw_properties_get(impl->props, "stream.rules")) != NULL) {
460
        struct session_info sinfo = {
461
@@ -658,22 +724,29 @@
462
            goto error;
463
        }
464
    }
465
+   session->direct_timestamp = pw_properties_get_bool(props, "sess.ts-direct", false);
466
 
467
-   pw_log_info("new session %s %s", info->origin, info->session);
468
+   pw_log_info("new session %s %s direct:%d", info->origin, info->session,
469
+           session->direct_timestamp);
470
 
471
    sess_latency_msec = pw_properties_get_uint32(props,
472
            "sess.latency.msec", impl->sess_latency_msec);
473
 
474
-   session->target_buffer = msec_to_bytes(info, sess_latency_msec);
475
-   session->max_error = msec_to_bytes(info, ERROR_MSEC);
476
+   session->target_buffer = msec_to_samples(info, sess_latency_msec);
477
+   session->max_error = msec_to_samples(info, ERROR_MSEC);
478
 
479
    pw_properties_setf(props, PW_KEY_NODE_RATE, "1/%d", info->info.rate);
480
    pw_properties_setf(props, PW_KEY_NODE_LATENCY, "%d/%d",
481
-           session->target_buffer / (2 * info->stride), info->info.rate);
482
+           session->target_buffer / 2, info->info.rate);
483
 
484
    spa_dll_init(&session->dll);
485
    spa_dll_set_bw(&session->dll, SPA_DLL_BW_MIN, 128, session->info.info.rate);
486
 
487
+   if (info->channelmap0) {
488
+       pw_properties_set(props, PW_KEY_NODE_CHANNELNAMES, info->channelmap);
489
+       pw_log_info("channelmap: %s", info->channelmap);
490
+   }
491
+
492
    session->stream = pw_stream_new(impl->core,
493
            "rtp-source playback", props);
494
    if (session->stream == NULL) {
495
@@ -702,21 +775,10 @@
496
        goto error;
497
    }
498
 
499
-   if ((fd = make_socket((const struct sockaddr *)&info->sa,
500
-                   info->salen, impl->ifname)) < 0) {
501
-       res = fd;
502
+   if (impl->always_process &&
503
+       (res = session_start(impl, session)) < 0)
504
        goto error;
505
-   }
506
 
507
-   session->source = pw_loop_add_io(impl->data_loop, fd,
508
-               SPA_IO_IN, true, on_rtp_io, session);
509
-   if (session->source == NULL) {
510
-       res = -errno;
511
-       pw_log_error("can't create io source: %m");
512
-       goto error;
513
-   }
514
-
515
-   pw_log_info("starting RTP listener");
516
    session_touch(session);
517
 
518
    session->impl = impl;
519
@@ -801,7 +863,32 @@
520
    return 0;
521
 }
522
 
523
-static int parse_sdp_a(struct impl *impl, char *c, struct sdp_info *info)
524
+// some AES67 devices have channelmap encoded in i=*
525
+// if `i` record is found, it matches the template
526
+// and channel count matches, name the channels respectively
527
+// `i=2 channels: 01, 08` is the format
528
+static int parse_sdp_i(struct impl *impl, char *c, struct sdp_info *info)
529
+{
530
+   if (!strstr(c, " channels: ")) {
531
+       return 0;
532
+   }
533
+
534
+   c += strlen("i=");
535
+   cstrcspn(c, " ") = '\0';
536
+
537
+   uint32_t channels;
538
+   if (sscanf(c, "%u", &channels) != 1 || channels <= 0 || channels > SPA_AUDIO_MAX_CHANNELS)
539
+       return 0;
540
+
541
+   c += strcspn(c, "\0");
542
+   c += strlen(" channels: ");
543
+
544
+   strncpy(info->channelmap, c, sizeof(info->channelmap) - 1);
545
+
546
+   return 0;
547
+}
548
+
549
+static int parse_sdp_a_rtpmap(struct impl *impl, char *c, struct sdp_info *info)
550
 {
551
    int payload, len, rate, channels;
552
 
553
@@ -833,6 +920,7 @@
554
    if (sscanf(c, "%u/%u", &rate, &channels) == 2) {
555
        info->info.rate = rate;
556
        info->info.channels = channels;
557
+       pw_log_debug("rate: %d, ch: %d", rate, channels);
558
        if (channels == 2) {
559
            info->info.position0 = SPA_AUDIO_CHANNEL_FL;
560
            info->info.position1 = SPA_AUDIO_CHANNEL_FR;
561
@@ -848,6 +936,35 @@
562
    return 0;
563
 }
564
 
565
+static int parse_sdp_a_mediaclk(struct impl *impl, char *c, struct sdp_info *info)
566
+{
567
+   if (!spa_strstartswith(c, "a=mediaclk:"))
568
+       return 0;
569
+
570
+   c += strlen("a=mediaclk:");
571
+
572
+   if (spa_strstartswith(c, "direct=")) {
573
+       int offset;
574
+       c += strlen("direct=");
575
+       if (sscanf(c, "%i", &offset) != 1)
576
+           return -EINVAL;
577
+       info->ts_offset = offset;
578
+   } else if (spa_strstartswith(c, "sender")) {
579
+       info->ts_offset = 0;
580
+   }
581
+   return 0;
582
+}
583
+
584
+static int parse_sdp_a_ts_refclk(struct impl *impl, char *c, struct sdp_info *info)
585
+{
586
+   if (!spa_strstartswith(c, "a=ts-refclk:"))
587
+       return 0;
588
+
589
+   c += strlen("a=ts-refclk:");
590
+   snprintf(info->refclk, sizeof(info->refclk), "%s", c);
591
+   return 0;
592
+}
593
+
594
 static int parse_sdp(struct impl *impl, char *sdp, struct sdp_info *info)
595
 {
596
    char *s = sdp;
597
@@ -872,8 +989,14 @@
598
            res = parse_sdp_c(impl, s, info);
599
        else if (spa_strstartswith(s, "m="))
600
            res = parse_sdp_m(impl, s, info);
601
-       else if (spa_strstartswith(s, "a="))
602
-           res = parse_sdp_a(impl, s, info);
603
+       else if (spa_strstartswith(s, "a=rtpmap:"))
604
+           res = parse_sdp_a_rtpmap(impl, s, info);
605
+       else if (spa_strstartswith(s, "a=mediaclk:"))
606
+           res = parse_sdp_a_mediaclk(impl, s, info);
607
+       else if (spa_strstartswith(s, "a=ts-refclk:"))
608
+           res = parse_sdp_a_ts_refclk(impl, s, info);
609
+       else if (spa_strstartswith(s, "i="))
610
+           res = parse_sdp_i(impl, s, info);
611
 
612
        if (res < 0)
613
            goto error;
614
@@ -1027,10 +1150,16 @@
615
 
616
    spa_list_for_each_safe(sess, tmp, &impl->sessions, link) {
617
        if (sess->timestamp + interval < timestamp) {
618
-           pw_log_debug("More than %lu elapsed from last advertisement at %lu", interval, sess->timestamp);
619
-           pw_log_info("No advertisement packets found for timeout, closing RTP source");
620
-           session_free(sess);
621
+           pw_log_debug("More than %lu elapsed from last advertisement at %lu",
622
+                   interval, sess->timestamp);
623
+           if (!sess->receiving) {
624
+               pw_log_info("SAP timeout, closing inactive RTP source");
625
+               session_free(sess);
626
+           } else {
627
+               pw_log_info("SAP timeout, keeping active RTP source");
628
+           }
629
        }
630
+       sess->receiving = false;
631
    }
632
 }
633
 
634
@@ -1140,6 +1269,8 @@
635
    str = pw_properties_get(impl->props, "local.ifname");
636
    impl->ifname = str ? strdup(str) : NULL;
637
 
638
+   impl->always_process = pw_properties_get_bool(impl->props, PW_KEY_NODE_ALWAYS_PROCESS, false);
639
+
640
    str = pw_properties_get(impl->props, "sap.ip");
641
    impl->sap_ip = strdup(str ? str : DEFAULT_SAP_IP);
642
    impl->sap_port = pw_properties_get_uint32(impl->props,
643
pipewire-0.3.65.tar.gz/src/pipewire/conf.c -> pipewire-0.3.66.tar.gz/src/pipewire/conf.c Changed
266
 
1
@@ -605,10 +605,70 @@
2
 }
3
 
4
 /*
5
+ * {
6
+ *     # all keys must match the value. ~ in value starts regex.
7
+ *     <key> = <value>
8
+ *     ...
9
+ * }
10
+ */
11
+static bool find_match(struct spa_json *arr, const struct spa_dict *props)
12
+{
13
+   struct spa_json it1;
14
+
15
+   while (spa_json_enter_object(arr, &it0) > 0) {
16
+       char key256, val1024;
17
+       const char *str, *value;
18
+       int match = 0, fail = 0;
19
+       int len;
20
+
21
+       while (spa_json_get_string(&it0, key, sizeof(key)) > 0) {
22
+           bool success = false;
23
+
24
+           if ((len = spa_json_next(&it0, &value)) <= 0)
25
+               break;
26
+
27
+           str = spa_dict_lookup(props, key);
28
+
29
+           if (spa_json_is_null(value, len)) {
30
+               success = str == NULL;
31
+           } else {
32
+               if (spa_json_parse_stringn(value, len, val, sizeof(val)) < 0)
33
+                   continue;
34
+               value = val;
35
+               len = strlen(val);
36
+           }
37
+           if (str != NULL) {
38
+               if (value0 == '~') {
39
+                   regex_t preg;
40
+                   if (regcomp(&preg, value+1, REG_EXTENDED | REG_NOSUB) == 0) {
41
+                       if (regexec(&preg, str, 0, NULL, 0) == 0)
42
+                           success = true;
43
+                       regfree(&preg);
44
+                   }
45
+               } else if (strncmp(str, value, len) == 0 &&
46
+                   strlen(str) == (size_t)len) {
47
+                   success = true;
48
+               }
49
+           }
50
+           if (success) {
51
+               match++;
52
+               pw_log_debug("'%s' match '%s' < > '%.*s'", key, str, len, value);
53
+           }
54
+           else
55
+               fail++;
56
+       }
57
+       if (match > 0 && fail == 0)
58
+           return true;
59
+   }
60
+   return false;
61
+}
62
+
63
+/*
64
  * context.modules = 
65
  *   {   name = <module-name>
66
- *        args = { <key> = <value> ... } 
67
- *        flags =   ifexists   nofail  
68
+ *       ( args = { <key> = <value> ... } )
69
+ *       ( flags =  ( ifexists ) ( nofail ) 
70
+ *       ( condition =  { key = value, .. } ..  )
71
  *   }
72
  * 
73
  */
74
@@ -617,7 +677,7 @@
75
 {
76
    struct data *d = user_data;
77
    struct pw_context *context = d->context;
78
-   struct spa_json it3;
79
+   struct spa_json it4;
80
    char key512, *s;
81
    int res = 0;
82
 
83
@@ -631,6 +691,7 @@
84
 
85
    while (spa_json_enter_object(&it1, &it2) > 0) {
86
        char *name = NULL, *args = NULL, *flags = NULL;
87
+       bool have_match = true;
88
 
89
        while (spa_json_get_string(&it2, key, sizeof(key)) > 0) {
90
            const char *val;
91
@@ -653,8 +714,16 @@
92
                    len = spa_json_container_len(&it2, val, len);
93
                flags = (char*)val;
94
                spa_json_parse_stringn(val, len, flags, len+1);
95
+           } else if (spa_streq(key, "condition")) {
96
+               if (!spa_json_is_array(val, len))
97
+                   break;
98
+               spa_json_enter(&it2, &it3);
99
+               have_match = find_match(&it3, &context->properties->dict);
100
            }
101
        }
102
+       if (!have_match)
103
+           continue;
104
+
105
        if (name != NULL)
106
            res = load_module(context, name, args, flags);
107
 
108
@@ -698,8 +767,9 @@
109
 /*
110
  * context.objects = 
111
  *   {   factory = <factory-name>
112
- *        args  = { <key> = <value> ... } 
113
- *        flags =   nofail   
114
+ *       ( args  = { <key> = <value> ... } )
115
+ *       ( flags =  ( nofail )  )
116
+ *       ( condition =  { key = value, .. } ..  )
117
  *   }
118
  * 
119
  */
120
@@ -708,7 +778,7 @@
121
 {
122
    struct data *d = user_data;
123
    struct pw_context *context = d->context;
124
-   struct spa_json it3;
125
+   struct spa_json it4;
126
    char key512, *s;
127
    int res = 0;
128
 
129
@@ -722,6 +792,7 @@
130
 
131
    while (spa_json_enter_object(&it1, &it2) > 0) {
132
        char *factory = NULL, *args = NULL, *flags = NULL;
133
+       bool have_match = true;
134
 
135
        while (spa_json_get_string(&it2, key, sizeof(key)) > 0) {
136
            const char *val;
137
@@ -745,8 +816,16 @@
138
 
139
                flags = (char*)val;
140
                spa_json_parse_stringn(val, len, flags, len+1);
141
+           } else if (spa_streq(key, "condition")) {
142
+               if (!spa_json_is_array(val, len))
143
+                   break;
144
+               spa_json_enter(&it2, &it3);
145
+               have_match = find_match(&it3, &context->properties->dict);
146
            }
147
        }
148
+       if (!have_match)
149
+           continue;
150
+
151
        if (factory != NULL)
152
            res = create_object(context, factory, args, flags);
153
 
154
@@ -807,8 +886,9 @@
155
 
156
 /*
157
  * context.exec = 
158
- *   { path = <program-name>
159
- *      args = "<arguments>" 
160
+ *   {   path = <program-name>
161
+ *       ( args = "<arguments>" )
162
+ *       ( condition =  { key = value, .. } ..  )
163
  *   }
164
  * 
165
  */
166
@@ -817,7 +897,7 @@
167
 {
168
    struct data *d = user_data;
169
    struct pw_context *context = d->context;
170
-   struct spa_json it3;
171
+   struct spa_json it4;
172
    char key512, *s;
173
    int res = 0;
174
 
175
@@ -831,6 +911,7 @@
176
 
177
    while (spa_json_enter_object(&it1, &it2) > 0) {
178
        char *path = NULL, *args = NULL;
179
+       bool have_match = true;
180
 
181
        while (spa_json_get_string(&it2, key, sizeof(key)) > 0) {
182
            const char *val;
183
@@ -845,8 +926,16 @@
184
            } else if (spa_streq(key, "args")) {
185
                args = (char*)val;
186
                spa_json_parse_stringn(val, len, args, len+1);
187
+           } else if (spa_streq(key, "condition")) {
188
+               if (!spa_json_is_array(val, len))
189
+                   break;
190
+               spa_json_enter(&it2, &it3);
191
+               have_match = find_match(&it3, &context->properties->dict);
192
            }
193
        }
194
+       if (!have_match)
195
+           continue;
196
+
197
        if (path != NULL)
198
            res = do_exec(context, path, args);
199
 
200
@@ -1014,65 +1103,6 @@
201
 }
202
 
203
 
204
-/*
205
- * {
206
- *     # all keys must match the value. ~ in value starts regex.
207
- *     <key> = <value>
208
- *     ...
209
- * }
210
- */
211
-static bool find_match(struct spa_json *arr, const struct spa_dict *props)
212
-{
213
-   struct spa_json it1;
214
-
215
-   while (spa_json_enter_object(arr, &it0) > 0) {
216
-       char key256, val1024;
217
-       const char *str, *value;
218
-       int match = 0, fail = 0;
219
-       int len;
220
-
221
-       while (spa_json_get_string(&it0, key, sizeof(key)) > 0) {
222
-           bool success = false;
223
-
224
-           if ((len = spa_json_next(&it0, &value)) <= 0)
225
-               break;
226
-
227
-           str = spa_dict_lookup(props, key);
228
-
229
-           if (spa_json_is_null(value, len)) {
230
-               success = str == NULL;
231
-           } else {
232
-               if (spa_json_parse_stringn(value, len, val, sizeof(val)) < 0)
233
-                   continue;
234
-               value = val;
235
-               len = strlen(val);
236
-           }
237
-           if (str != NULL) {
238
-               if (value0 == '~') {
239
-                   regex_t preg;
240
-                   if (regcomp(&preg, value+1, REG_EXTENDED | REG_NOSUB) == 0) {
241
-                       if (regexec(&preg, str, 0, NULL, 0) == 0)
242
-                           success = true;
243
-                       regfree(&preg);
244
-                   }
245
-               } else if (strncmp(str, value, len) == 0 &&
246
-                   strlen(str) == (size_t)len) {
247
-                   success = true;
248
-               }
249
-           }
250
-           if (success) {
251
-               match++;
252
-               pw_log_debug("'%s' match '%s' < > '%.*s'", key, str, len, value);
253
-           }
254
-           else
255
-               fail++;
256
-       }
257
-       if (match > 0 && fail == 0)
258
-           return true;
259
-   }
260
-   return false;
261
-}
262
-
263
 /**
264
  * 
265
  *     {
266
pipewire-0.3.65.tar.gz/src/pipewire/context.c -> pipewire-0.3.66.tar.gz/src/pipewire/context.c Changed
136
 
1
@@ -970,15 +970,114 @@
2
    return fa < fb ? -1 : (fa > fb ? 1 : 0);
3
 }
4
 
5
-static uint32_t find_best_rate(const uint32_t *rates, uint32_t n_rates, uint32_t rate, uint32_t best)
6
+static inline uint32_t calc_gcd(uint32_t a, uint32_t b)
7
 {
8
-   uint32_t i;
9
+   while (b != 0) {
10
+       uint32_t temp = a;
11
+       a = b;
12
+       b = temp % b;
13
+   }
14
+   return a;
15
+}
16
+
17
+struct rate_info {
18
+   uint32_t rate;
19
+   uint32_t gcd;
20
+   uint32_t diff;
21
+};
22
+
23
+static inline void update_highest_rate(struct rate_info *best, struct rate_info *current)
24
+{
25
+   /* find highest rate */
26
+   if (best->rate == 0 || best->rate < current->rate)
27
+       *best = *current;
28
+}
29
+
30
+static inline void update_nearest_gcd(struct rate_info *best, struct rate_info *current)
31
+{
32
+   /* find nearest GCD */
33
+   if (best->rate == 0 ||
34
+       (best->gcd < current->gcd) ||
35
+       (best->gcd == current->gcd && best->diff > current->diff))
36
+       *best = *current;
37
+}
38
+static inline void update_nearest_rate(struct rate_info *best, struct rate_info *current)
39
+{
40
+   /* find nearest rate */
41
+   if (best->rate == 0 || best->diff > current->diff)
42
+       *best = *current;
43
+}
44
+
45
+static uint32_t find_best_rate(const uint32_t *rates, uint32_t n_rates, uint32_t rate, uint32_t def)
46
+{
47
+   uint32_t i, limit;
48
+   struct rate_info best;
49
+   struct rate_info infon_rates;
50
+
51
+   for (i = 0; i < n_rates; i++) {
52
+       infoi.rate = ratesi;
53
+       infoi.gcd = calc_gcd(rate, ratesi);
54
+       infoi.diff = SPA_ABS((int32_t)rate - (int32_t)ratesi);
55
+   }
56
+
57
+   /* first find higher nearest GCD. This tries to find next bigest rate that
58
+    * requires the least amount of resample filter banks. Usually these are
59
+    * rates that are multiples of eachother or multiples of a common rate.
60
+    *
61
+    * 44100 and  32000 56000 88200 96000   -> 88200
62
+    * 48000 and  32000 56000 88200 96000   -> 96000
63
+    * 88200 and  44100 48000 96000 192000   -> 96000
64
+    * 32000 and  44100 192000  -> 44100
65
+    * 8000 and  44100 48000  -> 48000
66
+    * 8000 and  44100 192000  -> 44100
67
+    * 11025 and  44100 48000  -> 44100
68
+    * 44100 and  48000 176400  -> 48000
69
+    */
70
+   spa_zero(best);
71
+   /* Don't try to do excessive upsampling by limiting the max rate
72
+    * for desired < default to default*2. For other rates allow
73
+    * a x3 upsample rate max */
74
+   limit = rate < def ? def*2 : rate*3;
75
+   for (i = 0; i < n_rates; i++) {
76
+       if (infoi.rate >= rate && infoi.rate <= limit)
77
+           update_nearest_gcd(&best, &infoi);
78
+   }
79
+   if (best.rate != 0)
80
+       return best.rate;
81
+
82
+   /* we would need excessive upsampling, pick a nearest higher rate */
83
+   spa_zero(best);
84
+   for (i = 0; i < n_rates; i++) {
85
+       if (infoi.rate >= rate)
86
+           update_nearest_rate(&best, &infoi);
87
+   }
88
+   if (best.rate != 0)
89
+       return best.rate;
90
+
91
+   /* There is nothing above the rate, we need to downsample. Try to downsample
92
+    * but only to something that is from a common rate family. Also don't
93
+    * try to downsample to something that will sound worse (< 44100).
94
+    *
95
+    * 88200 and  22050 44100 48000  -> 44100
96
+    * 88200 and  22050 48000  -> 48000
97
+    */
98
+   spa_zero(best);
99
    for (i = 0; i < n_rates; i++) {
100
-       if (SPA_ABS((int32_t)rate - (int32_t)ratesi) <
101
-           SPA_ABS((int32_t)rate - (int32_t)best))
102
-           best = ratesi;
103
+       if (infoi.rate >= 44100)
104
+           update_nearest_gcd(&best, &infoi);
105
    }
106
-   return best;
107
+   if (best.rate != 0)
108
+       return best.rate;
109
+
110
+   /* There is nothing to downsample above our threshold. Downsample to whatever
111
+    * is the highest rate then. */
112
+   spa_zero(best);
113
+   for (i = 0; i < n_rates; i++)
114
+       update_highest_rate(&best, &infoi);
115
+   if (best.rate != 0)
116
+       return best.rate;
117
+
118
+   return def;
119
 }
120
 
121
 /* here we evaluate the complete state of the graph.
122
@@ -1201,10 +1300,12 @@
123
            running = true;
124
 
125
        current_rate = n->current_rate.denom;
126
-       if (lock_rate || n->reconfigure ||
127
+       if (lock_rate || n->reconfigure || !running ||
128
            (!force_rate &&
129
            (n->info.state > PW_NODE_STATE_IDLE)))
130
            /* when someone wants us to lock the rate of this driver or
131
+            * when we are in the process of reconfiguring the driver or
132
+            * when we are not running any followers or
133
             * when the driver is busy and we don't need to force a rate,
134
             * keep the current rate */
135
            target_rate = current_rate;
136
pipewire-0.3.65.tar.gz/src/pipewire/filter.c -> pipewire-0.3.66.tar.gz/src/pipewire/filter.c Changed
264
 
1
@@ -95,15 +95,15 @@
2
 
3
    struct pw_properties *props;
4
 
5
-   uint32_t change_mask_all;
6
+   uint64_t change_mask_all;
7
    struct spa_port_info info;
8
    struct spa_list param_list;
9
-#define IDX_EnumFormat 0
10
-#define IDX_Meta   1
11
-#define IDX_IO     2
12
-#define IDX_Format 3
13
-#define IDX_Buffers    4
14
-#define IDX_Latency    5
15
+#define PORT_EnumFormat    0
16
+#define PORT_Meta  1
17
+#define PORT_IO        2
18
+#define PORT_Format    3
19
+#define PORT_Buffers   4
20
+#define PORT_Latency   5
21
 #define N_PORT_PARAMS  6
22
    struct spa_param_info paramsN_PORT_PARAMS;
23
 
24
@@ -133,6 +133,7 @@
25
    struct spa_node impl_node;
26
    struct spa_hook_list hooks;
27
    struct spa_callbacks callbacks;
28
+   struct spa_io_clock *clock;
29
    struct spa_io_position *position;
30
 
31
    struct {
32
@@ -142,12 +143,12 @@
33
    struct spa_list port_list;
34
    struct pw_map ports2;
35
 
36
-   uint32_t change_mask_all;
37
+   uint64_t change_mask_all;
38
    struct spa_node_info info;
39
    struct spa_list param_list;
40
-#define IDX_PropInfo       0
41
-#define IDX_Props      1
42
-#define IDX_ProcessLatency 2
43
+#define NODE_PropInfo      0
44
+#define NODE_Props     1
45
+#define NODE_ProcessLatency    2
46
 #define N_NODE_PARAMS      3
47
    struct spa_param_info paramsN_NODE_PARAMS;
48
 
49
@@ -168,17 +169,18 @@
50
    unsigned int allow_mlock:1;
51
    unsigned int warn_mlock:1;
52
    unsigned int process_rt:1;
53
+   unsigned int driving:1;
54
 };
55
 
56
 static int get_param_index(uint32_t id)
57
 {
58
    switch (id) {
59
    case SPA_PARAM_PropInfo:
60
-       return IDX_PropInfo;
61
+       return NODE_PropInfo;
62
    case SPA_PARAM_Props:
63
-       return IDX_Props;
64
+       return NODE_Props;
65
    case SPA_PARAM_ProcessLatency:
66
-       return IDX_ProcessLatency;
67
+       return NODE_ProcessLatency;
68
    default:
69
        return -1;
70
    }
71
@@ -188,17 +190,17 @@
72
 {
73
    switch (id) {
74
    case SPA_PARAM_EnumFormat:
75
-       return IDX_EnumFormat;
76
+       return PORT_EnumFormat;
77
    case SPA_PARAM_Meta:
78
-       return IDX_Meta;
79
+       return PORT_Meta;
80
    case SPA_PARAM_IO:
81
-       return IDX_IO;
82
+       return PORT_IO;
83
    case SPA_PARAM_Format:
84
-       return IDX_Format;
85
+       return PORT_Format;
86
    case SPA_PARAM_Buffers:
87
-       return IDX_Buffers;
88
+       return PORT_Buffers;
89
    case SPA_PARAM_Latency:
90
-       return IDX_Latency;
91
+       return PORT_Latency;
92
    default:
93
        return -1;
94
    }
95
@@ -475,6 +477,12 @@
96
    pw_log_debug("%p: io %d %p/%zd", impl, id, data, size);
97
 
98
    switch(id) {
99
+   case SPA_IO_Clock:
100
+       if (data && size >= sizeof(struct spa_io_clock))
101
+           impl->clock = data;
102
+       else
103
+           impl->clock = NULL;
104
+       break;
105
    case SPA_IO_Position:
106
        if (data && size >= sizeof(struct spa_io_position))
107
            impl->position = data;
108
@@ -484,6 +492,7 @@
109
            do_set_position, 1, NULL, 0, true, impl);
110
        break;
111
    }
112
+   impl->driving = impl->clock && impl->position && impl->position->clock.id == impl->clock->id;
113
    pw_filter_emit_io_changed(&impl->this, NULL, id, data, size);
114
 
115
    return 0;
116
@@ -1530,9 +1539,9 @@
117
    impl->info.max_output_ports = UINT32_MAX;
118
    impl->info.flags = impl->process_rt ? SPA_NODE_FLAG_RT : 0;
119
    impl->info.props = &filter->properties->dict;
120
-   impl->paramsIDX_PropInfo = SPA_PARAM_INFO(SPA_PARAM_PropInfo, 0);
121
-   impl->paramsIDX_Props = SPA_PARAM_INFO(SPA_PARAM_Props, SPA_PARAM_INFO_WRITE);
122
-   impl->paramsIDX_ProcessLatency = SPA_PARAM_INFO(SPA_PARAM_ProcessLatency, 0);
123
+   impl->paramsNODE_PropInfo = SPA_PARAM_INFO(SPA_PARAM_PropInfo, 0);
124
+   impl->paramsNODE_Props = SPA_PARAM_INFO(SPA_PARAM_Props, SPA_PARAM_INFO_WRITE);
125
+   impl->paramsNODE_ProcessLatency = SPA_PARAM_INFO(SPA_PARAM_ProcessLatency, 0);
126
    impl->info.params = impl->params;
127
    impl->info.n_params = N_NODE_PARAMS;
128
    impl->info.change_mask = impl->change_mask_all;
129
@@ -1712,12 +1721,12 @@
130
        p->info.flags |= SPA_PORT_FLAG_CAN_ALLOC_BUFFERS;
131
    p->info.props = &p->props->dict;
132
    p->change_mask_all |= SPA_PORT_CHANGE_MASK_PARAMS;
133
-   p->paramsIDX_EnumFormat = SPA_PARAM_INFO(SPA_PARAM_EnumFormat, 0);
134
-   p->paramsIDX_Meta = SPA_PARAM_INFO(SPA_PARAM_Meta, 0);
135
-   p->paramsIDX_IO = SPA_PARAM_INFO(SPA_PARAM_IO, 0);
136
-   p->paramsIDX_Format = SPA_PARAM_INFO(SPA_PARAM_Format, SPA_PARAM_INFO_WRITE);
137
-   p->paramsIDX_Buffers = SPA_PARAM_INFO(SPA_PARAM_Buffers, 0);
138
-   p->paramsIDX_Latency = SPA_PARAM_INFO(SPA_PARAM_Latency, SPA_PARAM_INFO_WRITE);
139
+   p->paramsPORT_EnumFormat = SPA_PARAM_INFO(SPA_PARAM_EnumFormat, 0);
140
+   p->paramsPORT_Meta = SPA_PARAM_INFO(SPA_PARAM_Meta, 0);
141
+   p->paramsPORT_IO = SPA_PARAM_INFO(SPA_PARAM_IO, 0);
142
+   p->paramsPORT_Format = SPA_PARAM_INFO(SPA_PARAM_Format, SPA_PARAM_INFO_WRITE);
143
+   p->paramsPORT_Buffers = SPA_PARAM_INFO(SPA_PARAM_Buffers, 0);
144
+   p->paramsPORT_Latency = SPA_PARAM_INFO(SPA_PARAM_Latency, SPA_PARAM_INFO_WRITE);
145
    p->info.params = p->params;
146
    p->info.n_params = N_PORT_PARAMS;
147
 
148
@@ -1749,22 +1758,23 @@
149
    return NULL;
150
 }
151
 
152
-SPA_EXPORT
153
-int pw_filter_remove_port(void *port_data)
154
+static inline void free_port(struct filter *impl, struct port *port)
155
 {
156
-   struct port *port = SPA_CONTAINER_OF(port_data, struct port, user_data);
157
-   struct filter *impl = port->filter;
158
-
159
-   spa_node_emit_port_info(&impl->hooks, port->direction, port->id, NULL);
160
-
161
    spa_list_remove(&port->link);
162
+   spa_node_emit_port_info(&impl->hooks, port->direction, port->id, NULL);
163
    pw_map_remove(&impl->portsport->direction, port->id);
164
-
165
    clear_buffers(port);
166
    clear_params(impl, port, SPA_ID_INVALID);
167
    pw_properties_free(port->props);
168
    free(port);
169
+}
170
 
171
+SPA_EXPORT
172
+int pw_filter_remove_port(void *port_data)
173
+{
174
+   struct port *port = SPA_CONTAINER_OF(port_data, struct port, user_data);
175
+   struct filter *impl = port->filter;
176
+   free_port(impl, port);
177
    return 0;
178
 }
179
 
180
@@ -1844,25 +1854,6 @@
181
    return 0;
182
 }
183
 
184
-static int
185
-do_process(struct spa_loop *loop,
186
-                 bool async, uint32_t seq, const void *data, size_t size, void *user_data)
187
-{
188
-   struct filter *impl = user_data;
189
-   int res = impl_node_process(impl);
190
-   return spa_node_call_ready(&impl->callbacks, res);
191
-}
192
-
193
-static inline int call_trigger(struct filter *impl)
194
-{
195
-   int res = 0;
196
-   if (SPA_FLAG_IS_SET(impl->flags, PW_FILTER_FLAG_DRIVER)) {
197
-       res = pw_loop_invoke(impl->context->data_loop,
198
-           do_process, 1, NULL, 0, false, impl);
199
-   }
200
-   return res;
201
-}
202
-
203
 SPA_EXPORT
204
 struct pw_buffer *pw_filter_dequeue_buffer(void *port_data)
205
 {
206
@@ -1894,7 +1885,7 @@
207
    if ((res = push_queue(p, &p->queued, b)) < 0)
208
        return res;
209
 
210
-   return call_trigger(impl);
211
+   return res;
212
 }
213
 
214
 SPA_EXPORT
215
@@ -1960,3 +1951,48 @@
216
            drain ? do_drain : do_flush, 1, NULL, 0, true, impl);
217
    return 0;
218
 }
219
+
220
+SPA_EXPORT
221
+bool pw_filter_is_driving(struct pw_filter *filter)
222
+{
223
+   struct filter *impl = SPA_CONTAINER_OF(filter, struct filter, this);
224
+   return impl->driving;
225
+}
226
+
227
+static int
228
+do_trigger_process(struct spa_loop *loop,
229
+                 bool async, uint32_t seq, const void *data, size_t size, void *user_data)
230
+{
231
+   struct filter *impl = user_data;
232
+   int res = impl_node_process(impl);
233
+   return spa_node_call_ready(&impl->callbacks, res);
234
+}
235
+
236
+static int trigger_request_process(struct filter *impl)
237
+{
238
+   uint8_t buffer1024;
239
+   struct spa_pod_builder b = { 0 };
240
+
241
+   spa_pod_builder_init(&b, buffer, sizeof(buffer));
242
+   spa_node_emit_event(&impl->hooks,
243
+           spa_pod_builder_add_object(&b,
244
+               SPA_TYPE_EVENT_Node, SPA_NODE_EVENT_RequestProcess));
245
+   return 0;
246
+}
247
+
248
+SPA_EXPORT
249
+int pw_filter_trigger_process(struct pw_filter *filter)
250
+{
251
+   struct filter *impl = SPA_CONTAINER_OF(filter, struct filter, this);
252
+   int res = 0;
253
+
254
+   pw_log_trace_fp("%p", impl);
255
+
256
+   if (!impl->driving) {
257
+       res = trigger_request_process(impl);
258
+   } else {
259
+       res = pw_loop_invoke(impl->context->data_loop,
260
+           do_trigger_process, 1, NULL, 0, false, impl);
261
+   }
262
+   return res;
263
+}
264
pipewire-0.3.65.tar.gz/src/pipewire/filter.h -> pipewire-0.3.66.tar.gz/src/pipewire/filter.h Changed
18
 
1
@@ -239,6 +239,16 @@
2
  * be called when all data is played or recorded */
3
 int pw_filter_flush(struct pw_filter *filter, bool drain);
4
 
5
+/** Check if the filter is driving. The filter needs to have the
6
+ * PW_FILTER_FLAG_DRIVER set. When the filter is driving,
7
+ * pw_filter_trigger_process() needs to be called when data is
8
+ * available (output) or needed (input). Since 0.3.66 */
9
+bool pw_filter_is_driving(struct pw_filter *filter);
10
+
11
+/** Trigger a push/pull on the filter. One iteration of the graph will
12
+ * be scheduled and process() will be called. Since 0.3.66 */
13
+int pw_filter_trigger_process(struct pw_filter *filter);
14
+
15
 /**
16
  * \}
17
  */
18
pipewire-0.3.65.tar.gz/src/pipewire/impl-core.c -> pipewire-0.3.66.tar.gz/src/pipewire/impl-core.c Changed
10
 
1
@@ -351,7 +351,7 @@
2
    if (obj == NULL)
3
        goto error_create_failed;
4
 
5
-   return 0;
6
+   return obj;
7
 
8
 error_no_factory:
9
    res = -ENOENT;
10
pipewire-0.3.65.tar.gz/src/pipewire/keys.h -> pipewire-0.3.66.tar.gz/src/pipewire/keys.h Changed
17
 
1
@@ -342,8 +342,13 @@
2
                                  * and object name or object.serial */
3
 
4
 #ifndef PW_REMOVE_DEPRECATED
5
-#define PW_KEY_PRIORITY_MASTER     PW_DEPRECATED("priority.master")    /**< deprecated, use priority.driver */
6
-#define PW_KEY_NODE_TARGET     PW_DEPRECATED("node.target")        /**< deprecated since 0.3.64, use target.object. */
7
+# ifdef PW_ENABLE_DEPRECATED
8
+#  define PW_KEY_PRIORITY_MASTER   "priority.master"   /**< deprecated, use priority.driver */
9
+#  define PW_KEY_NODE_TARGET       "node.target"       /**< deprecated since 0.3.64, use target.object. */
10
+# else
11
+#  define PW_KEY_PRIORITY_MASTER   PW_DEPRECATED("priority.master")
12
+#  define PW_KEY_NODE_TARGET       PW_DEPRECATED("node.target")
13
+# endif /* PW_ENABLE_DEPRECATED */
14
 #endif /* PW_REMOVE_DEPRECATED */
15
 
16
 /** \}
17
pipewire-0.3.65.tar.gz/src/pipewire/log.c -> pipewire-0.3.66.tar.gz/src/pipewire/log.c Changed
29
 
1
@@ -29,6 +29,7 @@
2
 
3
 #include <spa/pod/pod.h>
4
 #include <spa/debug/types.h>
5
+#include <spa/debug/format.h>
6
 #include <spa/pod/iter.h>
7
 #include <spa/utils/list.h>
8
 
9
@@ -241,13 +242,14 @@
10
 {
11
    struct spa_debug_log_ctx ctx = SPA_LOGF_DEBUG_INIT(global_log, level,
12
            topic, file, line, func );
13
-   if (flags & PW_LOG_OBJECT_POD) {
14
+   if (object == NULL) {
15
+       pw_log_logt(level, topic, file, line, func, "NULL");
16
+   } else {
17
        const struct spa_pod *pod = object;
18
-       if (pod == NULL) {
19
-           pw_log_logt(level, topic, file, line, func, "NULL");
20
-       } else {
21
+       if (flags & PW_LOG_OBJECT_POD)
22
            spa_debugc_pod(&ctx.ctx, 0, SPA_TYPE_ROOT, pod);
23
-       }
24
+       else if (flags & PW_LOG_OBJECT_FORMAT)
25
+           spa_debugc_format(&ctx.ctx, 0, NULL, pod);
26
    }
27
 }
28
 
29
pipewire-0.3.65.tar.gz/src/pipewire/mem.c -> pipewire-0.3.66.tar.gz/src/pipewire/mem.c Changed
33
 
1
@@ -33,6 +33,7 @@
2
 #include <unistd.h>
3
 #include <stdlib.h>
4
 #include <sys/syscall.h>
5
+#include <sys/stat.h>
6
 
7
 #include <spa/utils/list.h>
8
 #include <spa/buffer/buffer.h>
9
@@ -363,6 +364,23 @@
10
    struct mapping *m;
11
    struct memmap *mm;
12
    struct pw_map_range range;
13
+   struct stat sb;
14
+
15
+   if (fstat(b->this.fd, &sb) != 0)
16
+       return NULL;
17
+
18
+   const bool valid = (int64_t) offset + size <= (int64_t) sb.st_size;
19
+   pw_log(valid ? SPA_LOG_LEVEL_DEBUG : SPA_LOG_LEVEL_ERROR,
20
+       "%p: block %p%u mapping %" PRIu32 "+%" PRIu32 " of file=%d/%" PRIu64 ":%" PRIu64 " with size=%" PRId64,
21
+       block->pool, block, block->id,
22
+       offset, size,
23
+       block->fd, (uint64_t) sb.st_dev, (uint64_t) sb.st_ino,
24
+       (int64_t) sb.st_size);
25
+
26
+   if (!valid) {
27
+       errno = -EINVAL;
28
+       return NULL;
29
+   }
30
 
31
    pw_map_range_init(&range, offset, size, p->pagesize);
32
 
33
pipewire-0.3.65.tar.gz/src/pipewire/private.h -> pipewire-0.3.66.tar.gz/src/pipewire/private.h Changed
18
 
1
@@ -1276,6 +1276,7 @@
2
 void pw_impl_client_unref(struct pw_impl_client *client);
3
 
4
 #define PW_LOG_OBJECT_POD  (1<<0)
5
+#define PW_LOG_OBJECT_FORMAT   (1<<1)
6
 void pw_log_log_object(enum spa_log_level level, const struct spa_log_topic *topic,
7
        const char *file, int line, const char *func, uint32_t flags,
8
        const void *object);
9
@@ -1288,7 +1289,7 @@
10
 })
11
 
12
 #define pw_log_pod(lev,pod) pw_log_object(lev,PW_LOG_TOPIC_DEFAULT,PW_LOG_OBJECT_POD,pod)
13
-#define pw_log_format(lev,pod) pw_log_object(lev,PW_LOG_TOPIC_DEFAULT,PW_LOG_OBJECT_POD,pod)
14
+#define pw_log_format(lev,pod) pw_log_object(lev,PW_LOG_TOPIC_DEFAULT,PW_LOG_OBJECT_FORMAT,pod)
15
 
16
 bool pw_log_is_default(void);
17
 
18
pipewire-0.3.65.tar.gz/src/pipewire/stream.c -> pipewire-0.3.66.tar.gz/src/pipewire/stream.c Changed
19
 
1
@@ -123,7 +123,7 @@
2
        struct spa_io_position *position;
3
    } rt;
4
 
5
-   uint32_t port_change_mask_all;
6
+   uint64_t port_change_mask_all;
7
    struct spa_port_info port_info;
8
    struct pw_properties *port_props;
9
 #define PORT_EnumFormat    0
10
@@ -137,7 +137,7 @@
11
 
12
    struct spa_list param_list;
13
 
14
-   uint32_t change_mask_all;
15
+   uint64_t change_mask_all;
16
    struct spa_node_info info;
17
 #define NODE_PropInfo  0
18
 #define NODE_Props 1
19
pipewire-0.3.65.tar.gz/src/pipewire/thread-loop.c -> pipewire-0.3.66.tar.gz/src/pipewire/thread-loop.c Changed
10
 
1
@@ -435,7 +435,7 @@
2
  *
3
  */
4
 SPA_EXPORT
5
-int pw_thread_loop_timed_wait_full(struct pw_thread_loop *loop, struct timespec *abstime)
6
+int pw_thread_loop_timed_wait_full(struct pw_thread_loop *loop, const struct timespec *abstime)
7
 {
8
    int ret;
9
    loop->n_waiting++;
10
pipewire-0.3.65.tar.gz/src/pipewire/thread-loop.h -> pipewire-0.3.66.tar.gz/src/pipewire/thread-loop.h Changed
10
 
1
@@ -153,7 +153,7 @@
2
 /** Release the lock and wait up to \a abstime until some thread calls
3
  * \ref pw_thread_loop_signal. Use \ref pw_thread_loop_get_time to make a timeout.
4
  * Since: 0.3.7 */
5
-int pw_thread_loop_timed_wait_full(struct pw_thread_loop *loop, struct timespec *abstime);
6
+int pw_thread_loop_timed_wait_full(struct pw_thread_loop *loop, const struct timespec *abstime);
7
 
8
 /** Signal all threads waiting with \ref pw_thread_loop_wait */
9
 void pw_thread_loop_signal(struct pw_thread_loop *loop, bool wait_for_accept);
10
pipewire-0.3.65.tar.gz/src/tools/pw-cat.c -> pipewire-0.3.66.tar.gz/src/tools/pw-cat.c Changed
58
 
1
@@ -255,14 +255,13 @@
2
 static int encoded_playback_fill(struct data *d, void *dest, unsigned int n_frames)
3
 {
4
    int ret, size = 0;
5
-   uint8_t buffer16384 = { 0 };
6
+   uint8_t buffer16384;
7
 
8
-   ret = fread(buffer, 1, 16384, d->encoded_file);
9
+   ret = fread(buffer, 1, SPA_MIN(n_frames, sizeof(buffer)), d->encoded_file);
10
    if (ret > 0) {
11
        memcpy(dest, buffer, ret);
12
        size = ret;
13
    }
14
-
15
    return (int)size;
16
 }
17
 
18
@@ -786,31 +785,6 @@
19
        n_frames = d->maxsize / data->stride;
20
        n_frames = SPA_MIN(n_frames, (int)b->requested);
21
 
22
-#ifdef HAVE_PW_CAT_FFMPEG_INTEGRATION
23
-       n_fill_frames = data->fill(data, p, n_frames);
24
-
25
-       if (n_fill_frames > 0 || n_frames == 0) {
26
-           d->chunk->offset = 0;
27
-           if (data->data_type == TYPE_ENCODED) {
28
-               d->chunk->stride = 0;
29
-               // encoded_playback_fill returns number of bytes
30
-               // read and not number of frames like other
31
-               // functions for raw audio.
32
-               d->chunk->size = n_fill_frames;
33
-               b->size = n_fill_frames;
34
-           } else {
35
-               d->chunk->stride = data->stride;
36
-               d->chunk->size = n_fill_frames * data->stride;
37
-               b->size = n_frames;
38
-           }
39
-           have_data = true;
40
-       } else if (n_fill_frames < 0) {
41
-           fprintf(stderr, "fill error %d\n", n_fill_frames);
42
-       } else {
43
-           if (data->verbose)
44
-               printf("drain start\n");
45
-       }
46
-#else
47
        n_fill_frames = data->fill(data, p, n_frames);
48
 
49
        if (n_fill_frames > 0 || n_frames == 0) {
50
@@ -825,7 +799,6 @@
51
            if (data->verbose)
52
                printf("drain start\n");
53
        }
54
-#endif
55
    } else {
56
        offset = SPA_MIN(d->chunk->offset, d->maxsize);
57
        size = SPA_MIN(d->chunk->size, d->maxsize - offset);
58
pipewire-0.3.66.tar.gz/subprojects/libcamera.wrap Added
5
 
1
@@ -0,0 +1,3 @@
2
+wrap-git
3
+url = https://git.libcamera.org/libcamera/libcamera.git
4
+revision = head
5
Refresh

No build results available

Refresh

No rpmlint results available

Request History
Bjørn Lie's avatar

zaitor created request about 2 years ago

New upstream release


Bjørn Lie's avatar

zaitor accepted request about 2 years ago

Xin